Skip to content

Commit 45c71bf

Browse files
committed
Implement responsive navigation design
1 parent aee3fac commit 45c71bf

14 files changed

Lines changed: 772 additions & 90 deletions

src/assets/icons/bars.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/icons/close.svg

Lines changed: 3 additions & 0 deletions
Loading

src/components/Menu.astro

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ type Props = HTMLAttributes<'details'> & {
3333
align?: 'start' | 'end';
3434
density?: 'default' | 'compact';
3535
width?: string;
36+
// Render inline rather than as a popup
37+
inline?: boolean;
3638
};
3739
3840
const {
@@ -41,6 +43,7 @@ const {
4143
align = 'start',
4244
density = 'default',
4345
width,
46+
inline = false,
4447
class: className,
4548
...rest
4649
} = Astro.props satisfies Props;
@@ -73,10 +76,11 @@ const iconDetails = (name: string | undefined) => {
7376
'menu',
7477
`menu--${align}`,
7578
density === 'compact' && 'menu--compact',
79+
inline && 'menu--inline',
7680
className,
7781
]}
78-
style={width ? `--menu-width: ${width}` : undefined}
79-
data-menu
82+
style={width && !inline ? `--menu-width: ${width}` : undefined}
83+
data-menu={inline ? undefined : ''}
8084
{...rest}
8185
>
8286
<slot name="trigger" />
@@ -351,4 +355,24 @@ const iconDetails = (name: string | undefined) => {
351355
img.menu__icon {
352356
object-fit: contain;
353357
}
358+
359+
.menu--inline .menu__list {
360+
position: static;
361+
position-area: none;
362+
width: auto;
363+
min-width: 0;
364+
max-width: none;
365+
margin: 0;
366+
padding: 0;
367+
background: none;
368+
border-radius: 0;
369+
box-shadow: none;
370+
opacity: 1;
371+
transform: none;
372+
transition: none;
373+
}
374+
375+
.menu--inline:not([open])::details-content {
376+
transition: none;
377+
}
354378
</style>

src/components/ProfileMenu.astro

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type Props = HTMLAttributes<'div'> & {
88
controlCenterHref?: string;
99
profileHref?: string;
1010
signOutHref?: string;
11+
inline?: boolean;
1112
};
1213
1314
const {
@@ -18,6 +19,8 @@ const {
1819
// Arbitrary subpaths of /docs are blocked on the server side, returning to /docs is the best we can do
1920
signOutHref = 'https://octopus.com/signout?ReturnUrl=/docs',
2021
22+
inline = false,
23+
2124
class: className,
2225
...rest
2326
} = Astro.props satisfies Props;
@@ -30,32 +33,63 @@ const leaving = {
3033
};
3134
3235
const items: MenuItem[] = [
33-
{
34-
// Details start out empty, signed-in-user.ts fills things in through Menu's hooks
35-
kind: 'detail',
36-
label: '',
37-
description: '',
38-
data: { 'data-user-details': '' },
39-
},
36+
// Omit name/email when inline, as they're used as the row you press to expand the menu
37+
...(inline
38+
? []
39+
: [
40+
{
41+
// Details start out empty, signed-in-user.ts fills things in through Menu's hooks
42+
kind: 'detail' as const,
43+
label: '',
44+
description: '',
45+
data: { 'data-user-details': '' },
46+
},
47+
]),
4048
{ label: 'Control Center', href: controlCenterHref, ...leaving },
4149
{ label: 'Profile', href: profileHref, ...leaving },
42-
{ kind: 'divider' },
50+
...(inline ? [] : [{ kind: 'divider' as const }]),
4351
{ label: 'Sign out', href: signOutHref },
4452
];
4553
---
4654

47-
<div class:list={['profile-menu', className]} {...rest}>
48-
<Menu label={label} items={items} align="end" width="16rem">
49-
<summary
50-
slot="trigger"
51-
class="profile-menu__trigger"
52-
aria-label={label}
53-
aria-haspopup="menu"
54-
>
55-
<Avatar size="medium" shape="circle" alt="" data-user-avatar>
56-
<span data-user-initials></span>
57-
</Avatar>
58-
</summary>
55+
<div
56+
class:list={['profile-menu', inline && 'profile-menu--inline', className]}
57+
{...rest}
58+
>
59+
<Menu
60+
label={label}
61+
items={items}
62+
align="end"
63+
width="16rem"
64+
inline={inline}
65+
density={inline ? 'compact' : 'default'}
66+
>
67+
{
68+
inline ? (
69+
<summary
70+
slot="trigger"
71+
class="profile-menu__row"
72+
aria-haspopup="menu"
73+
data-user-details
74+
>
75+
<span class="profile-menu__labels">
76+
<span class="profile-menu__name" data-menu-label />
77+
<span class="profile-menu__email" data-menu-description />
78+
</span>
79+
</summary>
80+
) : (
81+
<summary
82+
slot="trigger"
83+
class="profile-menu__trigger"
84+
aria-label={label}
85+
aria-haspopup="menu"
86+
>
87+
<Avatar size="medium" shape="circle" alt="" data-user-avatar>
88+
<span data-user-initials />
89+
</Avatar>
90+
</summary>
91+
)
92+
}
5993
</Menu>
6094
</div>
6195

@@ -93,4 +127,67 @@ const items: MenuItem[] = [
93127
background-color: currentColor;
94128
mask: url('../assets/icons/external-link.svg') center / 80% no-repeat;
95129
}
130+
131+
.profile-menu--inline {
132+
display: block;
133+
}
134+
135+
.profile-menu__row {
136+
display: flex;
137+
align-items: center;
138+
gap: var(--space8);
139+
padding-block: var(--space6);
140+
list-style: none;
141+
color: var(--colorTextPrimary);
142+
text-decoration: none;
143+
cursor: pointer;
144+
}
145+
146+
.profile-menu__row::-webkit-details-marker {
147+
display: none;
148+
}
149+
150+
.profile-menu__row:focus-visible {
151+
outline: var(--borderWidth2) solid var(--colorBorderSelected);
152+
outline-offset: var(--borderWidth2);
153+
}
154+
155+
.profile-menu__labels {
156+
display: flex;
157+
flex: 1 1 auto;
158+
flex-direction: column;
159+
min-width: 0;
160+
color: var(--colorTextPrimary);
161+
text-decoration: none;
162+
}
163+
164+
.profile-menu__name {
165+
font: var(--textBodyRegularLarge);
166+
overflow-wrap: anywhere;
167+
}
168+
169+
.profile-menu__email {
170+
color: var(--colorTextSecondary);
171+
font: var(--textBodyRegularSmall);
172+
overflow-wrap: anywhere;
173+
}
174+
175+
.profile-menu__row::after {
176+
content: '';
177+
flex: none;
178+
width: 1.25rem;
179+
height: 1.25rem;
180+
background-color: var(--colorIconTertiary);
181+
mask: url('../assets/icons/chevron-right.svg') center / contain no-repeat;
182+
}
183+
184+
:global([open]) > .profile-menu__row::after {
185+
mask-image: url('../assets/icons/chevron-down.svg');
186+
}
187+
188+
.profile-menu--inline :global(.menu__item) {
189+
padding-inline: 0;
190+
padding-block: var(--space6);
191+
font: var(--textBodyRegularLarge);
192+
}
96193
</style>

0 commit comments

Comments
 (0)