@hubspot/cms-component-library 0.3.7 → 0.3.9
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/AccordionContent/ContentFields.tsx +6 -6
- package/components/componentLibrary/Accordion/AccordionContent/index.tsx +3 -1
- package/components/componentLibrary/Accordion/AccordionContent/types.ts +2 -3
- package/components/componentLibrary/Accordion/AccordionTitle/AccordionTitleBase.tsx +45 -0
- package/components/componentLibrary/Accordion/AccordionTitle/ContentFields.tsx +8 -3
- package/components/componentLibrary/Accordion/AccordionTitle/index.tsx +18 -25
- package/components/componentLibrary/Accordion/AccordionTitle/islands/AccordionTitleIsland.tsx +29 -0
- package/components/componentLibrary/Accordion/AccordionTitle/types.ts +1 -0
- package/components/componentLibrary/Accordion/llm.txt +20 -13
- 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 +11 -3
- package/components/componentLibrary/Link/llm.txt +13 -0
- package/components/componentLibrary/Link/types.ts +6 -4
- package/components/componentLibrary/Logo/ContentFields.tsx +25 -0
- package/components/componentLibrary/Logo/index.tsx +13 -4
- package/components/componentLibrary/Logo/types.tsx +15 -0
- 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 +65 -0
- package/components/componentLibrary/_patterns/island-patterns.md +136 -0
- package/components/componentLibrary/utils/index.ts +1 -0
- package/package.json +4 -3
|
@@ -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
|
}
|
|
@@ -1,30 +1,25 @@
|
|
|
1
1
|
import styles from './index.module.scss';
|
|
2
2
|
import cx from '../utils/classname.js';
|
|
3
|
-
import type { CSSVariables } from '../utils/types.js';
|
|
4
3
|
import { CardProps } from './types.js';
|
|
5
4
|
import StyleFields from './StyleFields.js';
|
|
6
5
|
|
|
7
6
|
const CardComponent = ({
|
|
8
|
-
variant = '
|
|
7
|
+
variant = 'cardStyle1',
|
|
9
8
|
as: Component = 'div',
|
|
10
|
-
borderRadius = 8,
|
|
11
9
|
className = '',
|
|
12
10
|
style = {},
|
|
13
11
|
children,
|
|
14
12
|
...rest
|
|
15
13
|
}: CardProps) => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const defaultClasses = cx(styles.card, variantClass);
|
|
19
|
-
const combinedClasses = cx(defaultClasses, className);
|
|
20
|
-
|
|
21
|
-
const cssVariables: CSSVariables = {
|
|
22
|
-
'--hscl-card-borderRadius': `${borderRadius}px`,
|
|
23
|
-
...style,
|
|
24
|
-
};
|
|
14
|
+
const combinedClasses = cx(styles.card, className);
|
|
25
15
|
|
|
26
16
|
return (
|
|
27
|
-
<Component
|
|
17
|
+
<Component
|
|
18
|
+
className={combinedClasses}
|
|
19
|
+
style={style}
|
|
20
|
+
data-card-variant={variant}
|
|
21
|
+
{...rest}
|
|
22
|
+
>
|
|
28
23
|
{children}
|
|
29
24
|
</Component>
|
|
30
25
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Card Component
|
|
2
2
|
|
|
3
|
-
A flexible, polymorphic container component that provides a consistent visual structure for grouping related content with
|
|
3
|
+
A flexible, polymorphic container component that provides a consistent visual structure for grouping related content with theme variant styling.
|
|
4
4
|
|
|
5
5
|
## Import path
|
|
6
6
|
```tsx
|
|
@@ -9,7 +9,7 @@ import Card from '@hubspot/cms-component-library/Card';
|
|
|
9
9
|
|
|
10
10
|
## Purpose
|
|
11
11
|
|
|
12
|
-
The Card component provides a reusable container for grouping related content in HubSpot CMS projects. It
|
|
12
|
+
The Card component provides a reusable container for grouping related content in HubSpot CMS projects. It uses the theme variant system (via `data-card-variant` attribute) to apply visual styles like background color, text color, border radius, and borders. Use it when you need to visually separate and organize content into distinct sections with theme-consistent styling.
|
|
13
13
|
|
|
14
14
|
## Component Structure
|
|
15
15
|
|
|
@@ -17,29 +17,28 @@ The Card component provides a reusable container for grouping related content in
|
|
|
17
17
|
Card/
|
|
18
18
|
├── index.tsx # Main component with render logic
|
|
19
19
|
├── types.ts # TypeScript type definitions
|
|
20
|
-
├── StyleFields.tsx # HubSpot field definitions for styling
|
|
21
|
-
├── index.module.scss # CSS module
|
|
20
|
+
├── StyleFields.tsx # HubSpot field definitions for styling (VariantSelectionField)
|
|
21
|
+
├── index.module.scss # CSS module consuming theme variant CSS variables
|
|
22
22
|
└── stories/
|
|
23
23
|
├── Card.stories.tsx # Storybook examples
|
|
24
24
|
├── CardDecorator.tsx # Storybook decorator
|
|
25
|
-
└── CardDecorator.module.
|
|
25
|
+
└── CardDecorator.module.scss # Decorator styles with variant variable definitions
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Components
|
|
29
29
|
|
|
30
30
|
### Card (Main Component)
|
|
31
31
|
|
|
32
|
-
**Purpose:** Polymorphic container component that renders as a `<div>`, `<article>`, or `<section>` element with
|
|
32
|
+
**Purpose:** Polymorphic container component that renders as a `<div>`, `<article>`, or `<section>` element with theme variant styling applied via the `data-card-variant` attribute.
|
|
33
33
|
|
|
34
34
|
**Props:**
|
|
35
35
|
```tsx
|
|
36
36
|
{
|
|
37
|
-
variant?: '
|
|
37
|
+
variant?: 'cardStyle1' | 'cardStyle2' | 'cardStyle3' | 'cardStyle4'; // Theme variant (default: 'cardStyle1')
|
|
38
38
|
as?: 'div' | 'article' | 'section'; // HTML element to render (default: 'div')
|
|
39
|
-
borderRadius?: number; // Border radius in pixels (default: 8)
|
|
40
39
|
children?: React.ReactNode; // Card content
|
|
41
40
|
className?: string; // Additional CSS classes
|
|
42
|
-
style?: React.CSSProperties; // Inline styles
|
|
41
|
+
style?: React.CSSProperties; // Inline styles
|
|
43
42
|
}
|
|
44
43
|
```
|
|
45
44
|
|
|
@@ -59,7 +58,7 @@ import Card from '@hubspot/cms-component-library/Card';
|
|
|
59
58
|
### Semantic Article Card
|
|
60
59
|
|
|
61
60
|
```tsx
|
|
62
|
-
<Card as="article" variant="
|
|
61
|
+
<Card as="article" variant="cardStyle3">
|
|
63
62
|
<h3>Blog Post Title</h3>
|
|
64
63
|
<p>Article content goes here...</p>
|
|
65
64
|
</Card>
|
|
@@ -84,25 +83,11 @@ const features = [
|
|
|
84
83
|
</div>
|
|
85
84
|
```
|
|
86
85
|
|
|
87
|
-
### Card with Rich Content
|
|
88
|
-
|
|
89
|
-
```tsx
|
|
90
|
-
<Card variant="outlined">
|
|
91
|
-
<h3>Product Features</h3>
|
|
92
|
-
<ul>
|
|
93
|
-
<li>Real-time collaboration</li>
|
|
94
|
-
<li>Advanced security</li>
|
|
95
|
-
<li>Custom integrations</li>
|
|
96
|
-
</ul>
|
|
97
|
-
<button>Learn More</button>
|
|
98
|
-
</Card>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
86
|
## HubSpot CMS Integration
|
|
102
87
|
|
|
103
88
|
### Field Definitions
|
|
104
89
|
|
|
105
|
-
The Card component
|
|
90
|
+
The Card component uses `VariantSelectionField` from `@hubspot/cms-components/fields` to allow theme variant selection in the CMS editor.
|
|
106
91
|
|
|
107
92
|
#### StyleFields.tsx
|
|
108
93
|
|
|
@@ -110,14 +95,14 @@ Configurable props for customizing field labels, names, and defaults:
|
|
|
110
95
|
|
|
111
96
|
```tsx
|
|
112
97
|
<Card.StyleFields
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
98
|
+
cardVariantLabel="Card variant"
|
|
99
|
+
cardVariantName="cardVariant"
|
|
100
|
+
cardVariantDefault={{ variant_name: 'cardStyle1' }}
|
|
116
101
|
/>
|
|
117
102
|
```
|
|
118
103
|
|
|
119
104
|
**Fields:**
|
|
120
|
-
- `
|
|
105
|
+
- `cardVariant`: VariantSelectionField for selecting theme variant (variantDefinitionName: "card")
|
|
121
106
|
|
|
122
107
|
### Module Usage Example
|
|
123
108
|
|
|
@@ -127,7 +112,7 @@ import Card from '@hubspot/cms-component-library/Card';
|
|
|
127
112
|
export default function FeatureCardModule({ fieldValues }) {
|
|
128
113
|
return (
|
|
129
114
|
<Card
|
|
130
|
-
variant={fieldValues.
|
|
115
|
+
variant={fieldValues.cardVariant.variant_name}
|
|
131
116
|
as="article"
|
|
132
117
|
>
|
|
133
118
|
<h3>{fieldValues.title}</h3>
|
|
@@ -147,19 +132,13 @@ FeatureCardModule.fields = (
|
|
|
147
132
|
|
|
148
133
|
## Styling
|
|
149
134
|
|
|
150
|
-
### CSS Variables
|
|
151
|
-
|
|
152
|
-
The Card component uses CSS variables for theming and customization:
|
|
135
|
+
### Theme Variant CSS Variables (provided by the variant system via `data-card-variant`)
|
|
153
136
|
|
|
154
|
-
|
|
155
|
-
- `--
|
|
156
|
-
- `--
|
|
157
|
-
- `--
|
|
158
|
-
- `--hscl-card-boxShadow`: Box shadow (default: none)
|
|
159
|
-
- `--hscl-card-border`: Border style (default: none)
|
|
137
|
+
- `--hs-card-borderRadius`: Border radius
|
|
138
|
+
- `--hs-card-backgroundColor`: Background color
|
|
139
|
+
- `--hs-card-color`: Text color
|
|
140
|
+
- `--hs-card-border`: Border style
|
|
160
141
|
|
|
161
|
-
**Hover States:**
|
|
162
|
-
- `--hscl-card-boxShadow-hover`: Hover box shadow (inherits from base if not set)
|
|
163
142
|
|
|
164
143
|
## Accessibility
|
|
165
144
|
|
|
@@ -167,13 +146,13 @@ The Card component follows accessibility best practices:
|
|
|
167
146
|
|
|
168
147
|
- **Semantic HTML**: Use the `as` prop to render appropriate semantic elements (`article` for blog posts, `section` for page sections, `div` for generic containers)
|
|
169
148
|
- **Content Structure**: Ensure proper heading hierarchy within card content (e.g., if cards are in an h2 section, use h3 for card titles)
|
|
170
|
-
- **Color Contrast**:
|
|
149
|
+
- **Color Contrast**: Theme variants should meet WCAG AA contrast requirements; verify custom colors maintain sufficient contrast
|
|
171
150
|
- **Focus Management**: Cards themselves are not interactive, but ensure interactive elements within cards (buttons, links) have proper focus styles
|
|
172
151
|
|
|
173
152
|
## Best Practices
|
|
174
153
|
|
|
175
154
|
- **Choose semantic elements**: Use `as="article"` for self-contained content, `as="section"` for thematic groupings, and `as="div"` (default) for generic containers
|
|
176
|
-
- **Border radius via theme
|
|
155
|
+
- **Border radius via theme variants**: Border radius is controlled by the variant system, not a direct prop.
|
|
177
156
|
- **Override CSS variables**: Customize appearance using CSS variables rather than overriding class styles
|
|
178
157
|
- **Grid layouts**: Use CSS Grid or Flexbox for card layouts rather than relying on card margins
|
|
179
158
|
- **Content flexibility**: Cards are content-agnostic containers; structure internal content using appropriate semantic elements
|