@moderneinc/neo-styled-components 2.0.3 → 2.1.0-next.1c3b0f

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/dist/index.esm.js CHANGED
@@ -499,7 +499,7 @@ const NeoActivityIndicatorCell = ({ event = 'commit-job', secondaryEvent, scene
499
499
  NeoActivityIndicatorCell.displayName = 'NeoActivityIndicatorCell';
500
500
 
501
501
  /**
502
- * @license lucide-react v0.552.0 - ISC
502
+ * @license lucide-react v0.562.0 - ISC
503
503
  *
504
504
  * This source code is licensed under the ISC license.
505
505
  * See the LICENSE file in the root directory of this source tree.
@@ -526,7 +526,7 @@ const hasA11yProp = (props) => {
526
526
  };
527
527
 
528
528
  /**
529
- * @license lucide-react v0.552.0 - ISC
529
+ * @license lucide-react v0.562.0 - ISC
530
530
  *
531
531
  * This source code is licensed under the ISC license.
532
532
  * See the LICENSE file in the root directory of this source tree.
@@ -545,7 +545,7 @@ var defaultAttributes = {
545
545
  };
546
546
 
547
547
  /**
548
- * @license lucide-react v0.552.0 - ISC
548
+ * @license lucide-react v0.562.0 - ISC
549
549
  *
550
550
  * This source code is licensed under the ISC license.
551
551
  * See the LICENSE file in the root directory of this source tree.
@@ -583,7 +583,7 @@ const Icon = forwardRef(
583
583
  );
584
584
 
585
585
  /**
586
- * @license lucide-react v0.552.0 - ISC
586
+ * @license lucide-react v0.562.0 - ISC
587
587
  *
588
588
  * This source code is licensed under the ISC license.
589
589
  * See the LICENSE file in the root directory of this source tree.
@@ -608,7 +608,7 @@ const createLucideIcon = (iconName, iconNode) => {
608
608
  };
609
609
 
610
610
  /**
611
- * @license lucide-react v0.552.0 - ISC
611
+ * @license lucide-react v0.562.0 - ISC
612
612
  *
613
613
  * This source code is licensed under the ISC license.
614
614
  * See the LICENSE file in the root directory of this source tree.
@@ -622,7 +622,7 @@ const __iconNode$h = [
622
622
  const ArrowDown = createLucideIcon("arrow-down", __iconNode$h);
623
623
 
624
624
  /**
625
- * @license lucide-react v0.552.0 - ISC
625
+ * @license lucide-react v0.562.0 - ISC
626
626
  *
627
627
  * This source code is licensed under the ISC license.
628
628
  * See the LICENSE file in the root directory of this source tree.
@@ -636,7 +636,7 @@ const __iconNode$g = [
636
636
  const ArrowUp = createLucideIcon("arrow-up", __iconNode$g);
637
637
 
638
638
  /**
639
- * @license lucide-react v0.552.0 - ISC
639
+ * @license lucide-react v0.562.0 - ISC
640
640
  *
641
641
  * This source code is licensed under the ISC license.
642
642
  * See the LICENSE file in the root directory of this source tree.
@@ -658,7 +658,7 @@ const __iconNode$f = [
658
658
  const CalendarDays = createLucideIcon("calendar-days", __iconNode$f);
659
659
 
660
660
  /**
661
- * @license lucide-react v0.552.0 - ISC
661
+ * @license lucide-react v0.562.0 - ISC
662
662
  *
663
663
  * This source code is licensed under the ISC license.
664
664
  * See the LICENSE file in the root directory of this source tree.
@@ -669,7 +669,7 @@ const __iconNode$e = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
669
669
  const ChevronDown = createLucideIcon("chevron-down", __iconNode$e);
670
670
 
671
671
  /**
672
- * @license lucide-react v0.552.0 - ISC
672
+ * @license lucide-react v0.562.0 - ISC
673
673
  *
674
674
  * This source code is licensed under the ISC license.
675
675
  * See the LICENSE file in the root directory of this source tree.
@@ -680,7 +680,7 @@ const __iconNode$d = [["path", { d: "m15 18-6-6 6-6", key: "1wnfg3" }]];
680
680
  const ChevronLeft = createLucideIcon("chevron-left", __iconNode$d);
681
681
 
682
682
  /**
683
- * @license lucide-react v0.552.0 - ISC
683
+ * @license lucide-react v0.562.0 - ISC
684
684
  *
685
685
  * This source code is licensed under the ISC license.
686
686
  * See the LICENSE file in the root directory of this source tree.
@@ -691,7 +691,7 @@ const __iconNode$c = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
691
691
  const ChevronRight = createLucideIcon("chevron-right", __iconNode$c);
692
692
 
693
693
  /**
694
- * @license lucide-react v0.552.0 - ISC
694
+ * @license lucide-react v0.562.0 - ISC
695
695
  *
696
696
  * This source code is licensed under the ISC license.
697
697
  * See the LICENSE file in the root directory of this source tree.
@@ -702,7 +702,7 @@ const __iconNode$b = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
702
702
  const ChevronUp = createLucideIcon("chevron-up", __iconNode$b);
703
703
 
704
704
  /**
705
- * @license lucide-react v0.552.0 - ISC
705
+ * @license lucide-react v0.562.0 - ISC
706
706
  *
707
707
  * This source code is licensed under the ISC license.
708
708
  * See the LICENSE file in the root directory of this source tree.
@@ -716,7 +716,7 @@ const __iconNode$a = [
716
716
  const ChevronsUpDown = createLucideIcon("chevrons-up-down", __iconNode$a);
717
717
 
718
718
  /**
719
- * @license lucide-react v0.552.0 - ISC
719
+ * @license lucide-react v0.562.0 - ISC
720
720
  *
721
721
  * This source code is licensed under the ISC license.
722
722
  * See the LICENSE file in the root directory of this source tree.
@@ -731,7 +731,7 @@ const __iconNode$9 = [
731
731
  const CircleAlert = createLucideIcon("circle-alert", __iconNode$9);
732
732
 
733
733
  /**
734
- * @license lucide-react v0.552.0 - ISC
734
+ * @license lucide-react v0.562.0 - ISC
735
735
  *
736
736
  * This source code is licensed under the ISC license.
737
737
  * See the LICENSE file in the root directory of this source tree.
@@ -745,7 +745,7 @@ const __iconNode$8 = [
745
745
  const CircleCheck = createLucideIcon("circle-check", __iconNode$8);
746
746
 
747
747
  /**
748
- * @license lucide-react v0.552.0 - ISC
748
+ * @license lucide-react v0.562.0 - ISC
749
749
  *
750
750
  * This source code is licensed under the ISC license.
751
751
  * See the LICENSE file in the root directory of this source tree.
@@ -760,7 +760,7 @@ const __iconNode$7 = [
760
760
  const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$7);
761
761
 
762
762
  /**
763
- * @license lucide-react v0.552.0 - ISC
763
+ * @license lucide-react v0.562.0 - ISC
764
764
  *
765
765
  * This source code is licensed under the ISC license.
766
766
  * See the LICENSE file in the root directory of this source tree.
@@ -775,7 +775,7 @@ const __iconNode$6 = [
775
775
  const Columns3 = createLucideIcon("columns-3", __iconNode$6);
776
776
 
777
777
  /**
778
- * @license lucide-react v0.552.0 - ISC
778
+ * @license lucide-react v0.562.0 - ISC
779
779
  *
780
780
  * This source code is licensed under the ISC license.
781
781
  * See the LICENSE file in the root directory of this source tree.
@@ -790,7 +790,7 @@ const __iconNode$5 = [
790
790
  const ListFilter = createLucideIcon("list-filter", __iconNode$5);
791
791
 
792
792
  /**
793
- * @license lucide-react v0.552.0 - ISC
793
+ * @license lucide-react v0.562.0 - ISC
794
794
  *
795
795
  * This source code is licensed under the ISC license.
796
796
  * See the LICENSE file in the root directory of this source tree.
@@ -804,7 +804,7 @@ const __iconNode$4 = [
804
804
  const Plus = createLucideIcon("plus", __iconNode$4);
805
805
 
806
806
  /**
807
- * @license lucide-react v0.552.0 - ISC
807
+ * @license lucide-react v0.562.0 - ISC
808
808
  *
809
809
  * This source code is licensed under the ISC license.
810
810
  * See the LICENSE file in the root directory of this source tree.
@@ -818,7 +818,7 @@ const __iconNode$3 = [
818
818
  const Search = createLucideIcon("search", __iconNode$3);
819
819
 
820
820
  /**
821
- * @license lucide-react v0.552.0 - ISC
821
+ * @license lucide-react v0.562.0 - ISC
822
822
  *
823
823
  * This source code is licensed under the ISC license.
824
824
  * See the LICENSE file in the root directory of this source tree.
@@ -835,7 +835,7 @@ const __iconNode$2 = [
835
835
  const Trash2 = createLucideIcon("trash-2", __iconNode$2);
836
836
 
837
837
  /**
838
- * @license lucide-react v0.552.0 - ISC
838
+ * @license lucide-react v0.562.0 - ISC
839
839
  *
840
840
  * This source code is licensed under the ISC license.
841
841
  * See the LICENSE file in the root directory of this source tree.
@@ -854,7 +854,7 @@ const __iconNode$1 = [
854
854
  const WifiOff = createLucideIcon("wifi-off", __iconNode$1);
855
855
 
856
856
  /**
857
- * @license lucide-react v0.552.0 - ISC
857
+ * @license lucide-react v0.562.0 - ISC
858
858
  *
859
859
  * This source code is licensed under the ISC license.
860
860
  * See the LICENSE file in the root directory of this source tree.
@@ -1034,8 +1034,8 @@ const StyledBreadcrumbLink = styled(Link, {
1034
1034
  * </NeoBreadcrumbs>
1035
1035
  * ```
1036
1036
  */
1037
- function NeoBreadcrumbLink({ current = false, children, ...props }) {
1038
- return (jsx(StyledBreadcrumbLink, { current: current, "aria-current": current ? 'page' : undefined, underline: "none", ...props, children: children }));
1037
+ function NeoBreadcrumbLink({ current = false, children, component, ...props }) {
1038
+ return (jsx(StyledBreadcrumbLink, { ...(component && { component }), current: current, "aria-current": current ? 'page' : undefined, underline: "none", ...props, children: children }));
1039
1039
  }
1040
1040
  /**
1041
1041
  * NeoBreadcrumbs - Navigation breadcrumb component
@@ -3982,11 +3982,11 @@ NeoInputField.displayName = 'NeoInputField';
3982
3982
  // Size configuration for consistent styling
3983
3983
  const sizeConfig = {
3984
3984
  small: {
3985
- height: 36,
3986
- paddingTop: spacing.spacing_1,
3987
- paddingRight: spacing.spacing_1_1_4,
3988
- paddingBottom: spacing.spacing_1,
3989
- paddingLeft: spacing.spacing_1_1_4,
3985
+ height: spacing.spacing_3_1_2,
3986
+ paddingTop: 1,
3987
+ paddingRight: spacing.spacing_3_4,
3988
+ paddingBottom: 1,
3989
+ paddingLeft: spacing.spacing_3_4,
3990
3990
  gap: spacing.spacing_1,
3991
3991
  iconSize: typography.fontSize.h4,
3992
3992
  fontSize: typography.fontSize.sm,
@@ -3994,10 +3994,10 @@ const sizeConfig = {
3994
3994
  },
3995
3995
  medium: {
3996
3996
  height: spacing.spacing_5,
3997
- paddingTop: spacing.spacing_1,
3998
- paddingRight: spacing.spacing_1_1_4,
3999
- paddingBottom: spacing.spacing_1,
4000
- paddingLeft: spacing.spacing_1,
3997
+ paddingTop: 1,
3998
+ paddingRight: spacing.spacing_3_4,
3999
+ paddingBottom: 1,
4000
+ paddingLeft: spacing.spacing_3_4,
4001
4001
  gap: spacing.spacing_1,
4002
4002
  iconSize: typography.fontSize.h4,
4003
4003
  fontSize: typography.fontSize.default,
@@ -4005,10 +4005,10 @@ const sizeConfig = {
4005
4005
  },
4006
4006
  large: {
4007
4007
  height: spacing.spacing_6,
4008
- paddingTop: spacing.spacing_1,
4009
- paddingRight: spacing.spacing_1_1_4,
4010
- paddingBottom: spacing.spacing_1,
4011
- paddingLeft: spacing.spacing_1_1_4,
4008
+ paddingTop: 1,
4009
+ paddingRight: spacing.spacing_3_4,
4010
+ paddingBottom: 1,
4011
+ paddingLeft: spacing.spacing_3_4,
4012
4012
  gap: spacing.spacing_1_1_2,
4013
4013
  iconSize: typography.fontSize.h2,
4014
4014
  fontSize: typography.fontSize.default,
@@ -4017,10 +4017,10 @@ const sizeConfig = {
4017
4017
  },
4018
4018
  xlarge: {
4019
4019
  height: spacing.spacing_8,
4020
- paddingTop: spacing.spacing_1,
4021
- paddingRight: spacing.spacing_1_1_4,
4022
- paddingBottom: spacing.spacing_1,
4023
- paddingLeft: spacing.spacing_1_1_4,
4020
+ paddingTop: 1,
4021
+ paddingRight: spacing.spacing_3_4,
4022
+ paddingBottom: 1,
4023
+ paddingLeft: spacing.spacing_3_4,
4024
4024
  gap: spacing.spacing_2,
4025
4025
  iconSize: typography.fontSize.h2,
4026
4026
  fontSize: typography.fontSize.default,
@@ -4029,16 +4029,16 @@ const sizeConfig = {
4029
4029
  },
4030
4030
  xxlarge: {
4031
4031
  height: spacing.spacing_10,
4032
- paddingTop: spacing.spacing_1,
4033
- paddingRight: spacing.spacing_1_1_2,
4034
- paddingBottom: spacing.spacing_1,
4035
- paddingLeft: spacing.spacing_1_1_2,
4032
+ paddingTop: 1,
4033
+ paddingRight: spacing.spacing_3_4,
4034
+ paddingBottom: 1,
4035
+ paddingLeft: spacing.spacing_3_4,
4036
4036
  gap: spacing.spacing_2,
4037
4037
  iconSize: spacing.spacing_5,
4038
4038
  fontSize: typography.fontSize.h5,
4039
4039
  secondaryFontSize: typography.fontSize.sm,
4040
4040
  badgeFontSize: typography.fontSize.xs,
4041
- lineHeight: 1,
4041
+ lineHeight: 1.5,
4042
4042
  },
4043
4043
  };
4044
4044
  const StyledListItemButton$1 = styled(ListItemButton, {
@@ -4047,12 +4047,14 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4047
4047
  prop !== 'secondaryText' &&
4048
4048
  prop !== 'badge' &&
4049
4049
  prop !== 'icon' &&
4050
- prop !== 'isLink',
4051
- })(({ theme, size = 'medium', isLink }) => {
4050
+ prop !== 'isLink' &&
4051
+ prop !== 'hasSecondaryText' &&
4052
+ prop !== 'trailingElement',
4053
+ })(({ theme, size = 'medium', isLink, hasSecondaryText }) => {
4052
4054
  const config = sizeConfig[size];
4053
4055
  return {
4054
4056
  minHeight: config.height,
4055
- height: config.height,
4057
+ height: hasSecondaryText ? 'auto' : config.height,
4056
4058
  paddingTop: config.paddingTop,
4057
4059
  paddingRight: config.paddingRight,
4058
4060
  paddingBottom: config.paddingBottom,
@@ -4060,6 +4062,8 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4060
4062
  borderRadius: spacing.spacing_3_4, // 6px
4061
4063
  backgroundColor: semanticColors.input.background,
4062
4064
  gap: config.gap,
4065
+ // Icon is always centered vertically per Figma design
4066
+ alignItems: 'center',
4063
4067
  transition: theme.transitions.create(['background-color'], {
4064
4068
  duration: theme.transitions.duration.short,
4065
4069
  }),
@@ -4074,17 +4078,32 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4074
4078
  }),
4075
4079
  },
4076
4080
  // Primary text styling
4081
+ // Font weight varies by size per Figma:
4082
+ // - Default: small/medium use 400, large/xlarge/xxlarge use 500
4083
+ // - Link: xxlarge uses 600, others use their default weight
4077
4084
  '& .list-item-primary': {
4078
4085
  fontSize: theme.typography.pxToRem(config.fontSize),
4079
4086
  lineHeight: config.lineHeight,
4080
- fontWeight: typography.fontWeight.medium,
4087
+ fontWeight: isLink && size === 'xxlarge'
4088
+ ? typography.fontWeight.semiBold
4089
+ : size === 'small' || size === 'medium'
4090
+ ? typography.fontWeight.regular
4091
+ : typography.fontWeight.medium,
4081
4092
  color: isLink
4082
4093
  ? semanticColors.typography.link.primary
4083
4094
  : semanticColors.typography.navigation.default,
4084
4095
  overflow: 'hidden',
4085
4096
  textOverflow: 'ellipsis',
4086
4097
  whiteSpace: 'nowrap',
4098
+ ...(isLink && size === 'xxlarge' && { textDecoration: 'none' }),
4087
4099
  },
4100
+ // Link hover styling (underline on hover for xxlarge)
4101
+ ...(isLink &&
4102
+ size === 'xxlarge' && {
4103
+ '&:hover .list-item-primary': {
4104
+ textDecoration: 'underline',
4105
+ },
4106
+ }),
4088
4107
  // Badge styling (for xxlarge link variant)
4089
4108
  '& .list-item-badge': {
4090
4109
  fontSize: theme.typography.pxToRem(size === 'xxlarge' ? config.badgeFontSize || 12 : 12),
@@ -4092,24 +4111,34 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4092
4111
  fontWeight: typography.fontWeight.regular,
4093
4112
  color: semanticColors.typography.bodySecondary,
4094
4113
  },
4095
- // Secondary text styling
4114
+ // Secondary text styling - wraps with word break
4096
4115
  '& .list-item-secondary': {
4097
4116
  fontSize: theme.typography.pxToRem('secondaryFontSize' in config ? config.secondaryFontSize : config.fontSize),
4098
4117
  lineHeight: size === 'large' ? 1.5 : 1.4,
4099
4118
  fontWeight: typography.fontWeight.regular,
4100
4119
  color: semanticColors.typography.bodySecondary,
4101
- overflow: 'hidden',
4102
- textOverflow: 'ellipsis',
4103
- whiteSpace: 'nowrap',
4120
+ wordBreak: 'break-word',
4121
+ whiteSpace: 'normal',
4104
4122
  ...(size === 'large' && {
4105
4123
  marginBottom: -2,
4106
4124
  }),
4107
4125
  },
4126
+ // Trailing element styling (e.g., chevron icon)
4127
+ '& .list-item-trailing': {
4128
+ display: 'flex',
4129
+ alignItems: 'center',
4130
+ flexShrink: 0,
4131
+ marginLeft: spacing.spacing_1,
4132
+ color: semanticColors.icons.default,
4133
+ },
4108
4134
  // Hover state
4109
4135
  '&:hover': {
4110
4136
  backgroundColor: semanticColors.input.background,
4111
4137
  },
4112
4138
  // Selected/Active state
4139
+ // Font weights per Figma:
4140
+ // - Primary: small/medium/xxlarge use 500, large/xlarge use 600
4141
+ // - Secondary: large/xlarge use 500, xxlarge uses 400
4113
4142
  [`&.${listItemButtonClasses.selected}`]: {
4114
4143
  backgroundColor: semanticColors.buttons.secondary.hoverBackground,
4115
4144
  '&:hover': {
@@ -4119,11 +4148,15 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4119
4148
  color: semanticColors.icons.pressed,
4120
4149
  },
4121
4150
  '& .list-item-primary': {
4122
- fontWeight: typography.fontWeight.semiBold,
4151
+ fontWeight: size === 'large' || size === 'xlarge'
4152
+ ? typography.fontWeight.semiBold
4153
+ : typography.fontWeight.medium,
4123
4154
  color: semanticColors.typography.navigation.default,
4124
4155
  },
4125
4156
  '& .list-item-secondary': {
4126
- fontWeight: typography.fontWeight.medium,
4157
+ fontWeight: size === 'large' || size === 'xlarge'
4158
+ ? typography.fontWeight.medium
4159
+ : typography.fontWeight.regular,
4127
4160
  },
4128
4161
  },
4129
4162
  // Disabled state
@@ -4142,17 +4175,10 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4142
4175
  '& .list-item-badge': {
4143
4176
  color: semanticColors.buttons.tertiary.disabled,
4144
4177
  },
4145
- },
4146
- // Link-specific styling (underline on hover for xxlarge)
4147
- ...(isLink &&
4148
- size === 'xxlarge' && {
4149
- '& .list-item-primary': {
4150
- textDecoration: 'none',
4151
- },
4152
- '&:hover .list-item-primary': {
4153
- textDecoration: 'underline',
4178
+ '& .list-item-trailing': {
4179
+ color: semanticColors.icons.disabled,
4154
4180
  },
4155
- }),
4181
+ },
4156
4182
  };
4157
4183
  });
4158
4184
  /**
@@ -4169,6 +4195,7 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4169
4195
  * - state (default|active|disabled|link) → selected, disabled, isLink props
4170
4196
  * - showIcon → icon prop (pass ReactNode)
4171
4197
  * - showSupportingText → secondaryText prop (pass ReactNode)
4198
+ * - showChevron → trailingElement prop (pass ReactNode, e.g., ChevronRight icon)
4172
4199
  * - Placeholder → primaryText prop
4173
4200
  * - Recipe name → secondaryText prop
4174
4201
  * - (# recipes) → badge prop
@@ -4185,17 +4212,17 @@ const StyledListItemButton$1 = styled(ListItemButton, {
4185
4212
  * - semanticColors.surface.input.disabled-background (disabled background)
4186
4213
  * - semanticColors.buttons.tertiary.disabled (disabled text color)
4187
4214
  */
4188
- const NeoListItem = ({ size = 'medium', primaryText, secondaryText, badge, icon, isLink = false, component, ...props }) => {
4215
+ const NeoListItem = ({ size = 'medium', primaryText, secondaryText, badge, icon, isLink = false, trailingElement, component, ...props }) => {
4189
4216
  // Determine if secondary text should be shown based on size
4190
4217
  const showSecondaryText = secondaryText && (size === 'large' || size === 'xlarge' || size === 'xxlarge');
4191
4218
  // Determine if badge should be shown (only for xxlarge)
4192
4219
  const showBadge = badge && size === 'xxlarge';
4193
4220
  // Prepare primary text with badge if needed
4194
4221
  const primaryContent = showBadge ? (jsxs(Fragment, { children: [primaryText, ' ', jsx(Typography, { component: "span", className: "list-item-badge", children: badge })] })) : (primaryText);
4195
- return (jsxs(StyledListItemButton$1, { size: size, isLink: isLink, ...(component && { component }), ...props, children: [icon && jsx(ListItemIcon, { children: icon }), jsx(ListItemText, { primary: primaryContent, secondary: showSecondaryText ? secondaryText : undefined, slotProps: {
4222
+ return (jsxs(StyledListItemButton$1, { size: size, isLink: isLink, hasSecondaryText: !!showSecondaryText, ...(component && { component }), ...props, children: [icon && jsx(ListItemIcon, { children: icon }), jsx(ListItemText, { primary: primaryContent, secondary: showSecondaryText ? secondaryText : undefined, sx: { minWidth: 0, flex: 1 }, slotProps: {
4196
4223
  primary: { className: 'list-item-primary' },
4197
4224
  secondary: { className: 'list-item-secondary' },
4198
- } })] }));
4225
+ } }), trailingElement && jsx(Box, { className: "list-item-trailing", children: trailingElement })] }));
4199
4226
  };
4200
4227
  NeoListItem.displayName = 'NeoListItem';
4201
4228
 
@@ -4311,6 +4338,15 @@ const NeoListItemButton = ({ children, selected = false, disabled = false, ...pr
4311
4338
  };
4312
4339
  NeoListItemButton.displayName = 'NeoListItemButton';
4313
4340
 
4341
+ /**
4342
+ * Focus ring box-shadow using Neo design tokens
4343
+ * Combines focusWhite1 (inner white ring) and focusBlue2 (outer blue ring)
4344
+ */
4345
+ const focusRingShadow = `${shadows.focusWhite1.x}px ${shadows.focusWhite1.y}px ${shadows.focusWhite1.blur}px ${shadows.focusWhite1.spread}px ${shadows.focusWhite1.shadow}, ${shadows.focusBlue2.x}px ${shadows.focusBlue2.y}px ${shadows.focusBlue2.blur}px ${shadows.focusBlue2.spread}px ${shadows.focusBlue2.shadow}`;
4346
+ /**
4347
+ * Card box-shadow using Neo design tokens for hover and selected states
4348
+ */
4349
+ const activeShadow = `${shadows.card.x}px ${shadows.card.y}px ${shadows.card.blur}px ${shadows.card.spread}px ${shadows.card.shadow}`;
4314
4350
  /**
4315
4351
  * Styled Card component with Neo design tokens
4316
4352
  */
@@ -4326,16 +4362,17 @@ const StyledCard = styled(MuiCard, {
4326
4362
  backgroundColor: semanticColors.surfaces.card,
4327
4363
  border: `1px solid ${selected ? semanticColors.border.tabActive : semanticColors.border.card}`,
4328
4364
  borderRadius: theme.typography.pxToRem(4),
4329
- boxShadow: 'none',
4330
- transition: theme.transitions.create(['border-color']),
4365
+ boxShadow: selected ? activeShadow : 'none',
4366
+ transition: theme.transitions.create(['border-color', 'background-color', 'box-shadow']),
4331
4367
  cursor: 'pointer',
4332
4368
  '&:hover': {
4333
- borderColor: selected ? semanticColors.border.tabActive : semanticColors.border.card,
4369
+ borderColor: semanticColors.border.tabActive,
4370
+ boxShadow: activeShadow,
4334
4371
  },
4335
4372
  '&:focus-visible': {
4336
- outline: `2px solid ${semanticColors.border.tabActive}`,
4337
- outlineOffset: '2px',
4338
4373
  borderColor: semanticColors.border.tabActive,
4374
+ boxShadow: focusRingShadow,
4375
+ outline: 'none',
4339
4376
  },
4340
4377
  ...(disabled && {
4341
4378
  opacity: 0.5,
@@ -4401,6 +4438,11 @@ const Description = styled('p')(({ theme }) => ({
4401
4438
  lineHeight: 1.5,
4402
4439
  color: colors.grey[800],
4403
4440
  width: '100%',
4441
+ display: '-webkit-box',
4442
+ WebkitLineClamp: 2,
4443
+ WebkitBoxOrient: 'vertical',
4444
+ overflow: 'hidden',
4445
+ textOverflow: 'ellipsis',
4404
4446
  }));
4405
4447
  /**
4406
4448
  * NeoMarketplaceCard - Selectable card component for marketplace items
@@ -4430,9 +4472,12 @@ const Description = styled('p')(({ theme }) => ({
4430
4472
  * - Property 1="Default" → Base state (no props)
4431
4473
  *
4432
4474
  * Design Tokens Used:
4433
- * - semanticColors.surfaces.card (#FFFFFF) - Card background
4475
+ * - semanticColors.surfaces.card (#FFFFFF) - Default card background
4476
+ * - semanticColors.status.info.light (#f2f3ff) - Active/focused card background
4434
4477
  * - semanticColors.border.card (#d1d5db) - Default border
4435
4478
  * - semanticColors.border.tabActive (#2f42ff) - Active/focused border
4479
+ * - shadows.focusWhite1 - Inner white focus ring (2px spread)
4480
+ * - shadows.focusBlue2 - Outer blue focus ring (4px spread)
4436
4481
  * - colors.grey[800] (#1F2937) - Text color
4437
4482
  * - typography.fontSize.xxs (10px) - Recipe count
4438
4483
  * - typography.fontSize.xs (12px) - Description
@@ -4769,7 +4814,7 @@ const Subtitle = styled(Typography)(() => ({
4769
4814
  * </NeoPageContent>
4770
4815
  * ```
4771
4816
  */
4772
- function NeoPageContent({ pageTitle, subtitle, action, sticky = false, contentMaxWidth = false, disableGutters = false, isLoading = false, showBackToTop = true, children, sx, ...boxProps }) {
4817
+ function NeoPageContent({ pageTitle, subtitle, breadcrumbs, action, sticky = false, contentMaxWidth = false, centerContent = true, disableGutters = false, isLoading = false, showBackToTop = true, children, sx, ...boxProps }) {
4773
4818
  const [showBackToTopButton, setShowBackToTopButton] = useState(false);
4774
4819
  const headerRef = useRef(null);
4775
4820
  const containerRef = useRef(null);
@@ -4799,8 +4844,8 @@ function NeoPageContent({ pageTitle, subtitle, action, sticky = false, contentMa
4799
4844
  // Also try to scroll window in case component is in a different context
4800
4845
  window.scrollTo({ top: 0, behavior: 'smooth' });
4801
4846
  };
4802
- const headerContent = (jsxs(Stack, { ref: headerRef, direction: "row", justifyContent: "space-between", alignItems: "flex-start", sx: { flexShrink: 0, gap: spacing.spacing_2 }, children: [jsxs(Stack, { sx: { gap: spacing.spacing_1, flexGrow: 1 }, children: [jsx(PageTitle, { children: pageTitle }), subtitle &&
4803
- (typeof subtitle === 'string' ? (jsx(Subtitle, { variant: "caption", children: subtitle })) : (subtitle))] }), action] }));
4847
+ const headerContent = (jsxs(Stack, { ref: headerRef, sx: { flexShrink: 0, gap: '0.5rem' }, children: [breadcrumbs && breadcrumbs.items.length > 0 && (jsx(NeoBreadcrumbs, { children: breadcrumbs.items.map((item, index) => (jsx(NeoBreadcrumbLink, { href: item.href, component: item.href && breadcrumbs.linkComponent ? breadcrumbs.linkComponent : undefined, current: index === breadcrumbs.items.length - 1, children: item.label }, item.href ?? index))) })), jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "flex-start", sx: { gap: '1rem' }, children: [jsxs(Stack, { sx: { gap: '0.5rem', flexGrow: 1 }, children: [jsx(PageTitle, { children: pageTitle }), subtitle &&
4848
+ (typeof subtitle === 'string' ? (jsx(Subtitle, { variant: "caption", children: subtitle })) : (subtitle))] }), action] })] }));
4804
4849
  const content = isLoading ? (jsx(Box, { sx: {
4805
4850
  display: 'flex',
4806
4851
  alignItems: 'center',
@@ -4824,15 +4869,27 @@ function NeoPageContent({ pageTitle, subtitle, action, sticky = false, contentMa
4824
4869
  pb: `${spacing.spacing_2_1_2}px`,
4825
4870
  height: '100%',
4826
4871
  overflow: 'auto',
4827
- }, children: [sticky ? (jsx(StickyHeader, { ref: headerRef, sx: { pt: `${spacing.spacing_2}px` }, children: headerContent })) : (jsx(Box, { ref: headerRef, sx: { mb: `${spacing.spacing_1}px` }, children: headerContent })), jsx(Box, { sx: {
4872
+ }, children: [sticky ? (jsx(StickyHeader, { ref: headerRef, sx: {
4873
+ pt: `${spacing.spacing_2}px`,
4874
+ ...(contentMaxWidth !== false && {
4875
+ maxWidth: theme => theme.breakpoints.values[contentMaxWidth],
4876
+ width: '100%',
4877
+ }),
4878
+ }, children: headerContent })) : (jsx(Box, { ref: headerRef, sx: {
4879
+ mb: `${spacing.spacing_1}px`,
4880
+ ...(contentMaxWidth !== false && {
4881
+ maxWidth: theme => theme.breakpoints.values[contentMaxWidth],
4882
+ width: '100%',
4883
+ }),
4884
+ }, children: headerContent })), jsx(Box, { sx: {
4828
4885
  flexGrow: 1,
4829
4886
  minHeight: 0,
4830
4887
  display: 'flex',
4831
4888
  flexDirection: 'column',
4832
4889
  ...(contentMaxWidth !== false && {
4833
4890
  maxWidth: theme => theme.breakpoints.values[contentMaxWidth],
4834
- mx: 'auto',
4835
4891
  width: '100%',
4892
+ ...(centerContent ? { mx: 'auto' } : { alignSelf: 'flex-start' }),
4836
4893
  }),
4837
4894
  }, children: content })] }) }), showBackToTop && (jsx(Fade, { in: showBackToTopButton, children: jsx(Box, { sx: {
4838
4895
  position: 'fixed',
@@ -5662,10 +5719,10 @@ const NeoToastButton = ({ primary, variant = 'default', children, ...props }) =>
5662
5719
  NeoToast.displayName = 'NeoToast';
5663
5720
  NeoToastButton.displayName = 'NeoToastButton';
5664
5721
 
5665
- const ToggleContainer = styled('label')(({ disabled }) => ({
5722
+ const ToggleContainer = styled('label')(({ disabled, size = 'medium' }) => ({
5666
5723
  display: 'inline-flex',
5667
5724
  alignItems: 'flex-start',
5668
- gap: 12,
5725
+ gap: size === 'small' ? 8 : 12,
5669
5726
  cursor: disabled ? 'not-allowed' : 'pointer',
5670
5727
  userSelect: 'none',
5671
5728
  }));
@@ -5817,7 +5874,7 @@ const NeoToggle = ({ size = 'medium', label, helperText, disabled, ...props }) =
5817
5874
  return jsx(StyledSwitch, { size: size, disabled: disabled, ...props });
5818
5875
  }
5819
5876
  // With label, wrap in container for proper layout
5820
- return (jsxs(ToggleContainer, { disabled: disabled, children: [jsx(StyledSwitch, { size: size, disabled: disabled, ...props }), jsxs(LabelContainer, { children: [label && jsx(Label, { size: size, children: label }), helperText && jsx(HelperText, { size: size, children: helperText })] })] }));
5877
+ return (jsxs(ToggleContainer, { disabled: disabled, size: size, children: [jsx(StyledSwitch, { size: size, disabled: disabled, ...props }), jsxs(LabelContainer, { children: [label && jsx(Label, { size: size, children: label }), helperText && jsx(HelperText, { size: size, children: helperText })] })] }));
5821
5878
  };
5822
5879
  NeoToggle.displayName = 'NeoToggle';
5823
5880