@cerebruminc/cerebellum 17.3.2 → 17.3.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # react-component-lib-boilerplate
2
2
 
3
+ ## [17.3.3](https://github.com/cerebruminc/cerebellum/compare/v17.3.2...v17.3.3) (2026-06-17)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **button:** add secondaryText color property and hover text transitions ([22f165d](https://github.com/cerebruminc/cerebellum/commit/22f165d001263d93dccf583036c9ff4f07b37c83))
9
+ * **button:** sync Mantine theme button colors with accessible values ([b18f1eb](https://github.com/cerebruminc/cerebellum/commit/b18f1eb12f18eba7011d7b14c12fd110afe0083f))
10
+ * **button:** update color families for WCAG AA compliance ([75f6b2f](https://github.com/cerebruminc/cerebellum/commit/75f6b2fc044c50c536710339ae9472780f5924d8))
11
+ * **toggle-button:** update test to work with new colors ([e60fe71](https://github.com/cerebruminc/cerebellum/commit/e60fe71f0ce2ba94e2937c2200b38eb13a81523d))
12
+
3
13
  ## [17.3.2](https://github.com/cerebruminc/cerebellum/compare/v17.3.1...v17.3.2) (2026-06-15)
4
14
 
5
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerebruminc/cerebellum",
3
- "version": "17.3.2",
3
+ "version": "17.3.3",
4
4
  "description": "Cerebrum's React Component Library",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -89,7 +89,13 @@ export const Button: FC<ButtonType> = (props: ButtonType) => {
89
89
  const arrowGap = directionalButtonGap || theme.button?.directionalButtonGap;
90
90
  const confirmedCaretGap = caretGap || theme.button?.caretGap;
91
91
  const confirmedIconGap = iconGap ?? (boxed || textButton ? 10 : boxedOutline ? 13 : Icon ? 20 : arrowGap || 70);
92
- const iconColor = disabled ? disabledTextColor || theme.button?.disabledTextColor : primary && !white ? colorGroup.text : colorGroup.main;
92
+ const iconColor = disabled
93
+ ? disabledTextColor || theme.button?.disabledTextColor
94
+ : primary && !white
95
+ ? colorGroup.text
96
+ : secondary || boxed
97
+ ? colorGroup.secondaryText || colorGroup.main
98
+ : colorGroup.main;
93
99
  const loadingStyleFormat =
94
100
  loadingConfiguration || getLoadingConfiguration({ primary, outline, boxedOutline, secondary, textButton, boxed, colorGroup });
95
101
  const loadingOffsetParams = { iconSize, autoIconSize, iconGap, confirmedIconGap, Icon, backButton, caret, nextButton };
@@ -182,7 +188,9 @@ export const Button: FC<ButtonType> = (props: ButtonType) => {
182
188
  $disabled={disabled}
183
189
  $disabledTextColor={disabledTextColor}
184
190
  $letterSpacing={letterSpacing}
191
+ $outline={outline}
185
192
  $primary={!(!primary || white)}
193
+ $secondary={secondary}
186
194
  $textButton={textButton}
187
195
  $textFontSize={textFontSize}
188
196
  data-sentry-unmask
@@ -1,5 +1,5 @@
1
1
  import { colors } from "../../const/colors";
2
- import { styled } from "styled-components";
2
+ import { styled, type DefaultTheme } from "styled-components";
3
3
 
4
4
  import {
5
5
  ButtonElementProps,
@@ -11,6 +11,64 @@ import {
11
11
  UnderlineProps,
12
12
  } from "./types";
13
13
 
14
+ type BaseTextColorProps = Pick<TextProps, "$primary" | "$secondary" | "$boxed" | "$colorGroup" | "$disabled" | "$disabledTextColor"> & {
15
+ theme: DefaultTheme;
16
+ };
17
+
18
+ type HoverTextColorProps = Pick<
19
+ TextProps,
20
+ "$primary" | "$secondary" | "$boxed" | "$outline" | "$boxedOutline" | "$textButton" | "$colorGroup" | "$disabled" | "$disabledTextColor"
21
+ > & {
22
+ theme: DefaultTheme;
23
+ };
24
+
25
+ const getBaseTextColor = ({ $primary, $secondary, $boxed, $colorGroup, $disabled, $disabledTextColor, theme }: BaseTextColorProps) => {
26
+ if ($disabled) {
27
+ return $disabledTextColor || theme.button?.disabledTextColor;
28
+ }
29
+
30
+ if ($primary) {
31
+ return $colorGroup.text;
32
+ }
33
+
34
+ if ($secondary || $boxed) {
35
+ return $colorGroup.secondaryText || $colorGroup.main;
36
+ }
37
+
38
+ return $colorGroup.main;
39
+ };
40
+
41
+ const getHoverTextColor = ({
42
+ $primary,
43
+ $secondary,
44
+ $boxed,
45
+ $outline,
46
+ $boxedOutline,
47
+ $textButton,
48
+ $colorGroup,
49
+ $disabled,
50
+ $disabledTextColor,
51
+ theme,
52
+ }: HoverTextColorProps) => {
53
+ if ($disabled) {
54
+ return $disabledTextColor || theme.button?.disabledTextColor;
55
+ }
56
+
57
+ if ($primary) {
58
+ return $colorGroup.text;
59
+ }
60
+
61
+ if ($outline || $boxedOutline) {
62
+ return $colorGroup.hover;
63
+ }
64
+
65
+ if ($textButton || $secondary || $boxed) {
66
+ return $colorGroup.secondaryText || $colorGroup.main;
67
+ }
68
+
69
+ return $colorGroup.main;
70
+ };
71
+
14
72
  export const ButtonElement = styled.button<ButtonElementProps>`
15
73
  align-items: center;
16
74
  line-height: ${({ $lineHeight }) => ($lineHeight ? $lineHeight : 1.15)};
@@ -40,7 +98,11 @@ export const ButtonElement = styled.button<ButtonElementProps>`
40
98
  }};
41
99
  border: 1px solid
42
100
  ${({ $boxedOutline, disabled, $disabledTextColor, $outline, $colorGroup, $textButton, theme }) =>
43
- $boxedOutline || $outline ? (disabled ? $disabledTextColor || theme.button?.disabledTextColor : $colorGroup.main) : $textButton ? "transparent" : theme.button?.outlineColor || "transparent"};
101
+ $boxedOutline || $outline
102
+ ? (disabled ? $disabledTextColor || theme.button?.disabledTextColor : $colorGroup.main)
103
+ : $textButton
104
+ ? "transparent"
105
+ : theme.button?.outlineColor || "transparent"};
44
106
  border-radius: ${({ $borderRadius, $boxed, $boxedOutline, $textButton }) =>
45
107
  $borderRadius ?? ($boxedOutline ? 7 : $boxed || $textButton ? 8 : 30)}px;
46
108
  box-shadow: ${({ $colorGroup, disabled, $primary, $shadow }) =>
@@ -88,6 +150,8 @@ export const ButtonElement = styled.button<ButtonElementProps>`
88
150
  ? $colorGroup.medium
89
151
  : $colorGroup.hover;
90
152
  }};
153
+ ${({ $boxedOutline, $outline, $colorGroup, disabled }) =>
154
+ ($boxedOutline || $outline) && !disabled ? `border-color: ${$colorGroup.hover};` : ""}
91
155
  }
92
156
  &:after {
93
157
  content: "";
@@ -142,15 +206,18 @@ export const NextIconBox = styled(IconBox)`
142
206
  margin-right: 0;
143
207
  `;
144
208
  export const Text = styled.span<TextProps>`
145
- color: ${({ $primary, $colorGroup, $disabled, $disabledTextColor, theme }) =>
146
- $disabled ? $disabledTextColor || theme.button?.disabledTextColor : $primary ? $colorGroup.text : $colorGroup.main};
209
+ color: ${getBaseTextColor};
147
210
  font-size: ${(props) => props.$textFontSize || props.theme.button?.textFontSize}px;
148
211
  font-weight: ${({ $boxed, $boxedOutline, $textButton }) => ($boxed || $boxedOutline || $textButton ? 500 : 600)};
149
- letter-spacing: ${({ $boxed, $boxedOutline, $letterSpacing, $textButton: textButton }) => ($letterSpacing ? $letterSpacing : ($boxed || $boxedOutline || textButton ? "0.36px" : "0.07em"))};
212
+ letter-spacing: ${({ $boxed, $boxedOutline, $letterSpacing, $textButton: textButton }) => ($letterSpacing ? $letterSpacing : $boxed || $boxedOutline || textButton ? "0.36px" : "0.07em")};
150
213
  position: relative;
214
+ transition: color 150ms ease-out;
151
215
  vertical-align: middle;
152
216
  white-space: nowrap;
153
217
  z-index: 1;
218
+ ${ButtonElement}:hover & {
219
+ color: ${getHoverTextColor};
220
+ }
154
221
  `;
155
222
  export const TextButtonBackground = styled.span<TextBackgroundProps>`
156
223
  bottom: 0;
@@ -69,7 +69,7 @@ export const getLoadingConfiguration = ({
69
69
  if (primary) {
70
70
  return PrimaryButtonLoadingConfiguration(colorGroup?.text);
71
71
  } else if (secondary) {
72
- return SecondaryButtonLoadingConfiguration(colorGroup?.main);
72
+ return SecondaryButtonLoadingConfiguration(colorGroup?.secondaryText || colorGroup?.main);
73
73
  } else if (outline) {
74
74
  return OutlineButtonLoadingConfiguration(colorGroup?.main);
75
75
  } else if (boxedOutline) {
@@ -77,7 +77,7 @@ export const getLoadingConfiguration = ({
77
77
  } else if (textButton) {
78
78
  return TextButtonLoadingConfiguration(colorGroup?.main);
79
79
  } else if (boxed) {
80
- return BoxedButtonLoadingConfiguration(colorGroup?.main);
80
+ return BoxedButtonLoadingConfiguration(colorGroup?.secondaryText || colorGroup?.main);
81
81
  } else {
82
82
  return {};
83
83
  }
@@ -164,7 +164,9 @@ export interface TextProps {
164
164
  $disabled?: boolean;
165
165
  $disabledTextColor?: string;
166
166
  $letterSpacing?: string;
167
+ $outline?: boolean;
167
168
  $primary: boolean;
169
+ $secondary?: boolean;
168
170
  $textButton?: boolean;
169
171
  $textFontSize?: number;
170
172
  }
@@ -139,7 +139,7 @@ describe("ToggleButtons", () => {
139
139
  const iconBox = screen.getByTestId(`${buttonText} icon`);
140
140
  const path = iconBox.querySelector("path");
141
141
  // Default: active icon fill = colorGroup.hover = BLUE_100
142
- expect(path?.getAttribute("fill")).toBe("#4F6CEA");
142
+ expect(path?.getAttribute("fill")).toBe("#4965c8");
143
143
  });
144
144
 
145
145
  test("active icon uses colorGroup.light fill when invertActive is true", () => {
@@ -82,13 +82,13 @@ const blue: MantineColorsTuple = [
82
82
  "#E5EBFC", // BLUE_5 - 0
83
83
  "#D7E1FF", // BLUE_10 - 1
84
84
  "#ADBDFE", // BLUE_25 - 2
85
- "#829EFD", // BLUE_40 - 3
86
- "#7290F7", // BLUE_50 - 4
87
- "#6b8af5", // BLUE_65 - 5
88
- "#5d7cf0", // BLUE_80 - 6
89
- "#4F6CEA", // BLUE_100 - 7
90
- "#4d6be1", // BLUE_110 - 8
91
- "#4965c8", // BLUE_120 - 9
85
+ "#7A97FA", // BLUE_45 - 3
86
+ "#6988f4", // BLUE_70 - 4
87
+ "#5d7cf0", // BLUE_80 - 5
88
+ "#4F6CEA", // BLUE_100 - 6 (main)
89
+ "#4965c8", // BLUE_120 - 7 (hover)
90
+ "#4158aa", // BLUE_130 - 8 (secondaryText)
91
+ "#394B91", // BLUE_150 - 9
92
92
  ];
93
93
 
94
94
  // High-contrast binary palette — mirrors styled-components highContrastColorFamily (BLACK/WHITE only)
@@ -288,15 +288,15 @@ export const createCustomMantineTheme = (optionsOrBrandColor?: CreateCustomManti
288
288
  ? highContrastRed
289
289
  : [
290
290
  "#FFDDE5", // RED_5 - 0
291
- "#FFCCD4", // RED_15 - 1
291
+ "#FFD5DD", // RED_10 - 1
292
292
  "#FFBBC4", // RED_25 - 2
293
- "#FFAAB4", // RED_35 - 3
294
- "#FF8F9C", // RED_50 - 4
295
- "#FF7285", // RED_65 - 5
296
- "#FF506F", // RED_80 - 6
297
- "#FF3161", // RED_100 - 7
298
- "#EB305A", // RED_110 - 8
299
- "#D72F53", // RED_120 - 9
293
+ "#FF8F9C", // RED_50 - 3
294
+ "#FF677E", // RED_70 - 4
295
+ "#FF3161", // RED_100 - 5
296
+ "#D72F53", // RED_120 - 6 (main)
297
+ "#C42D4C", // RED_130 - 7 (hover)
298
+ "#B02B45", // RED_140 - 8 (secondaryText)
299
+ "#9E293E", // RED_150 - 9
300
300
  ],
301
301
  peach: highContrast
302
302
  ? highContrastGray
@@ -18,6 +18,7 @@ export interface ColorFamilyType {
18
18
  hover: string;
19
19
  text: string;
20
20
  outline?: string;
21
+ secondaryText?: string;
21
22
  }
22
23
  export interface ConnotationColorType {
23
24
  light: string;
package/src/theme.ts CHANGED
@@ -775,9 +775,10 @@ export const cerebellumTheme: DefaultTheme = {
775
775
  medium: colors.BLUE_10,
776
776
  mediumDark: colors.BLUE_25,
777
777
  dark: colors.BLUE_45,
778
- main: colors.BLUE_70,
779
- hover: colors.BLUE_100,
778
+ main: colors.BLUE_100,
779
+ hover: colors.BLUE_120,
780
780
  text: colors.WHITE,
781
+ secondaryText: colors.BLUE_130,
781
782
  },
782
783
  COOL_GREY: {
783
784
  light: colors.COOL_GREY_4,
@@ -811,9 +812,10 @@ export const cerebellumTheme: DefaultTheme = {
811
812
  medium: colors.RED_10,
812
813
  mediumDark: colors.RED_25,
813
814
  dark: colors.RED_65,
814
- main: colors.RED_100,
815
- hover: colors.RED_100,
815
+ main: colors.RED_120,
816
+ hover: colors.RED_130,
816
817
  text: colors.WHITE,
818
+ secondaryText: colors.RED_140,
817
819
  },
818
820
  },
819
821
  colorPicker: {