@hubspot/cms-component-library 0.3.0 → 0.3.2
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/Button/index.tsx +1 -1
- package/components/componentLibrary/Divider/StyleFields.tsx +72 -14
- package/components/componentLibrary/Divider/index.tsx +31 -16
- package/components/componentLibrary/Divider/llm.txt +97 -103
- package/components/componentLibrary/Divider/stories/Divider.stories.tsx +30 -19
- package/components/componentLibrary/Divider/types.ts +31 -20
- package/components/componentLibrary/Flex/index.module.scss +4 -6
- package/components/componentLibrary/Flex/llm.txt +5 -7
- package/components/componentLibrary/Flex/types.ts +0 -4
- package/components/componentLibrary/Icon/StyleFields.tsx +69 -0
- package/components/componentLibrary/Icon/index.module.scss +2 -2
- package/components/componentLibrary/Icon/index.tsx +6 -2
- package/components/componentLibrary/Icon/llm.txt +7 -6
- package/components/componentLibrary/Icon/stories/Icon.stories.tsx +11 -11
- package/components/componentLibrary/Icon/stories/IconDecorator.module.scss +1 -2
- package/components/componentLibrary/Icon/types.ts +26 -1
- package/components/componentLibrary/Text/ContentFields.tsx +66 -0
- package/components/componentLibrary/Text/index.module.scss +3 -0
- package/components/componentLibrary/Text/index.tsx +27 -0
- package/components/componentLibrary/Text/llm.txt +170 -0
- package/components/componentLibrary/Text/types.ts +16 -0
- package/components/componentLibrary/_patterns/css-patterns.md +7 -18
- package/package.json +4 -4
- package/components/componentLibrary/Divider/ContentFields.tsx +0 -63
- package/components/componentLibrary/Heading/ContentFields.tsx +0 -37
- package/components/componentLibrary/Heading/StyleFields.tsx +0 -40
- package/components/componentLibrary/Heading/index.module.scss +0 -11
- package/components/componentLibrary/Heading/index.tsx +0 -52
- package/components/componentLibrary/Heading/llm.txt +0 -175
- package/components/componentLibrary/Heading/stories/Heading.stories.tsx +0 -88
- package/components/componentLibrary/Heading/stories/HeadingDecorator.module.scss +0 -47
- package/components/componentLibrary/Heading/stories/HeadingDecorator.tsx +0 -8
- package/components/componentLibrary/Heading/types.ts +0 -35
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Text Component
|
|
2
|
+
|
|
3
|
+
A component for rendering HubSpot CMS rich text field content.
|
|
4
|
+
|
|
5
|
+
## Import path
|
|
6
|
+
```tsx
|
|
7
|
+
import Text from '@hubspot/cms-component-library/Text';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
The Text component wraps HubSpot's native `RichText` component to provide a consistent interface for rendering editable rich text content in HubSpot CMS modules. It handles the connection between a CMS field and the rendered output, while exposing a single CSS variable for text color theming. Use this component whenever a module needs a rich text area managed through the HubSpot page editor.
|
|
13
|
+
|
|
14
|
+
## Component Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Text/
|
|
18
|
+
├── index.tsx # Main component wrapping HubSpot RichText
|
|
19
|
+
├── types.ts # TypeScript type definitions
|
|
20
|
+
├── ContentFields.tsx # HubSpot field definitions for rich text content
|
|
21
|
+
└── index.module.scss # CSS module with design tokens
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Components
|
|
25
|
+
|
|
26
|
+
### Text (Main Component)
|
|
27
|
+
|
|
28
|
+
**Purpose:** Renders a HubSpot CMS rich text field at the given `fieldPath`, applying CSS module class and CSS variable theming for text color.
|
|
29
|
+
|
|
30
|
+
**Props:**
|
|
31
|
+
```tsx
|
|
32
|
+
{
|
|
33
|
+
fieldPath?: string; // Path to the RichText field in HubSpot CMS (e.g., 'text', 'bodyContent')
|
|
34
|
+
className?: string; // Additional CSS classes (default: '')
|
|
35
|
+
style?: CSSVariables; // Inline styles, supports CSS custom properties (default: {})
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage Examples
|
|
40
|
+
|
|
41
|
+
### Basic Text
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import Text from '@hubspot/cms-component-library/Text';
|
|
45
|
+
|
|
46
|
+
<Text fieldPath="text" />
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## HubSpot CMS Integration
|
|
50
|
+
|
|
51
|
+
### Field Definitions
|
|
52
|
+
|
|
53
|
+
The Text component provides `ContentFields` for defining a `RichTextField` inside a HubSpot CMS module.
|
|
54
|
+
|
|
55
|
+
#### ContentFields
|
|
56
|
+
|
|
57
|
+
Configurable props for customizing the text field label, name, default value, and editor features:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<Text.ContentFields
|
|
61
|
+
textLabel="Body text"
|
|
62
|
+
textName="bodyText"
|
|
63
|
+
textDefault="<p>Enter your content here.</p>"
|
|
64
|
+
textFeatureSet="bodyContent"
|
|
65
|
+
/>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Props:**
|
|
69
|
+
- `textLabel` (default: `'Text'`): Label shown in the HubSpot editor
|
|
70
|
+
- `textName` (default: `'text'`): Field name used as the `fieldPath` reference in the component
|
|
71
|
+
- `textDefault` (default: `'<p>Text goes here.</p>'`): Default HTML content for the field
|
|
72
|
+
- `textFeatureSet` (`'bodyContent' | 'heading'`, default: `'bodyContent'`): Controls the editor toolbar
|
|
73
|
+
- Use `'bodyContent'` for full-featured editing (includes image, emoji, table, embed, video, and more)
|
|
74
|
+
- Use `'heading'` for a reduced feature set suited to titles and captions (no rich media)
|
|
75
|
+
|
|
76
|
+
**Fields:**
|
|
77
|
+
- `text` (name set by `textName`): RichTextField for the rich text content
|
|
78
|
+
|
|
79
|
+
#### Style Fields
|
|
80
|
+
|
|
81
|
+
**Note:** There are no style fields included with this component. Do not try to import them.
|
|
82
|
+
|
|
83
|
+
### Module Usage Example
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import Text from '@hubspot/cms-component-library/Text';
|
|
87
|
+
|
|
88
|
+
export default function ArticleModule() {
|
|
89
|
+
return (
|
|
90
|
+
<Text fieldPath="bodyText" />
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Module Fields Example
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { ModuleFields } from '@hubspot/cms-components/fields';
|
|
99
|
+
import Text from '@hubspot/cms-component-library/Text';
|
|
100
|
+
|
|
101
|
+
export const fields = (
|
|
102
|
+
<ModuleFields>
|
|
103
|
+
<Text.ContentFields
|
|
104
|
+
textLabel="Body text"
|
|
105
|
+
textName="bodyText"
|
|
106
|
+
textDefault="<p>Enter your content here.</p>"
|
|
107
|
+
textFeatureSet="bodyContent"
|
|
108
|
+
/>
|
|
109
|
+
</ModuleFields>
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Multiple Text Fields in One Module
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { ModuleFields } from '@hubspot/cms-components/fields';
|
|
117
|
+
import Text from '@hubspot/cms-component-library/Text';
|
|
118
|
+
|
|
119
|
+
export const fields = (
|
|
120
|
+
<ModuleFields>
|
|
121
|
+
<Text.ContentFields
|
|
122
|
+
textLabel="Heading text"
|
|
123
|
+
textName="headingText"
|
|
124
|
+
textDefault="<p>Enter your heading.</p>"
|
|
125
|
+
textFeatureSet="heading"
|
|
126
|
+
/>
|
|
127
|
+
<Text.ContentFields
|
|
128
|
+
textLabel="Body text"
|
|
129
|
+
textName="bodyText"
|
|
130
|
+
textDefault="<p>Enter your body content.</p>"
|
|
131
|
+
textFeatureSet="bodyContent"
|
|
132
|
+
/>
|
|
133
|
+
</ModuleFields>
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
export default function ArticleModule() {
|
|
137
|
+
return (
|
|
138
|
+
<div>
|
|
139
|
+
<Text fieldPath="headingText" />
|
|
140
|
+
<Text fieldPath="bodyText" />
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Styling
|
|
147
|
+
|
|
148
|
+
### CSS Variables
|
|
149
|
+
|
|
150
|
+
**Base Styles:**
|
|
151
|
+
- `--hscl-text-color`: Text color applied to the rendered rich text content (default: `currentColor`)
|
|
152
|
+
|
|
153
|
+
## Accessibility
|
|
154
|
+
|
|
155
|
+
- **Semantic HTML**: Content is rendered directly from the HubSpot CMS editor output, which produces standard HTML elements (`<p>`, `<h1>`–`<h6>`, `<ul>`, `<ol>`, etc.)
|
|
156
|
+
- **Structured content**: Encourage editors to use proper heading hierarchy and list elements within the rich text editor for screen reader compatibility
|
|
157
|
+
- **Link accessibility**: Links created in the rich text editor will render as native `<a>` elements
|
|
158
|
+
|
|
159
|
+
## Best Practices
|
|
160
|
+
|
|
161
|
+
- **Match `fieldPath` to `textName`**: The `fieldPath` prop on the component must match the `textName` prop on `ContentFields` — they reference the same CMS field
|
|
162
|
+
- **Choose the right feature set**: Use `'heading'` for simpler text areas (titles, captions) to keep the editor toolbar uncluttered; use `'bodyContent'` for full article or section content
|
|
163
|
+
- **Style fields**: There are no style fields for this component. Do not try to import them
|
|
164
|
+
- **CSS Variables for theming**: Override `--hscl-text-color` via the `style` prop rather than using CSS selectors that target the component's internals
|
|
165
|
+
- **Multiple fields per module**: You can render multiple `Text` components in one module by giving each a unique `textName` / `fieldPath` pair
|
|
166
|
+
|
|
167
|
+
## Related Components
|
|
168
|
+
|
|
169
|
+
- **Button**: Use for call-to-action elements within or adjacent to text content
|
|
170
|
+
- **Link**: Use for standalone clickable text or wrapped content that requires a link without rich text editing
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CSSVariables } from '../utils/types.js';
|
|
2
|
+
|
|
3
|
+
export type TextProps = {
|
|
4
|
+
fieldPath?: string;
|
|
5
|
+
className?: string;
|
|
6
|
+
style?: CSSVariables;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type TextFeatureSet = 'bodyContent' | 'heading';
|
|
10
|
+
|
|
11
|
+
export type ContentFieldsProps = {
|
|
12
|
+
textLabel?: string;
|
|
13
|
+
textName?: string;
|
|
14
|
+
textDefault?: string;
|
|
15
|
+
textFeatureSet?: TextFeatureSet;
|
|
16
|
+
};
|
|
@@ -7,7 +7,7 @@ This document outlines CSS variable naming conventions, styling patterns, and CS
|
|
|
7
7
|
**Pattern:** `--hscl-componentName-[elementName]-cssProperty-[state]`
|
|
8
8
|
|
|
9
9
|
- **Prefix:** Always start with `--hscl-` (HubSpot Component Library)
|
|
10
|
-
- **Component name:** Lowercase component name in camelCase, with name taken from the index.tsx file (e.g., `button`, `
|
|
10
|
+
- **Component name:** Lowercase component name in camelCase, with name taken from the index.tsx file (e.g., `button`, `icon`, `divider`, `image`, `accordion`, `card`)
|
|
11
11
|
- **Element name (optional):** For sub-elements within a component, specify the element name in camelCase (e.g., `icon`, `body`, `title`, `header`, `overlay`, `submenu`)
|
|
12
12
|
- Omit for properties that apply to the main component element itself
|
|
13
13
|
- Include for properties that apply to specific sub-elements
|
|
@@ -19,8 +19,6 @@ This document outlines CSS variable naming conventions, styling patterns, and CS
|
|
|
19
19
|
--hscl-button-backgroundColor
|
|
20
20
|
--hscl-button-backgroundColor-hover
|
|
21
21
|
--hscl-button-backgroundColor-focus
|
|
22
|
-
--hscl-heading-fontSize
|
|
23
|
-
--hscl-heading-textAlign
|
|
24
22
|
--hscl-divider-borderColor
|
|
25
23
|
--hscl-icon-fill
|
|
26
24
|
--hscl-card-padding
|
|
@@ -47,12 +45,12 @@ This document outlines CSS variable naming conventions, styling patterns, and CS
|
|
|
47
45
|
|
|
48
46
|
**Nested/Dynamic Variables:**
|
|
49
47
|
```typescript
|
|
50
|
-
//
|
|
51
|
-
'--hscl-
|
|
52
|
-
'--hscl-
|
|
48
|
+
// Components can use template variables to dynamically reference nested variables
|
|
49
|
+
'--hscl-component-font': `var(--hscl-component-${variantValue}-font)`
|
|
50
|
+
'--hscl-component-fontSize': `var(--hscl-component-${variantValue}-fontSize)`
|
|
53
51
|
```
|
|
54
52
|
|
|
55
|
-
**Note:** When using dynamic variants
|
|
53
|
+
**Note:** When using dynamic variants, use numbers without underscores (e.g., `variant1` not `variant_1`).
|
|
56
54
|
|
|
57
55
|
## CSS Variables Application
|
|
58
56
|
|
|
@@ -105,15 +103,6 @@ const elementStyle: CSSVariables = {
|
|
|
105
103
|
```typescript
|
|
106
104
|
import type { CSSVariables } from '../utils/types.js';
|
|
107
105
|
|
|
108
|
-
// Heading - dynamic variable references
|
|
109
|
-
const cssVariables: CSSVariables = {
|
|
110
|
-
'--hscl-heading-font': `var(--hscl-heading-${displayAsValue}-font)`,
|
|
111
|
-
'--hscl-heading-fontSize': `var(--hscl-heading-${displayAsValue}-fontSize)`,
|
|
112
|
-
...(alignment && {
|
|
113
|
-
'--hscl-heading-textAlign': alignment.toLowerCase(),
|
|
114
|
-
}),
|
|
115
|
-
};
|
|
116
|
-
|
|
117
106
|
// Divider - direct values with units
|
|
118
107
|
const cssVariables: CSSVariables = {
|
|
119
108
|
'--hscl-divider-alignment': getAlignmentCSSVar(alignment),
|
|
@@ -235,8 +224,8 @@ Variables can reference other variables for dynamic behavior:
|
|
|
235
224
|
|
|
236
225
|
```typescript
|
|
237
226
|
const cssVariables = {
|
|
238
|
-
'--hscl-
|
|
239
|
-
'--hscl-
|
|
227
|
+
'--hscl-component-font': `var(--hscl-component-${variantValue}-font)`,
|
|
228
|
+
'--hscl-component-fontSize': `var(--hscl-component-${variantValue}-fontSize)`,
|
|
240
229
|
};
|
|
241
230
|
```
|
|
242
231
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cms-component-library",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "HubSpot CMS React component library for building CMS modules",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"exports": {
|
|
@@ -21,9 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"type": "module",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@hubspot/cms-components": "
|
|
25
|
-
"sass-embedded": "^1.
|
|
26
|
-
"tsx": "^4.20.5"
|
|
24
|
+
"@hubspot/cms-components": "1.2.17",
|
|
25
|
+
"sass-embedded": "^1.97.3"
|
|
27
26
|
},
|
|
28
27
|
"peerDependencies": {
|
|
29
28
|
"react": "^18.3.1"
|
|
@@ -31,6 +30,7 @@
|
|
|
31
30
|
"devDependencies": {
|
|
32
31
|
"@types/node": "^20.0.0",
|
|
33
32
|
"@vitejs/plugin-react": "4.6.0",
|
|
33
|
+
"@hubspot/cms-dev-server": "^1.0.0",
|
|
34
34
|
"typescript": "^5.0.0"
|
|
35
35
|
},
|
|
36
36
|
"author": "content-assets",
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { ChoiceField, NumberField } from '@hubspot/cms-components/fields';
|
|
2
|
-
import { ContentFieldsProps } from './types.js';
|
|
3
|
-
|
|
4
|
-
const ContentFields = ({
|
|
5
|
-
orientationLabel = 'Orientation',
|
|
6
|
-
orientationName = 'orientation',
|
|
7
|
-
orientationDefault = 'horizontal',
|
|
8
|
-
alignmentLabel = 'Alignment',
|
|
9
|
-
alignmentName = 'alignment',
|
|
10
|
-
alignmentDefault = 'stretch',
|
|
11
|
-
lengthLabel = 'Length',
|
|
12
|
-
lengthName = 'length',
|
|
13
|
-
lengthDefault = 100,
|
|
14
|
-
thicknessLabel = 'Thickness',
|
|
15
|
-
thicknessName = 'thickness',
|
|
16
|
-
thicknessDefault = 1,
|
|
17
|
-
}: ContentFieldsProps) => {
|
|
18
|
-
return (
|
|
19
|
-
<>
|
|
20
|
-
<ChoiceField
|
|
21
|
-
label={orientationLabel}
|
|
22
|
-
name={orientationName}
|
|
23
|
-
required={true}
|
|
24
|
-
choices={[
|
|
25
|
-
['horizontal', 'Horizontal'],
|
|
26
|
-
['vertical', 'Vertical'],
|
|
27
|
-
]}
|
|
28
|
-
default={orientationDefault}
|
|
29
|
-
/>
|
|
30
|
-
<ChoiceField
|
|
31
|
-
label={alignmentLabel}
|
|
32
|
-
name={alignmentName}
|
|
33
|
-
required={true}
|
|
34
|
-
choices={[
|
|
35
|
-
['stretch', 'Stretch'],
|
|
36
|
-
['start', 'Start'],
|
|
37
|
-
['center', 'Center'],
|
|
38
|
-
['end', 'End'],
|
|
39
|
-
]}
|
|
40
|
-
default={alignmentDefault}
|
|
41
|
-
/>
|
|
42
|
-
<NumberField
|
|
43
|
-
label={lengthLabel}
|
|
44
|
-
name={lengthName}
|
|
45
|
-
required={true}
|
|
46
|
-
suffix="%"
|
|
47
|
-
min={1}
|
|
48
|
-
max={100}
|
|
49
|
-
default={lengthDefault}
|
|
50
|
-
/>
|
|
51
|
-
<NumberField
|
|
52
|
-
label={thicknessLabel}
|
|
53
|
-
name={thicknessName}
|
|
54
|
-
required={true}
|
|
55
|
-
suffix="px"
|
|
56
|
-
min={1}
|
|
57
|
-
default={thicknessDefault}
|
|
58
|
-
/>
|
|
59
|
-
</>
|
|
60
|
-
);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export default ContentFields;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { ChoiceField, TextField } from '@hubspot/cms-components/fields';
|
|
2
|
-
import type { ContentFieldsProps } from './types.js';
|
|
3
|
-
|
|
4
|
-
const ContentFields = ({
|
|
5
|
-
headingTextLabel = 'Heading text',
|
|
6
|
-
headingTextName = 'headingText',
|
|
7
|
-
headingTextDefault = 'Heading Text',
|
|
8
|
-
headingLevelLabel = 'Heading level',
|
|
9
|
-
headingLevelName = 'headingLevel',
|
|
10
|
-
headingLevelDefault = 'h1',
|
|
11
|
-
}: ContentFieldsProps) => {
|
|
12
|
-
return (
|
|
13
|
-
<>
|
|
14
|
-
<TextField
|
|
15
|
-
label={headingTextLabel}
|
|
16
|
-
name={headingTextName}
|
|
17
|
-
default={headingTextDefault}
|
|
18
|
-
/>
|
|
19
|
-
<ChoiceField
|
|
20
|
-
label={headingLevelLabel}
|
|
21
|
-
name={headingLevelName}
|
|
22
|
-
choices={[
|
|
23
|
-
['h1', 'h1'],
|
|
24
|
-
['h2', 'h2'],
|
|
25
|
-
['h3', 'h3'],
|
|
26
|
-
['h4', 'h4'],
|
|
27
|
-
['h5', 'h5'],
|
|
28
|
-
['h6', 'h6'],
|
|
29
|
-
]}
|
|
30
|
-
required={true}
|
|
31
|
-
default={headingLevelDefault}
|
|
32
|
-
/>
|
|
33
|
-
</>
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export default ContentFields;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { AlignmentField, ChoiceField } from '@hubspot/cms-components/fields';
|
|
2
|
-
import type { StyleFieldsProps } from './types.js';
|
|
3
|
-
|
|
4
|
-
const StyleFields = ({
|
|
5
|
-
textAlignLabel = 'Heading alignment',
|
|
6
|
-
textAlignName = 'headingTextAlign',
|
|
7
|
-
textAlignDefault = 'LEFT',
|
|
8
|
-
displayAsLabel = 'Display as',
|
|
9
|
-
displayAsName = 'headingDisplayAs',
|
|
10
|
-
displayAsDefault = 'h1',
|
|
11
|
-
}: StyleFieldsProps) => {
|
|
12
|
-
return (
|
|
13
|
-
<>
|
|
14
|
-
<AlignmentField
|
|
15
|
-
label={textAlignLabel}
|
|
16
|
-
name={textAlignName}
|
|
17
|
-
alignmentDirection="HORIZONTAL"
|
|
18
|
-
default={{ horizontal_align: textAlignDefault }}
|
|
19
|
-
/>
|
|
20
|
-
<ChoiceField
|
|
21
|
-
label={displayAsLabel}
|
|
22
|
-
name={displayAsName}
|
|
23
|
-
choices={[
|
|
24
|
-
['h1', 'h1'],
|
|
25
|
-
['h2', 'h2'],
|
|
26
|
-
['h3', 'h3'],
|
|
27
|
-
['h4', 'h4'],
|
|
28
|
-
['h5', 'h5'],
|
|
29
|
-
['h6', 'h6'],
|
|
30
|
-
['display_1', 'Display 1'],
|
|
31
|
-
['display_2', 'Display 2'],
|
|
32
|
-
]}
|
|
33
|
-
required={true}
|
|
34
|
-
default={displayAsDefault}
|
|
35
|
-
/>
|
|
36
|
-
</>
|
|
37
|
-
);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export default StyleFields;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
.heading {
|
|
2
|
-
font-family: var(--hscl-heading-font);
|
|
3
|
-
font-size: var(--hscl-heading-fontSize);
|
|
4
|
-
font-style: var(--hscl-heading-fontStyle);
|
|
5
|
-
font-weight: var(--hscl-heading-fontWeight);
|
|
6
|
-
line-height: var(--hscl-heading-lineHeight);
|
|
7
|
-
margin-block: var(--hscl-heading-margin);
|
|
8
|
-
color: var(--hscl-heading-color);
|
|
9
|
-
text-align: var(--hscl-heading-textAlign);
|
|
10
|
-
}
|
|
11
|
-
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import styles from './index.module.scss';
|
|
2
|
-
import ContentFields from './ContentFields.js';
|
|
3
|
-
import StyleFields from './StyleFields.js';
|
|
4
|
-
import cx from '../utils/classname.js';
|
|
5
|
-
import type { CSSVariables } from '../utils/types.js';
|
|
6
|
-
import { HeadingProps } from './types.js';
|
|
7
|
-
|
|
8
|
-
const HeadingComponent = ({
|
|
9
|
-
headingLevel,
|
|
10
|
-
displayAs,
|
|
11
|
-
textAlign,
|
|
12
|
-
className = '',
|
|
13
|
-
style = {},
|
|
14
|
-
children = 'A clear and bold heading',
|
|
15
|
-
...rest
|
|
16
|
-
}: HeadingProps) => {
|
|
17
|
-
const HeadingLevel = headingLevel;
|
|
18
|
-
const displayAsValue = displayAs || headingLevel;
|
|
19
|
-
|
|
20
|
-
const cssVariables: CSSVariables = {
|
|
21
|
-
'--hscl-heading-font': `var(--hscl-heading-${displayAsValue}-font)`,
|
|
22
|
-
'--hscl-heading-fontSize': `var(--hscl-heading-${displayAsValue}-fontSize)`,
|
|
23
|
-
'--hscl-heading-fontStyle': `var(--hscl-heading-${displayAsValue}-fontStyle)`,
|
|
24
|
-
'--hscl-heading-fontWeight': `var(--hscl-heading-${displayAsValue}-fontWeight)`,
|
|
25
|
-
...(textAlign && {
|
|
26
|
-
'--hscl-heading-textAlign': textAlign.toLowerCase(),
|
|
27
|
-
}),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const defaultClasses = cx(styles.heading, className);
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<HeadingLevel
|
|
34
|
-
className={defaultClasses}
|
|
35
|
-
style={{ ...cssVariables, ...style }}
|
|
36
|
-
{...rest}
|
|
37
|
-
>
|
|
38
|
-
{children}
|
|
39
|
-
</HeadingLevel>
|
|
40
|
-
);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
type HeadingComponentType = typeof HeadingComponent & {
|
|
44
|
-
ContentFields: typeof ContentFields;
|
|
45
|
-
StyleFields: typeof StyleFields;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const Heading = HeadingComponent as HeadingComponentType;
|
|
49
|
-
Heading.ContentFields = ContentFields;
|
|
50
|
-
Heading.StyleFields = StyleFields;
|
|
51
|
-
|
|
52
|
-
export default Heading;
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# Heading Component
|
|
2
|
-
|
|
3
|
-
A semantic heading component that renders HTML heading elements (h1-h6) with flexible visual styling and alignment controls, allowing the semantic level to differ from the visual appearance.
|
|
4
|
-
|
|
5
|
-
## Import path
|
|
6
|
-
```tsx
|
|
7
|
-
import Heading from '@hubspot/cms-component-library/Heading';
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
## Purpose
|
|
11
|
-
|
|
12
|
-
The Heading component provides proper semantic structure while maintaining visual design flexibility. It solves the common challenge where semantic HTML hierarchy (h1-h6) needs to differ from visual styling - for example, an h3 element styled to look like an h1. This separation of semantic meaning from visual presentation ensures both accessibility and design consistency.
|
|
13
|
-
|
|
14
|
-
## Component Structure
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
Heading/
|
|
18
|
-
├── index.tsx # Main component with render logic
|
|
19
|
-
├── types.ts # TypeScript type definitions
|
|
20
|
-
├── ContentFields.tsx # HubSpot field definitions for content
|
|
21
|
-
├── StyleFields.tsx # HubSpot field definitions for styling
|
|
22
|
-
├── index.module.scss # CSS module with design tokens
|
|
23
|
-
└── stories/
|
|
24
|
-
├── Heading.stories.tsx # Component usage examples
|
|
25
|
-
├── HeadingDecorator.tsx # Storybook decorator
|
|
26
|
-
└── HeadingDecorator.module.css # Decorator styles
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Props
|
|
30
|
-
|
|
31
|
-
```tsx
|
|
32
|
-
{
|
|
33
|
-
headingLevel: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; // Semantic HTML level (required)
|
|
34
|
-
displayAs?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'display_1' | 'display_2'; // Visual style (defaults to headingLevel)
|
|
35
|
-
textAlign?: 'LEFT' | 'CENTER' | 'RIGHT'; // Text alignment
|
|
36
|
-
className?: string; // Additional CSS classes
|
|
37
|
-
style?: React.CSSProperties; // Inline styles (including CSS variables)
|
|
38
|
-
children?: React.ReactNode; // Heading text content
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Usage Examples
|
|
43
|
-
|
|
44
|
-
### Basic Heading
|
|
45
|
-
|
|
46
|
-
```tsx
|
|
47
|
-
import Heading from '@hubspot/cms-component-library/Heading';
|
|
48
|
-
|
|
49
|
-
<Heading headingLevel="h1">
|
|
50
|
-
Welcome to Our Site
|
|
51
|
-
</Heading>
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Semantic vs Visual Styling
|
|
55
|
-
|
|
56
|
-
When you need a lower-level heading to look like a higher-level one:
|
|
57
|
-
|
|
58
|
-
```tsx
|
|
59
|
-
<Heading
|
|
60
|
-
headingLevel="h3" // Semantic: third-level heading
|
|
61
|
-
displayAs="h1" // Visual: styled like an h1
|
|
62
|
-
>
|
|
63
|
-
Section Title
|
|
64
|
-
</Heading>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### With Text Alignment
|
|
68
|
-
|
|
69
|
-
```tsx
|
|
70
|
-
<Heading
|
|
71
|
-
headingLevel="h2"
|
|
72
|
-
textAlign="CENTER"
|
|
73
|
-
>
|
|
74
|
-
Centered Heading
|
|
75
|
-
</Heading>
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
## HubSpot CMS Integration
|
|
80
|
-
|
|
81
|
-
### Field Definitions
|
|
82
|
-
|
|
83
|
-
#### ContentFields.tsx
|
|
84
|
-
|
|
85
|
-
```tsx
|
|
86
|
-
<Heading.ContentFields
|
|
87
|
-
headingTextLabel="Heading text"
|
|
88
|
-
headingTextName="headingText"
|
|
89
|
-
headingTextDefault="Heading Text"
|
|
90
|
-
headingLevelLabel="Heading Level"
|
|
91
|
-
headingLevelName="headingLevel"
|
|
92
|
-
headingLevelDefault="h1"
|
|
93
|
-
/>
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**Fields:**
|
|
97
|
-
- `headingText`: TextField for heading content
|
|
98
|
-
- `headingLevel`: ChoiceField for semantic level (h1-h6)
|
|
99
|
-
|
|
100
|
-
#### StyleFields.tsx
|
|
101
|
-
|
|
102
|
-
```tsx
|
|
103
|
-
<Heading.StyleFields
|
|
104
|
-
textAlignLabel="Heading text align"
|
|
105
|
-
textAlignName="headingTextAlign"
|
|
106
|
-
textAlignDefault="LEFT"
|
|
107
|
-
displayAsLabel="Display as"
|
|
108
|
-
displayAsName="headingDisplayAs"
|
|
109
|
-
displayAsDefault="h1"
|
|
110
|
-
/>
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**Fields:**
|
|
114
|
-
- `headingTextAlign`: AlignmentField for text alignment
|
|
115
|
-
- `headingDisplayAs`: ChoiceField for visual style (h1-h6, display_1, display_2)
|
|
116
|
-
|
|
117
|
-
### Module Usage Example
|
|
118
|
-
|
|
119
|
-
```tsx
|
|
120
|
-
import Heading from '@hubspot/cms-component-library/Heading';
|
|
121
|
-
|
|
122
|
-
export default function HeroModule({ fieldValues }) {
|
|
123
|
-
return (
|
|
124
|
-
<Heading
|
|
125
|
-
headingLevel={fieldValues.headingLevel}
|
|
126
|
-
displayAs={fieldValues.headingDisplayAs}
|
|
127
|
-
textAlign={fieldValues.headingTextAlign?.horizontal_align}
|
|
128
|
-
>
|
|
129
|
-
{fieldValues.headingText}
|
|
130
|
-
</Heading>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Styling
|
|
136
|
-
|
|
137
|
-
### CSS Variables
|
|
138
|
-
|
|
139
|
-
The Heading component uses CSS variables for theming and customization:
|
|
140
|
-
|
|
141
|
-
**Base Styles:**
|
|
142
|
-
- `--hscl-heading-font`: Font family
|
|
143
|
-
- `--hscl-heading-fontSize`: Font size (set by displayAs level)
|
|
144
|
-
- `--hscl-heading-fontStyle`: Font style (set by displayAs level)
|
|
145
|
-
- `--hscl-heading-fontWeight`: Font weight (set by displayAs level)
|
|
146
|
-
- `--hscl-heading-lineHeight`: Line height
|
|
147
|
-
- `--hscl-heading-margin`: Vertical margin (margin-block)
|
|
148
|
-
- `--hscl-heading-color`: Text color
|
|
149
|
-
- `--hscl-heading-textAlign`: Text alignment
|
|
150
|
-
|
|
151
|
-
**Level-specific Variables:**
|
|
152
|
-
Each heading level has its own design tokens:
|
|
153
|
-
- `--hscl-heading-h1-font`, `--hscl-heading-h1-fontSize`, etc.
|
|
154
|
-
- `--hscl-heading-h2-font`, `--hscl-heading-h2-fontSize`, etc.
|
|
155
|
-
- Through `--hscl-heading-h6-font`, `--hscl-heading-h6-fontSize`, etc.
|
|
156
|
-
- `--hscl-heading-display_1-font`, `--hscl-heading-display_1-fontSize`, etc.
|
|
157
|
-
- `--hscl-heading-display_2-font`, `--hscl-heading-display_2-fontSize`, etc.
|
|
158
|
-
|
|
159
|
-
## Accessibility
|
|
160
|
-
|
|
161
|
-
The Heading component follows accessibility best practices:
|
|
162
|
-
|
|
163
|
-
- **Semantic HTML**: Always renders the appropriate heading element (h1-h6) based on `headingLevel` for proper document structure
|
|
164
|
-
- **Visual Hierarchy Independence**: The `displayAs` prop allows visual styling to differ from semantic structure, ensuring flexibility without breaking accessibility
|
|
165
|
-
- **Proper Nesting**: Ensure headings follow logical order in your document (h1 → h2 → h3, etc.) regardless of visual styling
|
|
166
|
-
- **Screen Readers**: Screen readers announce the semantic level (`headingLevel`), not the visual style
|
|
167
|
-
- **Keyboard Navigation**: Native heading element support for keyboard navigation and focus management
|
|
168
|
-
|
|
169
|
-
## Best Practices
|
|
170
|
-
|
|
171
|
-
- **Follow semantic order**: Use headings in logical order (h1 for main title, h2 for sections, h3 for subsections, etc.)
|
|
172
|
-
- **One h1 per page**: Each page should have exactly one h1 element representing the main topic
|
|
173
|
-
- **Use displayAs for visual flexibility**: When you need a different visual style than the semantic level requires, use the `displayAs` prop
|
|
174
|
-
- **Don't skip levels**: Don't jump from h2 to h4; maintain proper hierarchy
|
|
175
|
-
|