@hubspot/cms-component-library 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/components/componentLibrary/Accordion/AccordionContent/ContentFields.tsx +5 -3
  2. package/components/componentLibrary/Accordion/AccordionItem/StyleFields.tsx +5 -3
  3. package/components/componentLibrary/Accordion/AccordionItem/index.module.scss +2 -2
  4. package/components/componentLibrary/Accordion/AccordionItem/index.tsx +3 -3
  5. package/components/componentLibrary/Accordion/AccordionTitle/ContentFields.tsx +5 -3
  6. package/components/componentLibrary/Accordion/AccordionTitle/index.module.scss +2 -2
  7. package/components/componentLibrary/Accordion/stories/Accordion.stories.tsx +80 -1
  8. package/components/componentLibrary/Accordion/stories/AccordionDecorator.tsx +14 -14
  9. package/components/componentLibrary/Button/ContentFields.tsx +5 -3
  10. package/components/componentLibrary/Button/StyleFields.tsx +5 -3
  11. package/components/componentLibrary/Button/index.module.scss +22 -14
  12. package/components/componentLibrary/Button/index.tsx +6 -6
  13. package/components/componentLibrary/Button/stories/Button.AsButton.stories.tsx +30 -1
  14. package/components/componentLibrary/Button/stories/Button.AsLink.stories.tsx +38 -1
  15. package/components/componentLibrary/Button/stories/ButtonDecorator.tsx +1 -1
  16. package/components/componentLibrary/Card/StyleFields.tsx +5 -3
  17. package/components/componentLibrary/Card/stories/Card.stories.tsx +46 -1
  18. package/components/componentLibrary/Card/stories/CardDecorator.tsx +1 -1
  19. package/components/componentLibrary/Divider/ContentFields.tsx +5 -3
  20. package/components/componentLibrary/Divider/StyleFields.tsx +5 -3
  21. package/components/componentLibrary/Divider/index.module.scss +6 -6
  22. package/components/componentLibrary/Divider/index.tsx +7 -3
  23. package/components/componentLibrary/Divider/stories/Divider.stories.tsx +44 -50
  24. package/components/componentLibrary/Divider/stories/{DividerDecorator.module.css → DividerDecorator.module.scss} +5 -4
  25. package/components/componentLibrary/Divider/stories/DividerDecorator.tsx +1 -1
  26. package/components/componentLibrary/Divider/types.ts +3 -1
  27. package/components/componentLibrary/Drawer/hooks/index.tsx +13 -0
  28. package/components/componentLibrary/Drawer/index.module.scss +94 -0
  29. package/components/componentLibrary/Drawer/index.tsx +131 -0
  30. package/components/componentLibrary/Drawer/llm.txt +416 -0
  31. package/components/componentLibrary/Drawer/stories/Drawer.stories.tsx +512 -0
  32. package/components/componentLibrary/Drawer/stories/DrawerDecorator.module.scss +8 -0
  33. package/components/componentLibrary/Drawer/stories/DrawerDecorator.tsx +18 -0
  34. package/components/componentLibrary/Drawer/types.ts +25 -0
  35. package/components/componentLibrary/Flex/stories/FlexDecorator.tsx +1 -1
  36. package/components/componentLibrary/Flex/types.ts +3 -1
  37. package/components/componentLibrary/Grid/stories/Grid.stories.tsx +454 -152
  38. package/components/componentLibrary/Grid/stories/GridDecorator.tsx +2 -2
  39. package/components/componentLibrary/Heading/ContentFields.tsx +5 -3
  40. package/components/componentLibrary/Heading/StyleFields.tsx +11 -9
  41. package/components/componentLibrary/Heading/index.tsx +3 -3
  42. package/components/componentLibrary/Heading/llm.txt +8 -8
  43. package/components/componentLibrary/Heading/stories/Heading.stories.tsx +3 -3
  44. package/components/componentLibrary/Heading/stories/HeadingDecorator.tsx +1 -1
  45. package/components/componentLibrary/Heading/types.ts +4 -4
  46. package/components/componentLibrary/Icon/ContentFields.tsx +5 -3
  47. package/components/componentLibrary/Icon/stories/Icon.stories.tsx +1 -1
  48. package/components/componentLibrary/Icon/stories/IconDecorator.tsx +1 -1
  49. package/components/componentLibrary/Image/ContentFields.tsx +5 -3
  50. package/components/componentLibrary/Image/index.tsx +4 -4
  51. package/components/componentLibrary/Image/llm.txt +17 -17
  52. package/components/componentLibrary/Image/stories/Image.stories.tsx +61 -18
  53. package/components/componentLibrary/Image/stories/ImageDecorator.tsx +1 -1
  54. package/components/componentLibrary/Image/types.ts +2 -2
  55. package/components/componentLibrary/LanguageSwitcher/ContentFields.tsx +18 -0
  56. package/components/componentLibrary/LanguageSwitcher/LanguageOptions.module.scss +37 -0
  57. package/components/componentLibrary/LanguageSwitcher/LanguageOptions.tsx +65 -0
  58. package/components/componentLibrary/LanguageSwitcher/StyleFields.tsx +48 -0
  59. package/components/componentLibrary/LanguageSwitcher/_dummyData.tsx +247 -0
  60. package/components/componentLibrary/LanguageSwitcher/assets/Globe.tsx +16 -0
  61. package/components/componentLibrary/LanguageSwitcher/index.module.scss +58 -0
  62. package/components/componentLibrary/LanguageSwitcher/index.tsx +125 -0
  63. package/components/componentLibrary/LanguageSwitcher/llm.txt +380 -0
  64. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcher.stories.tsx +349 -0
  65. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.module.scss +5 -0
  66. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.tsx +8 -0
  67. package/components/componentLibrary/LanguageSwitcher/types.ts +48 -0
  68. package/components/componentLibrary/LanguageSwitcher/utils.tsx +38 -0
  69. package/components/componentLibrary/Link/ContentFields.tsx +5 -3
  70. package/components/componentLibrary/Link/StyleFields.tsx +5 -3
  71. package/components/componentLibrary/Link/index.module.scss +10 -0
  72. package/components/componentLibrary/Link/index.tsx +24 -14
  73. package/components/componentLibrary/Link/stories/Link.stories.tsx +35 -5
  74. package/components/componentLibrary/Link/stories/LinkDecorator.tsx +11 -1
  75. package/components/componentLibrary/Link/types.ts +22 -13
  76. package/components/componentLibrary/List/ContentFields.tsx +5 -3
  77. package/components/componentLibrary/List/ListItem/ContentFields.tsx +6 -17
  78. package/components/componentLibrary/List/ListItem/index.module.scss +1 -13
  79. package/components/componentLibrary/List/ListItem/index.tsx +3 -30
  80. package/components/componentLibrary/List/ListItem/types.ts +1 -16
  81. package/components/componentLibrary/List/StyleFields.tsx +15 -18
  82. package/components/componentLibrary/List/index.module.scss +3 -0
  83. package/components/componentLibrary/List/index.tsx +5 -2
  84. package/components/componentLibrary/List/llm.txt +73 -103
  85. package/components/componentLibrary/List/stories/List.stories.tsx +56 -80
  86. package/components/componentLibrary/List/stories/ListDecorator.tsx +3 -6
  87. package/components/componentLibrary/List/types.ts +1 -3
  88. package/components/componentLibrary/Logo/_dummyLogoData.ts +12 -0
  89. package/components/componentLibrary/Logo/assets/hubspot-logo.png +0 -0
  90. package/components/componentLibrary/Logo/index.module.scss +22 -0
  91. package/components/componentLibrary/Logo/index.tsx +73 -0
  92. package/components/componentLibrary/Logo/llm.txt +262 -0
  93. package/components/componentLibrary/Logo/stories/Logo.stories.tsx +88 -0
  94. package/components/componentLibrary/Logo/stories/LogoDecorator.module.scss +10 -0
  95. package/components/componentLibrary/Logo/stories/LogoDecorator.tsx +8 -0
  96. package/components/componentLibrary/Logo/types.tsx +16 -0
  97. package/components/componentLibrary/Menu/ContentFields.tsx +16 -0
  98. package/components/componentLibrary/Menu/MenuItem/Chevron/index.module.scss +6 -0
  99. package/components/componentLibrary/Menu/MenuItem/Chevron/index.tsx +17 -0
  100. package/components/componentLibrary/Menu/MenuItem/index.module.scss +7 -0
  101. package/components/componentLibrary/Menu/MenuItem/index.tsx +266 -0
  102. package/components/componentLibrary/Menu/MenuItem/types.ts +17 -0
  103. package/components/componentLibrary/Menu/NavigationMenu/ContentFields.tsx +20 -0
  104. package/components/componentLibrary/Menu/NavigationMenu/index.tsx +18 -0
  105. package/components/componentLibrary/Menu/NavigationMenu/islands/NavigationMenuIsland.tsx +95 -0
  106. package/components/componentLibrary/Menu/NavigationMenu/islands/index.module.scss +100 -0
  107. package/components/componentLibrary/Menu/NavigationMenu/islands/types.ts +19 -0
  108. package/components/componentLibrary/Menu/NavigationMenu/llm.txt +197 -0
  109. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenu.stories.tsx +286 -0
  110. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.module.scss +15 -0
  111. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.tsx +12 -0
  112. package/components/componentLibrary/Menu/NavigationMenu/types.ts +3 -0
  113. package/components/componentLibrary/Menu/VerticalMenu/ContentFields.tsx +20 -0
  114. package/components/componentLibrary/Menu/VerticalMenu/index.tsx +18 -0
  115. package/components/componentLibrary/Menu/VerticalMenu/islands/index.module.scss +53 -0
  116. package/components/componentLibrary/Menu/VerticalMenu/islands/verticalMenuIsland.tsx +78 -0
  117. package/components/componentLibrary/Menu/VerticalMenu/llm.txt +177 -0
  118. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenu.stories.tsx +242 -0
  119. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.module.scss +19 -0
  120. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.tsx +12 -0
  121. package/components/componentLibrary/Menu/VerticalMenu/types.ts +21 -0
  122. package/components/componentLibrary/Menu/_dummyMenuData.js +1346 -0
  123. package/components/componentLibrary/Menu/types.ts +56 -0
  124. package/components/componentLibrary/Menu/utils/transformMenuData.ts +11 -0
  125. package/components/componentLibrary/_patterns/README.md +15 -17
  126. package/components/componentLibrary/_patterns/checklist-and-examples.md +17 -17
  127. package/components/componentLibrary/_patterns/component-structure.md +21 -23
  128. package/components/componentLibrary/_patterns/css-patterns.md +170 -18
  129. package/components/componentLibrary/_patterns/field-patterns.md +97 -27
  130. package/components/componentLibrary/_patterns/function-declaration-patterns.md +281 -0
  131. package/components/componentLibrary/_patterns/llm-txt.template.md +4 -2
  132. package/components/componentLibrary/_patterns/prop-naming-patterns.md +208 -0
  133. package/components/componentLibrary/_patterns/storybook-patterns.md +25 -8
  134. package/components/componentLibrary/_patterns/typescript-patterns.md +6 -3
  135. package/package.json +4 -2
  136. /package/components/componentLibrary/Button/stories/{ButtonDecorator.module.css → ButtonDecorator.module.scss} +0 -0
  137. /package/components/componentLibrary/Card/stories/{CardDecorator.module.css → CardDecorator.module.scss} +0 -0
  138. /package/components/componentLibrary/Flex/stories/{FlexDecorator.module.css → FlexDecorator.module.scss} +0 -0
  139. /package/components/componentLibrary/Grid/stories/{GridDecorator.module.css → GridDecorator.module.scss} +0 -0
  140. /package/components/componentLibrary/Heading/stories/{HeadingDecorator.module.css → HeadingDecorator.module.scss} +0 -0
  141. /package/components/componentLibrary/Icon/stories/{IconDecorator.module.css → IconDecorator.module.scss} +0 -0
  142. /package/components/componentLibrary/Image/stories/{ImageDecorator.module.css → ImageDecorator.module.scss} +0 -0
  143. /package/components/componentLibrary/Image/stories/assets/{catSmile.jpg → cat-smile.jpg} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { Decorator } from '@storybook/react';
2
- import styles from './GridDecorator.module.css';
2
+ import styles from './GridDecorator.module.scss';
3
3
 
4
- export const withGridStyles: Decorator = (Story) => {
4
+ export const withGridStyles: Decorator = Story => {
5
5
  return (
6
6
  <div className={styles.storyWrapper}>
7
7
  <Story />
@@ -1,14 +1,14 @@
1
1
  import { ChoiceField, TextField } from '@hubspot/cms-components/fields';
2
2
  import type { ContentFieldsProps } from './types.js';
3
3
 
4
- export default function ContentFields({
4
+ const ContentFields = ({
5
5
  headingTextLabel = 'Heading text',
6
6
  headingTextName = 'headingText',
7
7
  headingTextDefault = 'Heading Text',
8
8
  headingLevelLabel = 'Heading level',
9
9
  headingLevelName = 'headingLevel',
10
10
  headingLevelDefault = 'h1',
11
- }: ContentFieldsProps) {
11
+ }: ContentFieldsProps) => {
12
12
  return (
13
13
  <>
14
14
  <TextField
@@ -32,4 +32,6 @@ export default function ContentFields({
32
32
  />
33
33
  </>
34
34
  );
35
- }
35
+ };
36
+
37
+ export default ContentFields;
@@ -1,21 +1,21 @@
1
1
  import { AlignmentField, ChoiceField } from '@hubspot/cms-components/fields';
2
2
  import type { StyleFieldsProps } from './types.js';
3
3
 
4
- export default function StyleFields({
5
- alignmentLabel = 'Heading alignment',
6
- alignmentName = 'headingAlignment',
7
- alignmentDefault = 'LEFT',
4
+ const StyleFields = ({
5
+ textAlignLabel = 'Heading alignment',
6
+ textAlignName = 'headingTextAlign',
7
+ textAlignDefault = 'LEFT',
8
8
  displayAsLabel = 'Display as',
9
9
  displayAsName = 'headingDisplayAs',
10
10
  displayAsDefault = 'h1',
11
- }: StyleFieldsProps) {
11
+ }: StyleFieldsProps) => {
12
12
  return (
13
13
  <>
14
14
  <AlignmentField
15
- label={alignmentLabel}
16
- name={alignmentName}
15
+ label={textAlignLabel}
16
+ name={textAlignName}
17
17
  alignmentDirection="HORIZONTAL"
18
- default={{ horizontal_align: alignmentDefault }}
18
+ default={{ horizontal_align: textAlignDefault }}
19
19
  />
20
20
  <ChoiceField
21
21
  label={displayAsLabel}
@@ -35,4 +35,6 @@ export default function StyleFields({
35
35
  />
36
36
  </>
37
37
  );
38
- }
38
+ };
39
+
40
+ export default StyleFields;
@@ -8,7 +8,7 @@ import { HeadingProps } from './types.js';
8
8
  const HeadingComponent = ({
9
9
  headingLevel,
10
10
  displayAs,
11
- alignment,
11
+ textAlign,
12
12
  className = '',
13
13
  style = {},
14
14
  children = 'A clear and bold heading',
@@ -22,8 +22,8 @@ const HeadingComponent = ({
22
22
  '--hscl-heading-fontSize': `var(--hscl-heading-${displayAsValue}-fontSize)`,
23
23
  '--hscl-heading-fontStyle': `var(--hscl-heading-${displayAsValue}-fontStyle)`,
24
24
  '--hscl-heading-fontWeight': `var(--hscl-heading-${displayAsValue}-fontWeight)`,
25
- ...(alignment && {
26
- '--hscl-heading-textAlign': alignment.toLowerCase(),
25
+ ...(textAlign && {
26
+ '--hscl-heading-textAlign': textAlign.toLowerCase(),
27
27
  }),
28
28
  };
29
29
 
@@ -32,7 +32,7 @@ Heading/
32
32
  {
33
33
  headingLevel: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; // Semantic HTML level (required)
34
34
  displayAs?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'display_1' | 'display_2'; // Visual style (defaults to headingLevel)
35
- alignment?: 'LEFT' | 'CENTER' | 'RIGHT'; // Text alignment
35
+ textAlign?: 'LEFT' | 'CENTER' | 'RIGHT'; // Text alignment
36
36
  className?: string; // Additional CSS classes
37
37
  style?: React.CSSProperties; // Inline styles (including CSS variables)
38
38
  children?: React.ReactNode; // Heading text content
@@ -64,12 +64,12 @@ When you need a lower-level heading to look like a higher-level one:
64
64
  </Heading>
65
65
  ```
66
66
 
67
- ### With Alignment
67
+ ### With Text Alignment
68
68
 
69
69
  ```tsx
70
70
  <Heading
71
71
  headingLevel="h2"
72
- alignment="CENTER"
72
+ textAlign="CENTER"
73
73
  >
74
74
  Centered Heading
75
75
  </Heading>
@@ -101,9 +101,9 @@ When you need a lower-level heading to look like a higher-level one:
101
101
 
102
102
  ```tsx
103
103
  <Heading.StyleFields
104
- alignmentLabel="Heading Alignment"
105
- alignmentName="headingAlignment"
106
- alignmentDefault="LEFT"
104
+ textAlignLabel="Heading text align"
105
+ textAlignName="headingTextAlign"
106
+ textAlignDefault="LEFT"
107
107
  displayAsLabel="Display as"
108
108
  displayAsName="headingDisplayAs"
109
109
  displayAsDefault="h1"
@@ -111,7 +111,7 @@ When you need a lower-level heading to look like a higher-level one:
111
111
  ```
112
112
 
113
113
  **Fields:**
114
- - `headingAlignment`: AlignmentField for text alignment
114
+ - `headingTextAlign`: AlignmentField for text alignment
115
115
  - `headingDisplayAs`: ChoiceField for visual style (h1-h6, display_1, display_2)
116
116
 
117
117
  ### Module Usage Example
@@ -124,7 +124,7 @@ export default function HeroModule({ fieldValues }) {
124
124
  <Heading
125
125
  headingLevel={fieldValues.headingLevel}
126
126
  displayAs={fieldValues.headingDisplayAs}
127
- alignment={fieldValues.headingAlignment?.horizontal_align}
127
+ textAlign={fieldValues.headingTextAlign?.horizontal_align}
128
128
  >
129
129
  {fieldValues.headingText}
130
130
  </Heading>
@@ -52,17 +52,17 @@ export const TextAlignment: Story = {
52
52
  render: () => (
53
53
  <SBContainer flex direction="column" gap="large" minWidth="500px">
54
54
  <SBContainer addBackground>
55
- <Heading headingLevel="h2" alignment="LEFT">
55
+ <Heading headingLevel="h2" textAlign="LEFT">
56
56
  Left Aligned
57
57
  </Heading>
58
58
  </SBContainer>
59
59
  <SBContainer addBackground>
60
- <Heading headingLevel="h2" alignment="CENTER">
60
+ <Heading headingLevel="h2" textAlign="CENTER">
61
61
  Center Aligned
62
62
  </Heading>
63
63
  </SBContainer>
64
64
  <SBContainer addBackground>
65
- <Heading headingLevel="h2" alignment="RIGHT">
65
+ <Heading headingLevel="h2" textAlign="RIGHT">
66
66
  Right Aligned
67
67
  </Heading>
68
68
  </SBContainer>
@@ -1,5 +1,5 @@
1
1
  import type { Decorator } from '@storybook/react';
2
- import styles from './HeadingDecorator.module.css';
2
+ import styles from './HeadingDecorator.module.scss';
3
3
 
4
4
  export const withHeadingStyles: Decorator = Story => (
5
5
  <div className={styles.decoratorContainer}>
@@ -10,7 +10,7 @@ export type DisplayAs = HeadingLevel | 'display_1' | 'display_2';
10
10
  export type HeadingProps = {
11
11
  headingLevel: HeadingLevel;
12
12
  displayAs?: DisplayAs;
13
- alignment?: (typeof AlignmentFieldDefaults)['horizontal_align'];
13
+ textAlign?: (typeof AlignmentFieldDefaults)['horizontal_align'];
14
14
  className?: string;
15
15
  style?: React.CSSProperties;
16
16
  children?: React.ReactNode;
@@ -26,9 +26,9 @@ export type ContentFieldsProps = {
26
26
  };
27
27
 
28
28
  export type StyleFieldsProps = {
29
- alignmentLabel?: string;
30
- alignmentName?: string;
31
- alignmentDefault?: (typeof AlignmentFieldDefaults)['horizontal_align'];
29
+ textAlignLabel?: string;
30
+ textAlignName?: string;
31
+ textAlignDefault?: (typeof AlignmentFieldDefaults)['horizontal_align'];
32
32
  displayAsLabel?: string;
33
33
  displayAsName?: string;
34
34
  displayAsDefault?: DisplayAs;
@@ -5,13 +5,13 @@ import {
5
5
  } from '@hubspot/cms-components/fields';
6
6
  import { ContentFieldsProps } from './types.js';
7
7
 
8
- export default function ContentFields({
8
+ const ContentFields = ({
9
9
  iconLabel = 'Icon',
10
10
  iconName = 'icon',
11
11
  iconDefault = { name: 'angle-right', unicode: 'f105', type: 'SOLID' },
12
12
  addIconToggle = false,
13
13
  ...rest
14
- }: ContentFieldsProps) {
14
+ }: ContentFieldsProps) => {
15
15
  let showIconLabel = 'Show icon';
16
16
  let showIconName = 'showIcon';
17
17
  let showIconDefault = false;
@@ -48,4 +48,6 @@ export default function ContentFields({
48
48
  />
49
49
  </>
50
50
  );
51
- }
51
+ };
52
+
53
+ export default ContentFields;
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import Icon from '../index.js';
3
3
  import { IconProps } from '../types.js';
4
4
  import { withIconStyles } from './IconDecorator.js';
5
- import styles from './IconDecorator.module.css';
5
+ import styles from './IconDecorator.module.scss';
6
6
  import { SBContainer } from '@sb-utils/SBContainer.js';
7
7
 
8
8
  const meta: Meta<IconProps> = {
@@ -1,5 +1,5 @@
1
1
  import type { Decorator } from '@storybook/react';
2
- import styles from './IconDecorator.module.css';
2
+ import styles from './IconDecorator.module.scss';
3
3
 
4
4
  export const withIconStyles: Decorator = Story => (
5
5
  <div className={styles.iconContainer}>
@@ -1,7 +1,7 @@
1
1
  import { ImageField } from '@hubspot/cms-components/fields';
2
2
  import { ContentFieldsProps } from './types.js';
3
3
 
4
- export default function ContentFields({
4
+ const ContentFields = ({
5
5
  imageLabel = 'Image',
6
6
  imageName = 'image',
7
7
  imageDefault = {
@@ -14,7 +14,7 @@ export default function ContentFields({
14
14
  showLoading = true,
15
15
  resizable = true,
16
16
  responsive = true,
17
- }: ContentFieldsProps) {
17
+ }: ContentFieldsProps) => {
18
18
  return (
19
19
  <>
20
20
  <ImageField
@@ -27,4 +27,6 @@ export default function ContentFields({
27
27
  />
28
28
  </>
29
29
  );
30
- }
30
+ };
31
+
32
+ export default ContentFields;
@@ -7,8 +7,8 @@ const ImageComponent = ({
7
7
  src,
8
8
  alt,
9
9
  title = '',
10
- imgWidth,
11
- imgHeight,
10
+ width,
11
+ height,
12
12
  loading = 'lazy',
13
13
  responsive = true,
14
14
  className = '',
@@ -27,8 +27,8 @@ const ImageComponent = ({
27
27
  src={src}
28
28
  alt={alt}
29
29
  title={title}
30
- width={imgWidth}
31
- height={imgHeight}
30
+ width={width}
31
+ height={height}
32
32
  loading={loading !== 'disabled' ? loading : 'eager'}
33
33
  className={combinedClasses}
34
34
  style={style}
@@ -32,9 +32,9 @@ Image/
32
32
  src: string; // Image source URL (required)
33
33
  alt: string; // Alternative text for accessibility (required)
34
34
  title?: string; // Tooltip text on hover
35
- imgWidth?: number; // Image width in pixels
36
- imgHeight?: number; // Image height in pixels
37
- loading?: 'lazy' | 'eager' | 'disabled'; // Loading strategy (default: 'lazy')
35
+ width?: number; // Image width in pixels
36
+ height?: number; // Image height in pixels
37
+ loading?: 'lazy' | 'eager' | 'disabled'; // Loading strategy (default: 'lazy')
38
38
  responsive?: boolean; // Enable responsive scaling (default: true)
39
39
  className?: string; // Additional CSS classes
40
40
  style?: React.CSSProperties; // Inline styles (including CSS variables)
@@ -52,8 +52,8 @@ import Image from '@hubspot/cms-component-library/Image';
52
52
  <Image
53
53
  src="https://example.com/image.jpg"
54
54
  alt="Description of the image"
55
- imgWidth={800}
56
- imgHeight={600}
55
+ width={800}
56
+ height={600}
57
57
  />
58
58
  ```
59
59
 
@@ -63,8 +63,8 @@ import Image from '@hubspot/cms-component-library/Image';
63
63
  <Image
64
64
  src="https://example.com/hero-image.jpg"
65
65
  alt="Hero banner showcasing product features"
66
- imgWidth={1200}
67
- imgHeight={600}
66
+ width={1200}
67
+ height={600}
68
68
  responsive={true}
69
69
  />
70
70
  ```
@@ -76,8 +76,8 @@ import Image from '@hubspot/cms-component-library/Image';
76
76
  src="https://example.com/product.jpg"
77
77
  alt="Product showcase"
78
78
  title="Click to view product details"
79
- imgWidth={600}
80
- imgHeight={400}
79
+ width={600}
80
+ height={400}
81
81
  />
82
82
  ```
83
83
 
@@ -87,8 +87,8 @@ import Image from '@hubspot/cms-component-library/Image';
87
87
  <Image
88
88
  src="https://example.com/hero.jpg"
89
89
  alt="Hero image"
90
- imgWidth={1920}
91
- imgHeight={1080}
90
+ width={1920}
91
+ height={1080}
92
92
  loading="eager"
93
93
  responsive={true}
94
94
  />
@@ -100,8 +100,8 @@ import Image from '@hubspot/cms-component-library/Image';
100
100
  <Image
101
101
  src="https://example.com/profile.jpg"
102
102
  alt="User profile picture"
103
- imgWidth={200}
104
- imgHeight={200}
103
+ width={200}
104
+ height={200}
105
105
  style={{
106
106
  borderRadius: '50%',
107
107
  border: '4px solid #0066cc',
@@ -156,8 +156,8 @@ export default function HeroModule({ fieldValues }) {
156
156
  <Image
157
157
  src={fieldValues.heroImage?.src}
158
158
  alt={fieldValues.heroImage?.alt}
159
- imgWidth={fieldValues.heroImage?.width}
160
- imgHeight={fieldValues.heroImage?.height}
159
+ width={fieldValues.heroImage?.width}
160
+ height={fieldValues.heroImage?.height}
161
161
  loading={fieldValues.heroImage?.loading || 'lazy'}
162
162
  responsive={true}
163
163
  />
@@ -194,14 +194,14 @@ The Image component follows accessibility best practices:
194
194
  - For decorative images, use an empty string: `alt=""`
195
195
  - **Semantic HTML**: Renders native `<img>` elements with proper attributes
196
196
  - **Title Attribute**: Optional `title` prop provides additional context on hover
197
- - **Dimensions**: `imgWidth` and `imgHeight` props help browsers allocate space and prevent layout shift
197
+ - **Dimensions**: `width` and `height` props help browsers allocate space and prevent layout shift
198
198
 
199
199
  ## Best Practices
200
200
 
201
201
  - **Always provide meaningful alt text**: Describe the content and purpose of the image for screen readers
202
202
  - **Use lazy loading for below-the-fold images**: Keep the default `loading="lazy"` for images not immediately visible
203
203
  - **Use eager loading for above-the-fold images**: Set `loading="eager"` for hero images and critical content
204
- - **Specify dimensions**: Always provide `imgWidth` and `imgHeight` to prevent cumulative layout shift (CLS)
204
+ - **Specify dimensions**: Always provide `width` and `height` to prevent cumulative layout shift (CLS)
205
205
  - **Enable responsive behavior**: Keep `responsive={true}` (default) unless you need fixed dimensions
206
206
  - **CSS Variables for consistent styling**: Use CSS variables rather than inline styles for site-wide theming
207
207
  - **Style fields**: The image component doesn't include style fields. Do not try to import them.
@@ -1,8 +1,8 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import Image from '../index.js';
3
3
  import { withImageStyles } from './ImageDecorator.js';
4
- import { SBContainer } from '@sb-utils/SBContainer.js';
5
- import placeholderImage from './assets/catSmile.jpg';
4
+ import { SBContainer, SBFocusWrapper } from '@sb-utils';
5
+ import placeholderImage from './assets/cat-smile.jpg';
6
6
 
7
7
  const meta: Meta<typeof Image> = {
8
8
  title: 'Component Library/Image',
@@ -26,8 +26,8 @@ export const Default: Story = {
26
26
  args: {
27
27
  src: placeholderImage,
28
28
  alt: 'A placeholder image demonstrating default Image component behavior',
29
- imgWidth: 600,
30
- imgHeight: 400,
29
+ width: 600,
30
+ height: 400,
31
31
  loading: 'lazy',
32
32
  responsive: true,
33
33
  },
@@ -47,8 +47,8 @@ export const ResponsiveBehavior: Story = {
47
47
  <Image
48
48
  src={placeholderImage}
49
49
  alt="Responsive image"
50
- imgWidth={800}
51
- imgHeight={600}
50
+ width={800}
51
+ height={600}
52
52
  responsive={true}
53
53
  />
54
54
  </SBContainer>
@@ -67,8 +67,8 @@ export const ResponsiveBehavior: Story = {
67
67
  <Image
68
68
  src={placeholderImage}
69
69
  alt="Non-responsive image at full 800x600 size"
70
- imgWidth={800}
71
- imgHeight={600}
70
+ width={800}
71
+ height={600}
72
72
  responsive={false}
73
73
  style={{ width: '800px', height: '600px' }}
74
74
  />
@@ -92,8 +92,8 @@ export const AccessibilityAltText: Story = {
92
92
  src={placeholderImage}
93
93
  alt="Sample image with tooltip"
94
94
  title="Hover over me to see the title tooltip"
95
- imgWidth={400}
96
- imgHeight={300}
95
+ width={400}
96
+ height={300}
97
97
  responsive={true}
98
98
  />
99
99
  <p style={{ marginTop: '8px', fontSize: '14px', color: '#666' }}>
@@ -114,8 +114,8 @@ export const EdgeCases: Story = {
114
114
  <Image
115
115
  src="fake-doesn't-exist.jpg"
116
116
  alt="This image failed to load - alt text is displayed"
117
- imgWidth={400}
118
- imgHeight={300}
117
+ width={400}
118
+ height={300}
119
119
  responsive={true}
120
120
  />
121
121
  </SBContainer>
@@ -132,8 +132,8 @@ export const WithCustomStyling: Story = {
132
132
  <Image
133
133
  src={placeholderImage}
134
134
  alt="Image with rounded corners"
135
- imgWidth={400}
136
- imgHeight={300}
135
+ width={400}
136
+ height={300}
137
137
  responsive={true}
138
138
  style={{ borderRadius: '16px' }}
139
139
  />
@@ -144,8 +144,8 @@ export const WithCustomStyling: Story = {
144
144
  <Image
145
145
  src={placeholderImage}
146
146
  alt="Image with border"
147
- imgWidth={400}
148
- imgHeight={300}
147
+ width={400}
148
+ height={300}
149
149
  responsive={true}
150
150
  style={{ border: '4px solid #3498db', borderRadius: '8px' }}
151
151
  />
@@ -156,8 +156,8 @@ export const WithCustomStyling: Story = {
156
156
  <Image
157
157
  src={placeholderImage}
158
158
  alt="Image with drop shadow"
159
- imgWidth={400}
160
- imgHeight={300}
159
+ width={400}
160
+ height={300}
161
161
  responsive={true}
162
162
  style={{
163
163
  boxShadow: '0 10px 30px rgba(0,0,0,0.3)',
@@ -168,3 +168,46 @@ export const WithCustomStyling: Story = {
168
168
  </SBContainer>
169
169
  ),
170
170
  };
171
+
172
+ export const InteractionStates: Story = {
173
+ name: 'Interaction States (Wrapped in Link)',
174
+ render: () => (
175
+ <SBContainer flex direction="column" gap="large">
176
+ <SBContainer addBackground>
177
+ <h4>Clickable Image</h4>
178
+ <p>Image wrapped in a link becomes clickable and shows hover effects</p>
179
+ <a href="https://www.hubspot.com" style={{ textDecoration: 'none' }}>
180
+ <Image
181
+ src={placeholderImage}
182
+ alt="Clickable image"
183
+ width={400}
184
+ height={300}
185
+ responsive={true}
186
+ style={{ borderRadius: '8px' }}
187
+ />
188
+ </a>
189
+ </SBContainer>
190
+
191
+ <SBContainer addBackground>
192
+ <h4>Focus State</h4>
193
+ <p>
194
+ In production, this focus outline appears when navigating with the
195
+ keyboard (Tab key). This Storybook demo auto-applies the focus state
196
+ to show how it looks.
197
+ </p>
198
+ <SBFocusWrapper>
199
+ <a href="https://www.hubspot.com" style={{ textDecoration: 'none' }}>
200
+ <Image
201
+ src={placeholderImage}
202
+ alt="Focused image wrapped in link"
203
+ width={400}
204
+ height={300}
205
+ responsive={true}
206
+ style={{ borderRadius: '8px' }}
207
+ />
208
+ </a>
209
+ </SBFocusWrapper>
210
+ </SBContainer>
211
+ </SBContainer>
212
+ ),
213
+ };
@@ -1,5 +1,5 @@
1
1
  import type { Decorator } from '@storybook/react';
2
- import styles from './ImageDecorator.module.css';
2
+ import styles from './ImageDecorator.module.scss';
3
3
 
4
4
  /**
5
5
  * Shared decorator for Image stories that applies default CSS variables
@@ -4,8 +4,8 @@ export type ImageProps = {
4
4
  src: string;
5
5
  alt: string;
6
6
  title?: string;
7
- imgWidth?: number;
8
- imgHeight?: number;
7
+ width?: number;
8
+ height?: number;
9
9
  loading?: 'lazy' | 'eager' | 'disabled';
10
10
  responsive?: boolean;
11
11
  className?: string;
@@ -0,0 +1,18 @@
1
+ import { TextField } from '@hubspot/cms-components/fields';
2
+ import type { ContentFieldsProps } from './types.js';
3
+
4
+ const ContentFields = ({
5
+ languageSwitcherSelectTextLabel = 'Language selector text',
6
+ languageSwitcherSelectTextName = 'languageSwitcherSelectText',
7
+ languageSwitcherSelectTextDefault = 'Select a language',
8
+ }: ContentFieldsProps) => {
9
+ return (
10
+ <TextField
11
+ label={languageSwitcherSelectTextLabel}
12
+ name={languageSwitcherSelectTextName}
13
+ default={languageSwitcherSelectTextDefault}
14
+ />
15
+ );
16
+ };
17
+
18
+ export default ContentFields;
@@ -0,0 +1,37 @@
1
+ .languageList {
2
+ background-color: var(--hscl-languageSwitcher-backgroundColor, transparent);
3
+ }
4
+
5
+ .languageItem {
6
+ &:last-child {
7
+ margin-bottom: 0;
8
+ }
9
+ }
10
+
11
+ .languageItem--active {
12
+ background-color: var(--hscl-languageSwitcher-backgroundColor-hover, transparent);
13
+ }
14
+
15
+ .languageItem .languageLink {
16
+ display: flex;
17
+ padding: 12px 16px;
18
+ align-items: center;
19
+ color: var(--hscl-languageSwitcher-color, #000000);
20
+ font-size: var(--hscl-languageSwitcher-fontSize, 18px);
21
+ font-weight: 600;
22
+ gap: 12px;
23
+ text-decoration: none;
24
+ transition: background-color 0.2s ease;
25
+
26
+ &:focus,
27
+ &:active,
28
+ &:visited,
29
+ &:hover {
30
+ color: var(--hscl-languageSwitcher-color-hover, #000000);
31
+ text-decoration: none;
32
+ }
33
+
34
+ &:hover {
35
+ background-color: var(--hscl-languageSwitcher-backgroundColor-hover, #f1f5f9);
36
+ }
37
+ }