@hubspot/cms-component-library 0.1.0-beta.1 → 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,11 +1,11 @@
1
1
  import { RichTextField } 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
  contentLabel = 'Content',
6
6
  contentName = 'content',
7
7
  contentDefault = '<p>Accordion content goes here. You can add multiple lines of text.</p>',
8
- }: ContentFieldsProps) {
8
+ }: ContentFieldsProps) => {
9
9
  return (
10
10
  // Todo: consider migrating over field library
11
11
  <RichTextField
@@ -14,4 +14,6 @@ export default function ContentFields({
14
14
  default={contentDefault}
15
15
  />
16
16
  );
17
- }
17
+ };
18
+
19
+ export default ContentFields;
@@ -1,11 +1,11 @@
1
1
  import { ChoiceField } from '@hubspot/cms-components/fields';
2
2
  import type { StyleFieldsProps } from './types.js';
3
3
 
4
- export default function StyleFields({
4
+ const StyleFields = ({
5
5
  variantLabel = 'Variant',
6
6
  variantName = 'variant',
7
7
  variantDefault = 'variant1',
8
- }: StyleFieldsProps) {
8
+ }: StyleFieldsProps) => {
9
9
  return (
10
10
  <ChoiceField
11
11
  label={variantLabel}
@@ -22,4 +22,6 @@ export default function StyleFields({
22
22
  default={variantDefault}
23
23
  />
24
24
  );
25
- }
25
+ };
26
+
27
+ export default StyleFields;
@@ -1,9 +1,9 @@
1
1
  .accordionItem {
2
- border: var(--hscl-accordion-borderThickness, 1px) solid
2
+ border: var(--hscl-accordion-borderWidth, 1px) solid
3
3
  var(--hscl-accordion-borderColor, #e0e0e0);
4
4
  border-radius: var(--hscl-accordion-borderRadius, 4px);
5
5
  background-color: var(--hscl-accordion-backgroundColor, transparent);
6
- color: var(--hscl-accordion-textColor, inherit);
6
+ color: var(--hscl-accordion-color, inherit);
7
7
  overflow: hidden;
8
8
  }
9
9
 
@@ -17,10 +17,10 @@ const AccordionItemComponent = ({
17
17
  ? mapVariantToCssVars('--hscl-accordion', variant, [
18
18
  'borderColor',
19
19
  'borderRadius',
20
- 'borderThickness',
20
+ 'borderWidth',
21
21
  'backgroundColor',
22
- 'textColor',
23
- 'icon-fillColor',
22
+ 'color',
23
+ 'icon-fill',
24
24
  ])
25
25
  : {};
26
26
 
@@ -1,14 +1,14 @@
1
1
  import { TextField, ChoiceField } 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
  titleLabel = 'Title',
6
6
  titleName = 'title',
7
7
  titleDefault = 'Accordion Title',
8
8
  iconLabel = 'Icon',
9
9
  iconName = 'icon',
10
10
  iconDefault = 'chevron',
11
- }: ContentFieldsProps) {
11
+ }: ContentFieldsProps) => {
12
12
  return (
13
13
  <>
14
14
  <ChoiceField
@@ -26,4 +26,6 @@ export default function ContentFields({
26
26
  <TextField label={titleLabel} name={titleName} default={titleDefault} />
27
27
  </>
28
28
  );
29
- }
29
+ };
30
+
31
+ export default ContentFields;
@@ -15,14 +15,14 @@
15
15
  .accordionTitleText {
16
16
  font-size: var(--hscl-accordion-title-fontSize, 24px);
17
17
  font-weight: var(--hscl-accordion-title-fontWeight, 600);
18
- margin-inline-end: var(--hscl-accordion-title-textMarginEnd, 16px);
18
+ margin-inline-end: var(--hscl-accordion-title-marginInlineEnd, 16px);
19
19
  flex: 1;
20
20
  }
21
21
 
22
22
  .accordionIcon {
23
23
  flex-shrink: 0;
24
24
  align-self: center;
25
- fill: var(--hscl-accordion-icon-fillColor, currentColor);
25
+ fill: var(--hscl-accordion-icon-fill, currentColor);
26
26
  margin-inline-start: auto;
27
27
  transition: transform 0.3s ease;
28
28
  display: block;
@@ -6,7 +6,7 @@ import Accordion, {
6
6
  } from '../index.js';
7
7
  import { AccordionProps } from '../types.js';
8
8
  import { withAccordionStyles } from './AccordionDecorator.js';
9
- import { SBContainer } from '@sb-utils/SBContainer.js';
9
+ import { SBContainer, SBFocusWrapper } from '@sb-utils';
10
10
 
11
11
  const meta: Meta<AccordionProps> = {
12
12
  title: 'Component Library/Accordion',
@@ -462,3 +462,82 @@ export const EdgeCases: Story = {
462
462
  </SBContainer>
463
463
  ),
464
464
  };
465
+
466
+ export const InteractionStates: Story = {
467
+ name: 'Interaction States',
468
+ render: () => (
469
+ <SBContainer flex direction="column" gap="large" width="800px">
470
+ <SBContainer addBackground>
471
+ <h4>Keyboard Navigation</h4>
472
+ <p>
473
+ <strong>Try:</strong> Press Tab to focus on accordion titles, then
474
+ press Enter or Space to expand/collapse them
475
+ </p>
476
+ <Accordion>
477
+ <AccordionItem>
478
+ <AccordionTitle>First accordion item</AccordionTitle>
479
+ <AccordionContent>
480
+ <p>
481
+ Accordion titles (summary elements) are natively keyboard
482
+ accessible. Tab to focus, Enter/Space to toggle.
483
+ </p>
484
+ </AccordionContent>
485
+ </AccordionItem>
486
+ <AccordionItem>
487
+ <AccordionTitle>Second accordion item</AccordionTitle>
488
+ <AccordionContent>
489
+ <p>
490
+ Focus rings appear automatically when navigating with the
491
+ keyboard, providing clear visual feedback.
492
+ </p>
493
+ </AccordionContent>
494
+ </AccordionItem>
495
+ <AccordionItem>
496
+ <AccordionTitle>Third accordion item</AccordionTitle>
497
+ <AccordionContent>
498
+ <p>
499
+ This demonstrates how users can navigate through all accordion
500
+ items using only the keyboard.
501
+ </p>
502
+ </AccordionContent>
503
+ </AccordionItem>
504
+ </Accordion>
505
+ </SBContainer>
506
+
507
+ <SBContainer addBackground>
508
+ <h4>Focus State</h4>
509
+ <p>
510
+ In production, this focus outline appears when navigating with the
511
+ keyboard (Tab key). This Storybook demo auto-applies the focus state
512
+ to show how it looks.
513
+ </p>
514
+ <SBFocusWrapper>
515
+ <Accordion>
516
+ <AccordionItem>
517
+ <AccordionTitle>Focused accordion item</AccordionTitle>
518
+ <AccordionContent>
519
+ <p>
520
+ This accordion title automatically receives focus to
521
+ demonstrate the focus ring appearance.
522
+ </p>
523
+ </AccordionContent>
524
+ </AccordionItem>
525
+ </Accordion>
526
+ </SBFocusWrapper>
527
+ </SBContainer>
528
+
529
+ <SBContainer addBackground>
530
+ <h4>Hover State</h4>
531
+ <p>Hover over accordion titles to see the hover effect</p>
532
+ <Accordion>
533
+ <AccordionItem>
534
+ <AccordionTitle>Hover over this title</AccordionTitle>
535
+ <AccordionContent>
536
+ <p>The title shows a subtle background change on hover.</p>
537
+ </AccordionContent>
538
+ </AccordionItem>
539
+ </Accordion>
540
+ </SBContainer>
541
+ </SBContainer>
542
+ ),
543
+ };
@@ -2,36 +2,36 @@ import type { Decorator } from '@storybook/react';
2
2
  import type { CSSVariables } from '../../utils/types.js';
3
3
 
4
4
  const defaultAccordionStyles: CSSVariables = {
5
- '--hscl-listItem-textColor-primary': '#33475b',
6
- '--hscl-listItem-textColor-secondary': '#516f90',
5
+ '--hscl-listItem-color-primary': '#33475b',
6
+ '--hscl-listItem-color-secondary': '#516f90',
7
7
 
8
8
  '--hscl-accordion-variant1-borderColor': '#e0e0e0',
9
9
  '--hscl-accordion-variant1-borderRadius': '4px',
10
- '--hscl-accordion-variant1-borderThickness': '1px',
10
+ '--hscl-accordion-variant1-borderWidth': '1px',
11
11
  '--hscl-accordion-variant1-backgroundColor': 'transparent',
12
- '--hscl-accordion-variant1-textColor': '#33475b',
13
- '--hscl-accordion-variant1-icon-fillColor': '#33475b',
12
+ '--hscl-accordion-variant1-color': '#33475b',
13
+ '--hscl-accordion-variant1-icon-fill': '#33475b',
14
14
 
15
15
  '--hscl-accordion-variant2-borderColor': '#cbd6e2',
16
16
  '--hscl-accordion-variant2-borderRadius': '8px',
17
- '--hscl-accordion-variant2-borderThickness': '1px',
17
+ '--hscl-accordion-variant2-borderWidth': '1px',
18
18
  '--hscl-accordion-variant2-backgroundColor': '#f5f8fa',
19
- '--hscl-accordion-variant2-textColor': '#33475b',
20
- '--hscl-accordion-variant2-icon-fillColor': '#516f90',
19
+ '--hscl-accordion-variant2-color': '#33475b',
20
+ '--hscl-accordion-variant2-icon-fill': '#516f90',
21
21
 
22
22
  '--hscl-accordion-variant3-borderColor': '#ff7a59',
23
23
  '--hscl-accordion-variant3-borderRadius': '0',
24
- '--hscl-accordion-variant3-borderThickness': '2px',
24
+ '--hscl-accordion-variant3-borderWidth': '2px',
25
25
  '--hscl-accordion-variant3-backgroundColor': 'transparent',
26
- '--hscl-accordion-variant3-textColor': '#33475b',
27
- '--hscl-accordion-variant3-icon-fillColor': '#ff7a59',
26
+ '--hscl-accordion-variant3-color': '#33475b',
27
+ '--hscl-accordion-variant3-icon-fill': '#ff7a59',
28
28
 
29
29
  '--hscl-accordion-variant4-borderColor': '#33475b',
30
30
  '--hscl-accordion-variant4-borderRadius': '12px',
31
- '--hscl-accordion-variant4-borderThickness': '1px',
31
+ '--hscl-accordion-variant4-borderWidth': '1px',
32
32
  '--hscl-accordion-variant4-backgroundColor': '#eaf0f6',
33
- '--hscl-accordion-variant4-textColor': '#33475b',
34
- '--hscl-accordion-variant4-icon-fillColor': '#33475b',
33
+ '--hscl-accordion-variant4-color': '#33475b',
34
+ '--hscl-accordion-variant4-icon-fill': '#33475b',
35
35
  };
36
36
 
37
37
  export const withAccordionStyles: Decorator = Story => (
@@ -2,7 +2,7 @@ import { TextField, LinkField } from '@hubspot/cms-components/fields';
2
2
  import Icon from '../Icon/index.js';
3
3
  import { ContentFieldsProps } from './types.js';
4
4
 
5
- export default function ContentFields({
5
+ const ContentFields = ({
6
6
  buttonTextLabel = 'Button text',
7
7
  buttonTextName = 'buttonText',
8
8
  buttonTextDefault = 'Click me',
@@ -22,7 +22,7 @@ export default function ContentFields({
22
22
  showIconLabel = 'Show button icon',
23
23
  showIconName,
24
24
  showIconDefault,
25
- }: ContentFieldsProps) {
25
+ }: ContentFieldsProps) => {
26
26
  const iconContentFieldsProps = {
27
27
  iconLabel,
28
28
  iconName,
@@ -49,4 +49,6 @@ export default function ContentFields({
49
49
  <Icon.ContentFields addIconToggle={true} {...iconContentFieldsProps} />
50
50
  </>
51
51
  );
52
- }
52
+ };
53
+
54
+ export default ContentFields;
@@ -1,11 +1,11 @@
1
1
  import { ChoiceField } from '@hubspot/cms-components/fields';
2
2
  import { StyleFieldsProps } from './types.js';
3
3
 
4
- export default function StyleFields({
4
+ const StyleFields = ({
5
5
  buttonVariantLabel = 'Button variant',
6
6
  buttonVariantName = 'buttonVariant',
7
7
  buttonVariantDefault = 'primary',
8
- }: StyleFieldsProps) {
8
+ }: StyleFieldsProps) => {
9
9
  return (
10
10
  <>
11
11
  <ChoiceField
@@ -20,4 +20,6 @@ export default function StyleFields({
20
20
  />
21
21
  </>
22
22
  );
23
- }
23
+ };
24
+
25
+ export default StyleFields;
@@ -17,21 +17,32 @@
17
17
  transition: all 0.2s ease;
18
18
  text-decoration: none;
19
19
 
20
+ @media (prefers-reduced-motion: reduce) {
21
+ transition: none;
22
+ }
23
+
20
24
  &:hover {
21
- background-color: var(--hscl-button-backgroundColor-hover);
22
- color: var(--hscl-button-color-hover);
23
- border-width: var(--hscl-button-borderWidth-hover);
24
- border-color: var(--hscl-button-borderColor-hover);
25
+ background-color: var(--hscl-button-backgroundColor-hover, var(--hscl-button-backgroundColor));
26
+ color: var(--hscl-button-color-hover, var(--hscl-button-color));
27
+ border-width: var(--hscl-button-borderWidth-hover, var(--hscl-button-borderWidth));
28
+ border-color: var(--hscl-button-borderColor-hover, var(--hscl-button-borderColor));
25
29
  text-decoration: none;
26
30
  }
27
31
 
28
- &:focus {
29
- background-color: var(--hscl-button-backgroundColor-focus);
30
- color: var(--hscl-button-color-focus);
31
- border-width: var(--hscl-button-borderWidth-focus);
32
- border-color: var(--hscl-button-borderColor-focus);
33
- outline: var(--hscl-button-outlineWidth-focus) solid var(--hscl-button-outlineColor-focus);
34
- outline-offset: var(--hscl-button-outlineOffset-focus);
32
+ &:focus-visible {
33
+ background-color: var(--hscl-button-backgroundColor-focus, var(--hscl-button-backgroundColor));
34
+ color: var(--hscl-button-color-focus, var(--hscl-button-color));
35
+ border-width: var(--hscl-button-borderWidth-focus, var(--hscl-button-borderWidth));
36
+ border-color: var(--hscl-button-borderColor-focus, var(--hscl-button-borderColor));
37
+ outline: var(--hscl-button-outlineWidth-focus, 2px) solid var(--hscl-button-outlineColor-focus, currentColor);
38
+ outline-offset: var(--hscl-button-outlineOffset-focus, 2px);
39
+ }
40
+
41
+ &:active {
42
+ background-color: var(--hscl-button-backgroundColor-active, var(--hscl-button-backgroundColor));
43
+ color: var(--hscl-button-color-active, var(--hscl-button-color));
44
+ border-width: var(--hscl-button-borderWidth-active, var(--hscl-button-borderWidth));
45
+ border-color: var(--hscl-button-borderColor-active, var(--hscl-button-borderColor));
35
46
  }
36
47
 
37
48
  &:disabled {
@@ -39,7 +50,6 @@
39
50
  cursor: not-allowed;
40
51
  }
41
52
 
42
-
43
53
  svg {
44
54
  display: inline-flex;
45
55
  align-items: center;
@@ -47,5 +57,3 @@
47
57
  fill: var(--hscl-button-icon-fill);
48
58
  }
49
59
  }
50
-
51
-
@@ -56,7 +56,7 @@ const ButtonComponent = ({
56
56
  );
57
57
  };
58
58
 
59
- function renderButton({
59
+ const renderButton = ({
60
60
  onClick,
61
61
  disabled = false,
62
62
  children,
@@ -67,7 +67,7 @@ const ButtonComponent = ({
67
67
  iconPurpose,
68
68
  iconTitle,
69
69
  ...rest
70
- }: Omit<ButtonAsButtonProps, 'buttonType'> & Partial<BaseButtonProps>) {
70
+ }: Omit<ButtonAsButtonProps, 'buttonType'> & Partial<BaseButtonProps>) => {
71
71
  const iconProps = {
72
72
  iconFieldPath,
73
73
  iconSize,
@@ -81,9 +81,9 @@ const ButtonComponent = ({
81
81
  <RenderWithIcon {...iconProps}>{children}</RenderWithIcon>
82
82
  </button>
83
83
  );
84
- }
84
+ };
85
85
 
86
- function renderLink({
86
+ const renderLink = ({
87
87
  href = '',
88
88
  target = '_self',
89
89
  rel = '',
@@ -95,7 +95,7 @@ const ButtonComponent = ({
95
95
  iconPurpose,
96
96
  iconTitle,
97
97
  ...rest
98
- }: Omit<ButtonAsLinkProps, 'buttonType'> & Partial<BaseButtonProps>) {
98
+ }: Omit<ButtonAsLinkProps, 'buttonType'> & Partial<BaseButtonProps>) => {
99
99
  const iconProps = {
100
100
  iconFieldPath,
101
101
  iconSize,
@@ -109,7 +109,7 @@ const ButtonComponent = ({
109
109
  <RenderWithIcon {...iconProps}>{children}</RenderWithIcon>
110
110
  </a>
111
111
  );
112
- }
112
+ };
113
113
 
114
114
  // TypeScript properly discriminates based on buttonType
115
115
  if (buttonType === 'button') return renderButton(rest);
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import Button from '../index.js';
3
3
  import { ButtonAsButtonProps } from '../types.js';
4
4
  import { withButtonStyles } from './ButtonDecorator.js';
5
- import { SBContainer } from '@sb-utils/SBContainer.js';
5
+ import { SBContainer, SBFocusWrapper } from '@sb-utils';
6
6
 
7
7
  const meta: Meta<ButtonAsButtonProps> = {
8
8
  title: 'Component Library/Button/As Button',
@@ -64,3 +64,32 @@ export const DisabledStates: Story = {
64
64
  </SBContainer>
65
65
  ),
66
66
  };
67
+
68
+ export const InteractionStates: Story = {
69
+ name: 'Interaction States',
70
+ render: () => (
71
+ <SBContainer flex direction="column" gap="large">
72
+ <SBContainer addBackground>
73
+ <h4>Hover State</h4>
74
+ <p>Hover over the button to see the hover effect</p>
75
+ <Button buttonType="button" variant="primary">
76
+ Hover me
77
+ </Button>
78
+ </SBContainer>
79
+
80
+ <SBContainer addBackground>
81
+ <h4>Focus State</h4>
82
+ <p>
83
+ In production, this focus outline appears when navigating with the
84
+ keyboard (Tab key). This Storybook demo auto-applies the focus state
85
+ to show how it looks.
86
+ </p>
87
+ <SBFocusWrapper>
88
+ <Button buttonType="button" variant="primary">
89
+ Focused button
90
+ </Button>
91
+ </SBFocusWrapper>
92
+ </SBContainer>
93
+ </SBContainer>
94
+ ),
95
+ };
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import Button from '../index.js';
3
3
  import { ButtonAsLinkProps } from '../types.js';
4
4
  import { withButtonStyles } from './ButtonDecorator.js';
5
- import { SBContainer } from '@sb-utils/SBContainer.js';
5
+ import { SBContainer, SBFocusWrapper } from '@sb-utils';
6
6
 
7
7
  const meta: Meta<ButtonAsLinkProps> = {
8
8
  title: 'Component Library/Button/As Link',
@@ -68,3 +68,40 @@ export const LinkTargets: Story = {
68
68
  </SBContainer>
69
69
  ),
70
70
  };
71
+
72
+ export const InteractionStates: Story = {
73
+ name: 'Interaction States',
74
+ render: () => (
75
+ <SBContainer flex direction="column" gap="large">
76
+ <SBContainer addBackground>
77
+ <h4>Hover State</h4>
78
+ <p>Hover over the button to see the hover effect</p>
79
+ <Button
80
+ buttonType="link"
81
+ href="https://www.hubspot.com"
82
+ variant="primary"
83
+ >
84
+ Hover me
85
+ </Button>
86
+ </SBContainer>
87
+
88
+ <SBContainer addBackground>
89
+ <h4>Focus State</h4>
90
+ <p>
91
+ In production, this focus outline appears when navigating with the
92
+ keyboard (Tab key). This Storybook demo auto-applies the focus state
93
+ to show how it looks.
94
+ </p>
95
+ <SBFocusWrapper>
96
+ <Button
97
+ buttonType="link"
98
+ href="https://www.hubspot.com"
99
+ variant="primary"
100
+ >
101
+ Focused button
102
+ </Button>
103
+ </SBFocusWrapper>
104
+ </SBContainer>
105
+ </SBContainer>
106
+ ),
107
+ };
@@ -1,5 +1,5 @@
1
1
  import type { Decorator } from '@storybook/react';
2
- import styles from './ButtonDecorator.module.css';
2
+ import styles from './ButtonDecorator.module.scss';
3
3
  /**
4
4
  * Shared decorator for Button stories that applies default CSS variables
5
5
  */
@@ -1,14 +1,14 @@
1
1
  import { ChoiceField, NumberField } from '@hubspot/cms-components/fields';
2
2
  import { StyleFieldsProps } from './types.js';
3
3
 
4
- export default function StyleFields({
4
+ const StyleFields = ({
5
5
  variantLabel = 'Card Variant',
6
6
  variantName = 'variant',
7
7
  variantDefault = 'elevated',
8
8
  borderRadiusLabel = 'Border Radius',
9
9
  borderRadiusName = 'borderRadius',
10
10
  borderRadiusDefault = 8,
11
- }: StyleFieldsProps) {
11
+ }: StyleFieldsProps) => {
12
12
  return (
13
13
  <>
14
14
  <ChoiceField
@@ -31,4 +31,6 @@ export default function StyleFields({
31
31
  />
32
32
  </>
33
33
  );
34
- }
34
+ };
35
+
36
+ export default StyleFields;
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import Card from '../index.js';
3
3
  import { CardProps } from '../types.js';
4
4
  import { withCardStyles } from './CardDecorator.js';
5
- import { SBContainer } from '@sb-utils/SBContainer.js';
5
+ import { SBContainer, SBFocusWrapper } from '@sb-utils';
6
6
 
7
7
  const CardDivider = () => (
8
8
  <hr
@@ -212,3 +212,48 @@ export const EdgeCases: Story = {
212
212
  </SBContainer>
213
213
  ),
214
214
  };
215
+
216
+ export const InteractionStates: Story = {
217
+ name: 'Interaction States (Wrapped in Link)',
218
+ render: () => (
219
+ <SBContainer flex direction="column" gap="large">
220
+ <SBContainer addBackground>
221
+ <h4>Clickable Card</h4>
222
+ <p>
223
+ Card wrapped in a link becomes clickable and shows hover/focus states
224
+ </p>
225
+ <a href="https://www.hubspot.com" style={{ textDecoration: 'none' }}>
226
+ <Card borderRadius={8}>
227
+ <CardHeading>Clickable Feature Card</CardHeading>
228
+ <CardDivider />
229
+ <CardContent>
230
+ This entire card is wrapped in a link. Click anywhere on the card
231
+ to navigate.
232
+ </CardContent>
233
+ </Card>
234
+ </a>
235
+ </SBContainer>
236
+
237
+ <SBContainer addBackground>
238
+ <h4>Focus State</h4>
239
+ <p>
240
+ In production, this focus outline appears when navigating with the
241
+ keyboard (Tab key). This Storybook demo auto-applies the focus state
242
+ to show how it looks.
243
+ </p>
244
+ <SBFocusWrapper>
245
+ <a href="https://www.hubspot.com" style={{ textDecoration: 'none' }}>
246
+ <Card borderRadius={8}>
247
+ <CardHeading>Focused Card</CardHeading>
248
+ <CardDivider />
249
+ <CardContent>
250
+ This card shows the focus outline when navigating with the
251
+ keyboard.
252
+ </CardContent>
253
+ </Card>
254
+ </a>
255
+ </SBFocusWrapper>
256
+ </SBContainer>
257
+ </SBContainer>
258
+ ),
259
+ };
@@ -1,5 +1,5 @@
1
1
  import type { Decorator } from '@storybook/react';
2
- import styles from './CardDecorator.module.css';
2
+ import styles from './CardDecorator.module.scss';
3
3
 
4
4
  export const withCardStyles: Decorator = Story => (
5
5
  <div className={styles.cardContainer}>
@@ -1,7 +1,7 @@
1
1
  import { ChoiceField, NumberField } from '@hubspot/cms-components/fields';
2
2
  import { ContentFieldsProps } from './types.js';
3
3
 
4
- export default function ContentFields({
4
+ const ContentFields = ({
5
5
  orientationLabel = 'Orientation',
6
6
  orientationName = 'orientation',
7
7
  orientationDefault = 'horizontal',
@@ -14,7 +14,7 @@ export default function ContentFields({
14
14
  thicknessLabel = 'Thickness',
15
15
  thicknessName = 'thickness',
16
16
  thicknessDefault = 1,
17
- }: ContentFieldsProps) {
17
+ }: ContentFieldsProps) => {
18
18
  return (
19
19
  <>
20
20
  <ChoiceField
@@ -58,4 +58,6 @@ export default function ContentFields({
58
58
  />
59
59
  </>
60
60
  );
61
- }
61
+ };
62
+
63
+ export default ContentFields;
@@ -1,11 +1,11 @@
1
1
  import { ChoiceField } from '@hubspot/cms-components/fields';
2
2
  import { StyleFieldsProps } from './types.js';
3
3
 
4
- export default function StyleFields({
4
+ const StyleFields = ({
5
5
  borderStyleLabel = 'Border style',
6
6
  borderStyleName = 'borderStyle',
7
7
  borderStyleDefault = 'solid',
8
- }: StyleFieldsProps) {
8
+ }: StyleFieldsProps) => {
9
9
  return (
10
10
  <>
11
11
  <ChoiceField
@@ -22,4 +22,6 @@ export default function StyleFields({
22
22
  />
23
23
  </>
24
24
  );
25
- }
25
+ };
26
+
27
+ export default StyleFields;