@automattic/vip-design-system 2.1.0 → 2.3.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 (101) hide show
  1. package/.eslintrc.js +3 -0
  2. package/build/system/Accordion/Accordion.test.d.ts +0 -1
  3. package/build/system/Accordion/Accordion.test.js +23 -12
  4. package/build/system/Avatar/Avatar.d.ts +0 -1
  5. package/build/system/Avatar/Avatar.js +9 -17
  6. package/build/system/Avatar/Avatar.stories.d.ts +2 -1
  7. package/build/system/Avatar/Avatar.stories.js +19 -2
  8. package/build/system/Button/Button.d.ts +2 -0
  9. package/build/system/Button/Button.js +6 -15
  10. package/build/system/Button/Button.stories.js +86 -67
  11. package/build/system/Card/Card.js +1 -2
  12. package/build/system/Drawer/Drawer.d.ts +12 -3
  13. package/build/system/Drawer/Drawer.js +8 -9
  14. package/build/system/Drawer/Drawer.stories.d.ts +0 -1
  15. package/build/system/Drawer/Drawer.stories.js +19 -225
  16. package/build/system/Drawer/styles.d.ts +2 -2
  17. package/build/system/Drawer/styles.js +13 -9
  18. package/build/system/Form/Checkbox/Checkbox.js +0 -3
  19. package/build/system/Form/Checkbox/Checkbox.stories.js +2 -2
  20. package/build/system/Form/Checkbox/styles.js +16 -11
  21. package/build/system/Form/Radio/Radio.stories.js +1 -1
  22. package/build/system/Form/Toggle.d.ts +14 -2
  23. package/build/system/Form/Toggle.js +68 -69
  24. package/build/system/Form/Toggle.stories.d.ts +29 -18
  25. package/build/system/Form/Toggle.stories.js +99 -0
  26. package/build/system/Form/Toggle.test.js +36 -19
  27. package/build/system/Link/Link.d.ts +1 -1
  28. package/build/system/Link/Link.js +5 -20
  29. package/build/system/Link/Link.stories.d.ts +3 -1
  30. package/build/system/Link/Link.stories.js +18 -1
  31. package/build/system/MobileMenu/MobileMenu.d.ts +16 -0
  32. package/build/system/MobileMenu/MobileMenu.js +112 -0
  33. package/build/system/MobileMenu/MobileMenu.stories.d.ts +19 -0
  34. package/build/system/MobileMenu/MobileMenu.stories.js +173 -0
  35. package/build/system/MobileMenu/MobileMenu.test.d.ts +1 -0
  36. package/build/system/MobileMenu/MobileMenu.test.js +81 -0
  37. package/build/system/Nav/NavItem.js +4 -5
  38. package/build/system/Nav/NavItemGroup.d.ts +3 -0
  39. package/build/system/Nav/NavItemGroup.js +18 -6
  40. package/build/system/Nav/styles/variants/menu.js +15 -9
  41. package/build/system/Nav/styles/variants/menugroup.js +9 -5
  42. package/build/system/Nav/styles/variants/toolbar.js +1 -2
  43. package/build/system/Nav/styles.js +1 -0
  44. package/build/system/NewDialog/DialogClose.d.ts +7 -2
  45. package/build/system/NewDialog/DialogClose.js +25 -15
  46. package/build/system/NewDialog/index.d.ts +2 -1
  47. package/build/system/NewDialog/index.js +2 -1
  48. package/build/system/NewForm/FormAutocomplete.css +1 -3
  49. package/build/system/Table/Table.js +1 -0
  50. package/build/system/Toolbar/Toolbar.js +1 -1
  51. package/build/system/Toolbar/Toolbar.stories.d.ts +3 -0
  52. package/build/system/Toolbar/Toolbar.stories.js +62 -6
  53. package/build/system/Toolbar/ToolbarUtilNav.d.ts +3 -0
  54. package/build/system/Toolbar/ToolbarUtilNav.js +21 -3
  55. package/build/system/Toolbar/index.d.ts +3 -0
  56. package/build/system/Toolbar/index.js +2 -1
  57. package/build/system/index.d.ts +4 -1
  58. package/build/system/index.js +4 -0
  59. package/build/system/theme/index.d.ts +70 -35
  60. package/build/system/theme/index.js +47 -18
  61. package/package.json +2 -1
  62. package/src/system/Accordion/Accordion.test.tsx +15 -8
  63. package/src/system/Avatar/Avatar.stories.tsx +15 -1
  64. package/src/system/Avatar/Avatar.tsx +8 -12
  65. package/src/system/Button/Button.stories.tsx +40 -31
  66. package/src/system/Button/Button.tsx +6 -15
  67. package/src/system/Card/Card.tsx +0 -1
  68. package/src/system/Drawer/Drawer.stories.tsx +28 -197
  69. package/src/system/Drawer/Drawer.tsx +31 -18
  70. package/src/system/Drawer/styles.ts +15 -5
  71. package/src/system/Form/Checkbox/Checkbox.stories.tsx +2 -2
  72. package/src/system/Form/Checkbox/Checkbox.tsx +0 -3
  73. package/src/system/Form/Checkbox/styles.ts +57 -46
  74. package/src/system/Form/Radio/Radio.stories.tsx +1 -1
  75. package/src/system/Form/Toggle.stories.tsx +122 -0
  76. package/src/system/Form/{Toggle.test.js → Toggle.test.tsx} +1 -1
  77. package/src/system/Form/Toggle.tsx +77 -0
  78. package/src/system/Link/Link.stories.tsx +21 -0
  79. package/src/system/Link/Link.tsx +10 -17
  80. package/src/system/MobileMenu/MobileMenu.stories.tsx +171 -0
  81. package/src/system/MobileMenu/MobileMenu.test.tsx +50 -0
  82. package/src/system/MobileMenu/MobileMenu.tsx +116 -0
  83. package/src/system/Nav/NavItem.tsx +3 -3
  84. package/src/system/Nav/NavItemGroup.tsx +18 -2
  85. package/src/system/Nav/styles/variants/menu.ts +15 -9
  86. package/src/system/Nav/styles/variants/menugroup.ts +7 -3
  87. package/src/system/Nav/styles/variants/toolbar.ts +1 -2
  88. package/src/system/Nav/styles.ts +1 -0
  89. package/src/system/NewDialog/DialogClose.tsx +36 -23
  90. package/src/system/NewDialog/index.ts +3 -2
  91. package/src/system/NewForm/FormAutocomplete.css +1 -3
  92. package/src/system/Table/Table.tsx +1 -1
  93. package/src/system/Toolbar/Toolbar.stories.tsx +50 -3
  94. package/src/system/Toolbar/Toolbar.tsx +1 -1
  95. package/src/system/Toolbar/ToolbarUtilNav.tsx +19 -2
  96. package/src/system/Toolbar/index.tsx +6 -1
  97. package/src/system/index.js +4 -0
  98. package/src/system/theme/index.js +47 -18
  99. package/build/system/Form/Toggle.stories.jsx +0 -96
  100. package/src/system/Form/Toggle.js +0 -74
  101. package/src/system/Form/Toggle.stories.jsx +0 -96
@@ -0,0 +1,77 @@
1
+ /* eslint-disable max-len */
2
+ /** @jsxImportSource theme-ui */
3
+
4
+ import * as Switch from '@radix-ui/react-switch';
5
+ import classNames, { Argument } from 'classnames';
6
+ import { Theme } from 'theme-ui';
7
+
8
+ // Documentation for Radix Switch component
9
+ // https://www.radix-ui.com/docs/primitives/components/switch
10
+
11
+ interface ThemeProps extends Theme {
12
+ outline?: Record< string, string >;
13
+ }
14
+
15
+ export type ToggleProps = Switch.SwitchProps & {
16
+ name?: string;
17
+ className?: Argument;
18
+ onChange?: () => void;
19
+ variant?: string;
20
+ };
21
+
22
+ export const Toggle = ( {
23
+ name = 'toggle',
24
+ onChange,
25
+ className,
26
+ variant = 'primary',
27
+ ...rest
28
+ }: ToggleProps ) => (
29
+ <Switch.Root
30
+ className={ classNames( 'vip-toggle-component', className ) }
31
+ sx={ {
32
+ all: 'unset',
33
+ cursor: 'pointer',
34
+ position: 'relative',
35
+ width: 40,
36
+ height: 20,
37
+ borderRadius: 5,
38
+ backgroundColor: 'muted',
39
+ backgroundRepeat: 'no-repeat',
40
+ backgroundPosition: 'right 2px top 2px',
41
+ backgroundImage: `url(
42
+ 'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.53846 3L3 4.53846L6.46156 8.00001L3.00003 11.4615L4.53848 13L8.00001 9.53847L11.4615 13L13 11.4615L9.53847 8.00001L13 4.53849L11.4615 3.00003L8.00001 6.46156L4.53846 3Z" fill="white"/></svg>')`,
43
+ WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)',
44
+
45
+ '&:focus-visible': ( theme: ThemeProps ) => theme.outline,
46
+ '&[disabled]': {
47
+ opacity: 0.7,
48
+ },
49
+ '&[data-state="checked"]': {
50
+ backgroundColor: variant,
51
+ backgroundPosition: 'left 2px top 2px',
52
+ backgroundImage: `url(
53
+ 'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M13.4999 4.9995L5.7254 12.4008L2.5 9.33023L3.83307 7.92994L5.7254 9.73144L12.1668 3.59921L13.4999 4.9995Z" fill="white"/></svg>')`,
54
+ },
55
+ } }
56
+ name={ name }
57
+ onCheckedChange={ onChange }
58
+ { ...rest }
59
+ >
60
+ <Switch.Thumb
61
+ sx={ {
62
+ display: 'block',
63
+ width: 16,
64
+ height: 16,
65
+ backgroundColor: 'white',
66
+ borderRadius: '100%',
67
+ boxShadow: 'rgb(0 0 0 / 5%) 0px 1px 5px, rgb(0 0 0 / 15%) 0px 1px 1px',
68
+ transition: 'transform 100ms',
69
+ transform: 'translateX(2px)',
70
+ willChange: 'transform',
71
+ '&[data-state="checked"]': { transform: 'translateX(22px)' },
72
+ } }
73
+ />
74
+ </Switch.Root>
75
+ );
76
+
77
+ Toggle.displayName = 'Toggle';
@@ -2,7 +2,9 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { Link } from '..';
5
+ import { Flex } from '../Flex/Flex';
5
6
 
7
+ import type { LinkProps } from './Link';
6
8
  import type { StoryObj } from '@storybook/react';
7
9
 
8
10
  /**
@@ -22,3 +24,22 @@ export const Default: Story = {
22
24
  href: '#!',
23
25
  },
24
26
  };
27
+
28
+ const buttonTypes: LinkProps[ 'variant' ][] = [
29
+ 'button-primary',
30
+ 'button-secondary',
31
+ 'button-tertiary',
32
+ 'button-danger',
33
+ 'button-display',
34
+ 'button-ghost',
35
+ ];
36
+
37
+ export const ButtonVariants = () => (
38
+ <Flex sx={ { gap: 2 } }>
39
+ { buttonTypes.map( ( variant, index ) => (
40
+ <Link key={ index } href="#!" variant={ variant }>
41
+ Hello
42
+ </Link>
43
+ ) ) }
44
+ </Flex>
45
+ );
@@ -15,33 +15,26 @@ interface LinkTheme extends Theme {
15
15
  }
16
16
 
17
17
  export interface LinkProps extends ThemeLinkProps {
18
- active?: boolean;
18
+ variant?:
19
+ | 'primary'
20
+ | 'button-primary'
21
+ | 'button-secondary'
22
+ | 'button-tertiary'
23
+ | 'button-ghost'
24
+ | 'button-display'
25
+ | 'button-danger';
19
26
  }
20
27
 
21
28
  export const defaultLinkComponentStyle: ThemeUIStyleObject = {
22
- color: 'link',
23
- textDecorationThickness: '0.1em',
24
- textUnderlineOffset: '0.1em',
25
- '&:visited': {
26
- color: 'links.visited',
27
- },
28
- '&:active': {
29
- color: 'links.active',
30
- },
31
- '&:hover, &:focus': {
32
- color: 'links.hover',
33
- textDecorationLine: 'underline',
34
- textDecorationThickness: '2px',
35
- },
36
29
  '&:focus-visible': ( theme: LinkTheme ) => theme.outline,
37
30
  };
38
31
 
39
32
  export const Link = forwardRef< HTMLAnchorElement, LinkProps >(
40
- ( { active = false, sx, ...props }: LinkProps, ref: Ref< HTMLAnchorElement > ) => (
33
+ ( { variant = 'primary', sx, ...props }: LinkProps, ref: Ref< HTMLAnchorElement > ) => (
41
34
  <ThemeLink
35
+ variant={ variant }
42
36
  sx={ {
43
37
  ...defaultLinkComponentStyle,
44
- color: active ? 'links.active' : 'link',
45
38
  ...sx,
46
39
  } }
47
40
  ref={ ref }
@@ -0,0 +1,171 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ import { AiOutlineLock } from 'react-icons/ai';
4
+ import {
5
+ BiBell,
6
+ BiBulb,
7
+ BiCodeAlt,
8
+ BiData,
9
+ BiGridAlt,
10
+ BiHistory,
11
+ BiTachometer,
12
+ BiWindows,
13
+ } from 'react-icons/bi';
14
+ import { MdOutlinePhotoLibrary } from 'react-icons/md';
15
+
16
+ import { MobileMenu, MobileMenuTrigger, MobileMenuWrapper } from './MobileMenu';
17
+ import { Nav, NavItem } from '..';
18
+ import { CustomLink } from '../utils/stories/CustomLink';
19
+
20
+ import type { StoryObj } from '@storybook/react';
21
+
22
+ export default {
23
+ title: 'Navigation/MobileMenu',
24
+ component: MobileMenu,
25
+ parameters: {
26
+ docs: {
27
+ description: {
28
+ component: `
29
+ The MobileMenu component is a navigation component that is hidden by default and can be toggled open and closed. It opens a panel that can contain any content. The base of this component is a Drawer.
30
+
31
+ ## Guidance
32
+
33
+ ### When to use the Drawer component
34
+
35
+ - Use the MobileMenu component to displaying a drawer with mobile content.
36
+
37
+ ### When to consider something else
38
+
39
+ - If you need something that is not a menu on a mobile view, consider using another component.
40
+
41
+ ## Accessibility Considerations guidance
42
+
43
+ - The MobileMenu is based on the Drawer component and inherits all of its accessibility considerations.
44
+
45
+ ## Using the component
46
+
47
+ - Look at the examples below to see how the MobileMenu component is used.
48
+ - You can use the \`toolbarItems\` prop to pass the content that will be in the header
49
+ - You can pass \`display\` prop to the MobileMenuTrigger to control the display of the trigger. The array must match the ThemeUI breakpoints.
50
+
51
+ ## Component Properties
52
+ `,
53
+ },
54
+ },
55
+ },
56
+ };
57
+
58
+ type Story = StoryObj< typeof MobileMenu >;
59
+
60
+ export const MobileMenuExample = () => (
61
+ <MobileMenuWrapper>
62
+ <MobileMenuTrigger label="Menu" variant="primary" display={ [ 'flex', 'flex', 'flex' ] } />
63
+ <MobileMenu
64
+ toolbarItems={
65
+ <>
66
+ <NavItem.MenuInverse href="/apps" active as={ CustomLink }>
67
+ My Applications
68
+ </NavItem.MenuInverse>
69
+
70
+ <NavItem.MenuInverse href="/orgs" as={ CustomLink }>
71
+ My Organizations
72
+ </NavItem.MenuInverse>
73
+ </>
74
+ }
75
+ >
76
+ <Nav.Menu sx={ { mb: 4 } } label="Nav Menu">
77
+ <NavItem.Menu
78
+ href="https://wordpress.com"
79
+ renderIcon={ size => <BiGridAlt size={ size } /> }
80
+ as={ CustomLink }
81
+ >
82
+ Overview
83
+ </NavItem.Menu>
84
+ <NavItem.Menu
85
+ as={ CustomLink }
86
+ href="https://random-website.com/"
87
+ renderIcon={ size => <BiWindows size={ size } /> }
88
+ >
89
+ Network Sites
90
+ </NavItem.Menu>
91
+ <NavItem.Menu
92
+ as={ CustomLink }
93
+ href="https://random-website.com/"
94
+ renderIcon={ size => <AiOutlineLock size={ size } /> }
95
+ >
96
+ Domains & TLS
97
+ </NavItem.Menu>
98
+
99
+ <NavItem.MenuGroup active label="Logs" renderIcon={ size => <BiHistory size={ size } /> }>
100
+ <NavItem.Menu as={ CustomLink } href="https://google.com/">
101
+ Audit
102
+ </NavItem.Menu>
103
+ <NavItem.Menu active as={ CustomLink } href="https://wpvip.com/">
104
+ Runtime
105
+ </NavItem.Menu>
106
+ <NavItem.Menu as={ CustomLink } href="https://dashboard.wpvip.com/">
107
+ Slow Query
108
+ </NavItem.Menu>
109
+ </NavItem.MenuGroup>
110
+
111
+ <NavItem.MenuGroup
112
+ label="Performance"
113
+ renderIcon={ size => <BiTachometer size={ size } /> }
114
+ >
115
+ <NavItem.Menu as={ CustomLink } href="https://random-website.com/">
116
+ Metrics
117
+ </NavItem.Menu>
118
+ <NavItem.Menu as={ CustomLink } href="https://random-website.com/">
119
+ Monitor
120
+ </NavItem.Menu>
121
+ <NavItem.Menu as={ CustomLink } href="https://random-website.com/">
122
+ Cache
123
+ </NavItem.Menu>
124
+ </NavItem.MenuGroup>
125
+ <NavItem.Menu
126
+ as={ CustomLink }
127
+ href="https://random-website.com/"
128
+ renderIcon={ size => <BiCodeAlt size={ size } /> }
129
+ >
130
+ Code [v]
131
+ </NavItem.Menu>
132
+ <NavItem.Menu
133
+ as={ CustomLink }
134
+ href="https://random-website.com/"
135
+ renderIcon={ size => <BiData size={ size } /> }
136
+ >
137
+ Database [v]
138
+ </NavItem.Menu>
139
+ <NavItem.Menu
140
+ as={ CustomLink }
141
+ href="https://random-website.com/"
142
+ renderIcon={ size => <MdOutlinePhotoLibrary size={ size } /> }
143
+ >
144
+ Media [v]
145
+ </NavItem.Menu>
146
+ <NavItem.Menu
147
+ as={ CustomLink }
148
+ href="https://random-website.com/"
149
+ renderIcon={ size => <BiBell size={ size } /> }
150
+ >
151
+ Notifications
152
+ </NavItem.Menu>
153
+ <NavItem.Menu
154
+ as={ CustomLink }
155
+ href="https://random-website.com/"
156
+ renderIcon={ size => <BiBulb size={ size } /> }
157
+ >
158
+ Features
159
+ </NavItem.Menu>
160
+ </Nav.Menu>
161
+ </MobileMenu>
162
+ </MobileMenuWrapper>
163
+ );
164
+
165
+ export const Default: Story = {
166
+ render: () => (
167
+ <>
168
+ <MobileMenuExample />
169
+ </>
170
+ ),
171
+ };
@@ -0,0 +1,50 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+ // @ts-nocheck
3
+ /** @jsxImportSource theme-ui */
4
+ import { render, screen } from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
6
+ import { axe } from 'jest-axe';
7
+ import { ThemeUIProvider } from 'theme-ui';
8
+
9
+ import { MobileMenuExample } from './MobileMenu.stories';
10
+ import { theme } from '../';
11
+
12
+ const renderWithTheme = children =>
13
+ render( <ThemeUIProvider theme={ theme }>{ children }</ThemeUIProvider> );
14
+
15
+ const renderComponent = () => renderWithTheme( <MobileMenuExample /> );
16
+
17
+ function getMenuTrigger() {
18
+ return screen.getByRole( 'button', { name: 'Menu' } );
19
+ }
20
+
21
+ describe( '<MobileMenu />', () => {
22
+ it( 'renders the MobileMenu trigger', async () => {
23
+ const { container } = renderComponent();
24
+
25
+ // Should find the trigger
26
+ expect( getMenuTrigger() ).toBeInTheDocument();
27
+
28
+ expect( getMenuTrigger() ).toHaveAttribute( 'data-state', 'closed' );
29
+
30
+ // Check for accessibility issues
31
+ expect( await axe( container ) ).toHaveNoViolations();
32
+ } );
33
+
34
+ it( 'opens MobileMenu and check for items', async () => {
35
+ const user = userEvent.setup();
36
+
37
+ const { container } = renderComponent();
38
+
39
+ await user.click( getMenuTrigger() );
40
+
41
+ // Should find the open content
42
+ expect( screen.getByText( 'My Applications' ) ).toBeVisible(); // First menu
43
+ expect( screen.getByText( 'My Organizations' ) ).toBeVisible();
44
+ expect( screen.getByText( 'Performance' ) ).toBeVisible();
45
+ expect( screen.getByText( 'Features' ) ).toBeVisible(); // Last Menu
46
+
47
+ // Check for accessibility issues
48
+ expect( await axe( container ) ).toHaveNoViolations();
49
+ } );
50
+ } );
@@ -0,0 +1,116 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ import React, { forwardRef } from 'react';
4
+ import { BiMenu } from 'react-icons/bi';
5
+
6
+ import * as Drawer from '../Drawer/Drawer';
7
+ import { DialogCloseDefault } from '../NewDialog/DialogClose';
8
+ import { Logo } from '../Toolbar/Logo';
9
+ import { Button, Flex, Nav, Box } from '../index';
10
+
11
+ export interface MobileMenuProps {
12
+ children: React.ReactNode;
13
+ toolbarItems?: React.ReactNode;
14
+ }
15
+
16
+ export const MobileMenu = forwardRef< HTMLDivElement, MobileMenuProps >(
17
+ ( { children, toolbarItems }, ref ) => (
18
+ <Drawer.Content
19
+ variant="left"
20
+ label="Main Navigation Items"
21
+ ref={ ref }
22
+ renderClose={ () => <DialogCloseDefault variant="inverse" /> }
23
+ >
24
+ <Box
25
+ sx={ {
26
+ backgroundColor: 'toolbar.background',
27
+ boxShadow: 'none',
28
+ gap: 4,
29
+ } }
30
+ >
31
+ <Flex
32
+ sx={ {
33
+ flexWrap: 'wrap',
34
+ height: 64,
35
+ width: '100%',
36
+ py: 0,
37
+ px: 5,
38
+ gap: 7,
39
+ alignItems: 'center',
40
+ } }
41
+ >
42
+ <Logo />
43
+ </Flex>
44
+
45
+ <div
46
+ sx={ {
47
+ overflowX: 'hidden',
48
+ overflowY: 'auto',
49
+ height: 'calc(100vh - 64px)',
50
+ display: 'flex',
51
+ flex: 1,
52
+ } }
53
+ >
54
+ <Flex sx={ { width: '100%', flexDirection: 'column' } }>
55
+ { toolbarItems && (
56
+ <Nav.Primary label="Main Links" orientation="vertical">
57
+ { toolbarItems }
58
+ </Nav.Primary>
59
+ ) }
60
+
61
+ <Box
62
+ sx={ {
63
+ alignSelf: 'stretch',
64
+ backgroundColor: 'layer.1',
65
+ minHeight: `calc(100vh - 64px)`,
66
+ gap: 4,
67
+ width: '100%',
68
+ a: {
69
+ border: 'none',
70
+ },
71
+ } }
72
+ >
73
+ { children }
74
+ </Box>
75
+ </Flex>
76
+ </div>
77
+ </Box>
78
+ </Drawer.Content>
79
+ )
80
+ );
81
+
82
+ export const MobileMenuWrapper = ( { children }: MobileMenuProps ) => (
83
+ <Drawer.Root>{ children }</Drawer.Root>
84
+ );
85
+
86
+ type MobileMenuTriggerDisplayProp = 'none' | 'flex';
87
+ type MobileMenuTriggerProps = {
88
+ label: string;
89
+ variant?: 'primary' | 'inverse';
90
+ display?: MobileMenuTriggerDisplayProp[];
91
+ };
92
+
93
+ export const MobileMenuTrigger = ( {
94
+ label = 'Menu',
95
+ variant = 'primary',
96
+ display = [ 'none', 'flex', 'flex', 'none' ],
97
+ }: MobileMenuTriggerProps ) => (
98
+ <Drawer.Trigger>
99
+ <Button
100
+ type="button"
101
+ variant="tertiary"
102
+ sx={ {
103
+ display,
104
+ alignItems: 'center',
105
+ color:
106
+ variant === 'inverse' ? 'button.primary.label.default' : 'button.tertiary.label.default',
107
+ width: 38,
108
+ height: 38,
109
+ p: 0,
110
+ } }
111
+ aria-label={ label }
112
+ >
113
+ <BiMenu size={ 16 } role="presentation" />
114
+ </Button>
115
+ </Drawer.Trigger>
116
+ );
@@ -6,7 +6,7 @@ import { Ref, forwardRef } from 'react';
6
6
  import { Theme, ThemeUIStyleObject } from 'theme-ui';
7
7
 
8
8
  import { NavItemRenderIconProp, NavProps, NavVariant, VIP_NAV } from './Nav';
9
- import { ItemGroupMenu } from './NavItemGroup';
9
+ import { IconContainer, ItemGroupMenu } from './NavItemGroup';
10
10
  import { navItemLinkStyles, navItemStyles } from './styles';
11
11
 
12
12
  export const NAV_ITEM_ICON_SIZE = 20;
@@ -90,7 +90,7 @@ const NavLink = forwardRef< HTMLAnchorElement, NavItemProps >(
90
90
  {
91
91
  children,
92
92
  as: LinkComponent = NavRawLink,
93
- renderIcon = () => null,
93
+ renderIcon,
94
94
  href,
95
95
  active,
96
96
  disabled,
@@ -115,7 +115,7 @@ const NavLink = forwardRef< HTMLAnchorElement, NavItemProps >(
115
115
  { ...rest }
116
116
  >
117
117
  <LinkComponent>
118
- { renderIcon( NAV_ITEM_ICON_SIZE ) }
118
+ { renderIcon ? <IconContainer>{ renderIcon( NAV_ITEM_ICON_SIZE ) }</IconContainer> : null }
119
119
  { children }
120
120
  </LinkComponent>
121
121
  </NavigationMenu.Link>
@@ -28,7 +28,7 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
28
28
  orientation,
29
29
  className,
30
30
  active,
31
- renderIcon = () => null,
31
+ renderIcon,
32
32
  children,
33
33
  sx,
34
34
  }: NavItemGroupProps,
@@ -59,7 +59,9 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
59
59
  ...navItemGroupTriggerStyles,
60
60
  } }
61
61
  >
62
- { renderIcon( NAV_ITEM_ICON_SIZE ) }
62
+ { renderIcon ? (
63
+ <IconContainer>{ renderIcon( NAV_ITEM_ICON_SIZE ) }</IconContainer>
64
+ ) : null }
63
65
  { label }
64
66
 
65
67
  <BiChevronDown
@@ -79,6 +81,20 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
79
81
  }
80
82
  );
81
83
 
84
+ export const IconContainer = ( { children }: { children: React.ReactNode } ) => (
85
+ <div
86
+ sx={ {
87
+ width: 28,
88
+ height: 30,
89
+ alignItems: 'center',
90
+ justifyContent: 'center',
91
+ display: 'inline-flex',
92
+ } }
93
+ >
94
+ { children }
95
+ </div>
96
+ );
97
+
82
98
  export const ItemGroupMenu = forwardRef< HTMLLIElement, NavItemGroupProps >(
83
99
  ( props: NavItemGroupProps, ref: Ref< HTMLLIElement > ) => (
84
100
  <NavItemGroupBase { ...props } orientation="vertical" variant="menu" ref={ ref } />
@@ -14,7 +14,7 @@ export const menuItemStyles = ( orientation: NavProps[ 'orientation' ] ): ThemeU
14
14
  border: 'none',
15
15
  height: 38,
16
16
  width: '100%',
17
- mb: 1,
17
+ justifyContent: 'space-between',
18
18
  } );
19
19
 
20
20
  // Menu Inverse Item Style <li>
@@ -31,7 +31,7 @@ export const menuInverseItemStyles = (
31
31
  // Menu Item Link <a>
32
32
 
33
33
  const visitedLink = '&:visited';
34
- const activeAfter = '&[data-active]::after';
34
+ const activeAfter = '&[data-active]::before';
35
35
  const active = '&[data-active]';
36
36
  const focusNotActiveHoverNotActive = '&:focus:not(&[data-active]), &:hover:not(&[data-active])';
37
37
  const notHover = ':not(&:hover)';
@@ -45,15 +45,19 @@ export const menuItemLinkStyles: MixedStyleProp = {
45
45
  borderRadius: 1,
46
46
  color: 'text',
47
47
  display: 'inline-flex',
48
- fontSize: 'inherit',
48
+ fontSize: '0.875rem',
49
49
  fontWeight: 'body',
50
- gap: 3,
51
50
  height: 38,
52
51
  mx: 0,
53
52
  mb: 0,
53
+ pt: 3,
54
+ pr: 4,
55
+ pb: 3,
54
56
  pl: 5,
55
- pr: 3,
56
- py: 2,
57
+ ':has(div > svg)': {
58
+ pl: 4,
59
+ },
60
+ gap: 1,
57
61
  textDecoration: 'none',
58
62
  width: '100%',
59
63
  [ visitedLink ]: {
@@ -68,11 +72,11 @@ export const menuItemLinkStyles: MixedStyleProp = {
68
72
  borderRadius: '90px',
69
73
  height: 26,
70
74
  top: '6px',
71
- left: 3,
75
+ left: 2,
72
76
  },
73
77
  [ active ]: {
74
78
  color: 'heading',
75
- backgroundColor: 'layer.2',
79
+ backgroundColor: 'backgrounds.primary',
76
80
  textDecoration: 'none',
77
81
  cursor: 'default',
78
82
  svg: {
@@ -93,6 +97,8 @@ export const menuItemLinkStyles: MixedStyleProp = {
93
97
  [ svgIcon ]: {
94
98
  color: 'icon.secondary',
95
99
  fill: 'icon.secondary',
100
+ width: 20,
101
+ height: 20,
96
102
  display: 'block',
97
103
  },
98
104
  };
@@ -114,7 +120,7 @@ export const menuInverseItemLinkStyles = {
114
120
  ? {
115
121
  ...menuItemLinkStyles[ active ],
116
122
  color: 'toolbar.text.default',
117
- backgroundColor: 'tool`bar.background',
123
+ backgroundColor: 'toolbar.background',
118
124
  }
119
125
  : {},
120
126
  [ focusNotActiveHoverNotActive ]:
@@ -8,8 +8,12 @@ export const navItemGroupStyles = (
8
8
  variant?: NavVariant
9
9
  ): ThemeUIStyleObject => {
10
10
  const defaultStyle = {
11
- '&:last-of-type': {
11
+ li: {
12
+ mb: 1,
13
+ },
14
+ 'li:last-of-type': {
12
15
  mr: orientation === 'horizontal' ? 0 : undefined,
16
+ mb: orientation === 'vertical' ? 0 : undefined,
13
17
  },
14
18
  };
15
19
 
@@ -29,7 +33,6 @@ export const navItemGroupStyles = (
29
33
  };
30
34
 
31
35
  export const navItemGroupTriggerStyles: ThemeUIStyleObject = {
32
- mb: 1,
33
36
  'svg[data-arrow-indicator]': {
34
37
  position: 'absolute',
35
38
  right: 3,
@@ -53,8 +56,9 @@ export const navItemGroupTriggerStyles: ThemeUIStyleObject = {
53
56
  export const navItemGroupContentUlStyles: ThemeUIStyleObject = {
54
57
  m: 0,
55
58
  p: 0,
56
- pl: 3,
59
+ pl: 5,
57
60
  listStyle: 'none',
61
+ pt: 1,
58
62
  };
59
63
 
60
64
  const slideDown = keyframes( {
@@ -8,6 +8,7 @@ export const toolbarRootStyles: ThemeUIStyleObject = {
8
8
  display: [ 'none', 'none', 'flex' ],
9
9
  height: '100%',
10
10
  ml: 0,
11
+ gap: 6,
11
12
  width: 'max-content',
12
13
  };
13
14
 
@@ -28,8 +29,6 @@ export const defaultToolbarLinkStyle: ThemeUIStyleObject = {
28
29
  export const toolbarItemLinkStyles: ThemeUIStyleObject = {
29
30
  ...defaultItemLinkStyles,
30
31
  position: 'relative',
31
- ml: 3,
32
- mr: 3,
33
32
  height: '100%',
34
33
  ...defaultToolbarLinkStyle,
35
34
 
@@ -136,6 +136,7 @@ export const navMenuListStyles = ( orientation: NavProps[ 'orientation' ] ): The
136
136
  justifyContent: 'flex-start',
137
137
  m: 0,
138
138
  height: '100%',
139
+ gap: 1,
139
140
  px: 0,
140
141
  flexDirection: orientation === 'horizontal' ? 'row' : 'column',
141
142
  };