FlexMenu

FlexMenu is designed to make rendering flex-powered menus from an array of data easy and fast. With flexibility in terms of what you render and ease of styling this menu should cover about 80% of horizontal menu implementations.

FlexMenu

Edit the props to see this component update live!

jsx

Data

This component builds a menu (nav > ul > li > a) from an array of data. Each object in the array defines a li item in the menu. To define a li item you must provide a unique "id", a "label" and a "url":

json
[
{
id: "1",
label: "Comic Books",
url: "/comic-books/index",
}, ...
]

From this object a li item containing an a tag, which uses the "label" and "url", will be added to the menu.

However, sometimes you may want to render a li element in the menu which contains an arbitrary component instead of a standard a tag.

In this case you must add a component property to the data object:

json
[
{
id: "1",
label: "Comic Books",
url: "/comic-books/index",
component: "ComicBookComponent"
}, ...
]

Because you have defined a component you must pass in a componentMap prop to point from the component defined in your data to the component to be used

jsx
import { MyComicBookComponent } from "super-comicbook-components"
...some stuff
<FlexMenu
componentMap={{
ComicBookComponent: MyComicBookComponent
}}
data={[
{
id: "1",
label: "Comic Books",
url: "/comic-books/index",
component: "ComicBookComponent"
}, ...more data
]}
/>
...other stuff

If you want to wrap the component in a link it is important to use the FlexMenuLink component not Link component.

The point and power of FlexMenu lies in the application of functions to provide the correct padding, margins, etc... to render various flex-powered menus. Using the FlexMenuLink component when you want to render a component wrapped in an a tag ensures that the correct values are applied.

Variants

The variant prop is passed through to the FlexMenuLink component. It defines the style of the links.

Variant: mdx

Variant: ui

Variant: chrome

Variant: primary

Variant: secondary

Variant: black

Variant: grey

Variant: white

Variant: info

Variant: success

Variant: warning

Variant: error

Variant: fancy

Props

Justify

The justify prop takes the flex values: "center", "flex-start", "flex-end", "space-between", "space-around".

Add padding to the links if necessary.

justify right

jsx
<Box color="primaryText" bg="primary">
<FlexMenu
variant="primary"
justify="flex-end"
height="80px"
data={menuData}
componentMap={{
SiteName: (props) => {
return (
<SiteBranding
variant="primary"
siteName="SmerthTheme"
siteSlogan="Nice building blocks"
size="xs"
item={{ url: "/" }}
>
<SiteLogo />
</SiteBranding>
)
},
}}
sx={{
li: {
borderRight: (theme) => `1px solid ${theme.colors.primaryMuted}`,
"&:first-of-type": {
borderLeft: (theme) => `1px solid ${theme.colors.primaryMuted}`,
},
".active": { textDecoration: "none" },
"&:hover": { bg: "primaryMuted" },
a: {
px: [1, 2],
},
},
}}
/>
</Box>

Expand

When passing "center" or "space-around" to the justify prop the menu items are spaced evenly, the distance between each item is the same.

Example: justify space-around

jsx
<Box color="secondaryText" bg="secondary">
<FlexMenu
variant="secondary"
justify="space-around"
height="80px"
data={menuData}
componentMap={{
SiteName: (props) => {
return (
<SiteBranding
variant="secondary"
siteName="SmerthTheme"
siteSlogan="Nice building blocks"
hideName
hideSlogan
item={{ url: "/" }}
>
<SiteLogo />
</SiteBranding>
)
},
}}
sx={{
li: {
".active": { textDecoration: "none" },
a: {
border: "1px solid",
borderColor: "secondaryMuted",
"&:hover": { bg: "secondaryMuted" },
},
},
}}
/>
</Box>

To cause the a tags fill the space between each item you can pass in the expand prop.

This is necessary for some types of styling.

Example: justify space-around with expand prop

jsx
<Box color="secondaryText" bg="secondary">
<FlexMenu
variant="secondary"
justify="space-around"
height="80px"
expand
data={menuData}
componentMap={{
SiteName: (props) => {
return (
<SiteBranding
variant="secondary"
siteName="SmerthTheme"
siteSlogan="Nice building blocks"
hideName
hideSlogan
item={{ url: "/" }}
>
<SiteLogo />
</SiteBranding>
)
},
}}
sx={{
li: {
".active": { textDecoration: "none" },
a: {
border: "1px solid",
borderColor: "secondaryMuted",
"&:hover": { bg: "secondaryMuted" },
},
},
}}
/>
</Box>

wrapOnMobile

By default is set to overflow: hidden on the nav element. If the ul width exceeds the nav the overflow content will not be accessible.

Passing the wrapOnMobile prop does the following:

  • sets overflow: scroll on the ul for desktop screens, so users can scroll through overflow menu items
  • sets flex-wrap: wrap on the ul on mobile so the list wraps across multiple rows.
  • set gap for rows to add space between the wrapped content rows.

Example: wrapOnMobile

jsx
<Box color="chromeText" bg="chrome" sx={{ position: "relative" }}>
<FlexMenu
variant="chrome"
justify="space-between"
height="45px"
wrapOnMobile
gap="8px 0px"
mr={["0px", "0px", "30px"]}
data={bigMenuData}
componentMap={{
SiteName: (props) => {
return (
<SiteBranding
variant="chrome"
siteName="SmerthTheme"
siteSlogan="Nice building blocks"
hideName
hideSlogan
item={{ url: "/" }}
>
<SiteLogo size="43px" />
</SiteBranding>
)
},
}}
sx={{
li: {
".active": { textDecoration: "none" },
a: {
px: 2,
border: "1px solid",
borderColor: "chromeMuted",
"&:hover": { bg: "chromeMuted" },
},
},
}}
/>
<SwapOnMobile
mobileComponent={""}
desktopComponent={
<Box
variant="layout.svg"
px={3}
bg="chromeMuted"
sx={{ position: "absolute", height: "100%", top: "0px", right: "0px" }}
>
<MdArrowForwardIos />
</Box>
}
/>
</Box>

Styling Notes

  • No background color is set on the menu. It will inherit from the containing element. To have Link colors match the background-color set the menu variant to same color as the container background-color.
  • It is often more flexible, for styling purposes, to apply height to the a tag instead of the nav tag, the ul tag or the li tag. The height of a menu can be set on the a tag using the dedicated variable: "header". This can be convenient if you need other page elements (like page offset from top) use the same variable.
  • You can use the Theme-ui sx prop to style nested elements in the menu.
  • When items are justified left, right or centered, apply margin or padding to the li element to separate items according to the styling requirements.

© 2023 - smerth.com - All Rights Reserved