Skip to content

Commit

Permalink
feat: ForgeLayout (#1545)
Browse files Browse the repository at this point in the history
## πŸ“ Changes

- Adds ForgeLayout

## βœ… Checklist

Easy UI has certain UX standards that must be met. In general,
non-trivial changes should meet the following criteria:

- [x] Visuals match Design Specs in Figma
- [x] Stories accompany any component changes
- [x] Code is in accordance with our style guide
- [x] Design tokens are utilized
- [x] Unit tests accompany any component changes
- [x] TSDoc is written for any API surface area
- [x] Specs are up-to-date
- [x] Console is free from warnings
- [x] No accessibility violations are reported
- [x] Cross-browser check is performed (Chrome, Safari, Firefox)
- [x] Changeset is added

~Strikethrough~ any items that are not applicable to this pull request.
  • Loading branch information
stephenjwatkins authored Dec 19, 2024
1 parent b6cf8ae commit 72816da
Show file tree
Hide file tree
Showing 18 changed files with 1,301 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/tasty-news-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@easypost/easy-ui-icons": minor
"@easypost/easy-ui": minor
---

feat: ForgeLayout
120 changes: 120 additions & 0 deletions documentation/specs/ForgeLayout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# `ForgeLayout` Component Specification

## Overview

`ForgeLayout` defines the header, nav, and main content areas of a Forge product page.

### Prior Art

- [Primer `<PageLayout />`](https://primer.style/design/components/page-layout/react)
- [Paste `<SidebarNavigation /`>](https://paste.twilio.design/components/sidebar-navigation)

---

## Design

`ForgeLayout` will be a compound component consisting of `ForgeLayout`, `ForgeLayout.Nav`, `ForgeLayout.Header`, and `ForgeLayout.Content`.

`ForgeLayout` is highly composable. Subcomponents within a `ForgeLayout` can be replaced as needed. Subcomponents are lightweight wrappers with built-in styles and constraints.

`ForgeLayout` is concerned only with presentational structure. It is meant to be wrapped by an app layout that may include app-specific business logic and configuration.

`ForgeLayout` can be in an `expanded` or `collapsed` navigational state by using the `navState` prop. When `expanded`, the navigation is present, along with any relevant header controls. When `collapsed`, the navigation is hidden, and the relevant controls are presented in the header.

`ForgeLayout` is aware of a global `mode` prop. When passed `test`, the shell is decorated with a color to indicate a non-production environment. The `mode` can be changed with the `ModeSwitcher` control.

### API

```tsx
import { ForgeLayout } from "@easypost/easy-ui/ForgeLayout";

function App() {
return (
<ForgeLayout mode="test" navState="expanded">
<ForgeLayout.Nav>
<ForgeLayout.NavLink href="/1" iconSymbol={Icon}>
Item 1
</ForgeLayout.NavLink>
<ForgeLayout.NavSection title={<>Title</>}>
<ForgeLayout.NavLink href="/2" iconSymbol={Icon}>
Item 2
</ForgeLayout.NavLink>
<ForgeLayout.NavLink href="/3" iconSymbol={Icon}>
Item 3
</ForgeLayout.NavLink>
</ForgeLayout.NavSection>
<ForgeLayout.NavSection title={<>Title</>}>
<ForgeLayout.NavLink href="/4" iconSymbol={Icon}>
Item 4
</ForgeLayout.NavLink>
<ForgeLayout.NavLink href="/5" iconSymbol={Icon}>
Item 5
</ForgeLayout.NavLink>
</ForgeLayout.NavSection>
</ForgeLayout.Nav>
<ForgeLayout.Header>
<ForgeLayout.Controls visibleWhenNavStateIs="collapsed">
<ForgeLayout.BreadrumbsNavigation>
<ForgeLayout.BackButton onClick={() => {}}>
Back
</ForgeLayout.BackButton>
<ForgeLayout.Breadrumbs>
<ForgeLayout.Breadrumb>Breadcrumb</ForgeLayout.Breadrumb>
<ForgeLayout.Breadrumb>Breadcrumb</ForgeLayout.Breadrumb>
</ForgeLayout.Breadrumbs>
</ForgeLayout.BreadrumbsNavigation>
</ForgeLayout.Controls>
<ForgeLayout.Controls visibleWhenNavStateIs="expanded">
<ForgeLayout.ModeSwitcher onModeChange={action("Mode changed!")} />
<ForgeLayout.Search value={"search"} onChange={() => {}} />
</ForgeLayout.Controls>
<ForgeLayout.Actions>
<ForgeLayout.MenuAction
accessibilityLabel="Action 1"
iconSymbol={AlarmIcon}
renderBadge={() => <ForgeLayout.ActionBadge />}
>
<Menu.Overlay onAction={action("Menu item clicked!")}>
<Menu.Item>Action 1:1</Menu.Item>
<Menu.Item>Action 1:2</Menu.Item>
</Menu.Overlay>
</ForgeLayout.MenuAction>
<ForgeLayout.MenuAction
accessibilityLabel="Action 2"
iconSymbol={SupportIcon}
>
<Menu.Overlay onAction={action("Menu item clicked!")}>
<Menu.Item>Action 2:1</Menu.Item>
<Menu.Item>Action 2:2</Menu.Item>
</Menu.Overlay>
</ForgeLayout.MenuAction>
<ForgeLayout.LinkAction
href="/4"
accessibilityLabel="Action 3"
iconSymbol={SettingsIcon}
/>
</ForgeLayout.Actions>
</ForgeLayout.Header>
<ForgeLayout.Content>Page Content</ForgeLayout.Content>
</ForgeLayout>
);
}
```

---

## Behavior

### Accessibility

- `ForgeLayout.Header` will render as `header`
- `ForgeLayout.Content` will render as `main`
- `ForgeLayout.Nav` will be rendered as `nav` with associated `aria-label`
- `ForgeLayout.NavLink` will render as `<a>`
- Selected nav links will be decorated as `aria-current="page"`

### Dependencies

- `Text`
- `useLink`
- Will use `EasyUIProvider`'s navigation hooks to support client-side links. See [client side routing](https://react-spectrum.adobe.com/react-aria/routing.html#routerprovider). This was added as part of `NexusLayout`.
5 changes: 5 additions & 0 deletions easy-ui-icons/src/AccountTree.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "account_tree",
"style": "outlined",
"source": "@material-symbols/svg-300"
}
5 changes: 5 additions & 0 deletions easy-ui-icons/src/DoorOpen.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "door_open",
"style": "outlined",
"source": "@material-symbols/svg-300"
}
5 changes: 5 additions & 0 deletions easy-ui-icons/src/Key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "key",
"style": "outlined",
"source": "@material-symbols/svg-300"
}
5 changes: 5 additions & 0 deletions easy-ui-icons/src/Shield.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "shield",
"style": "outlined",
"source": "@material-symbols/svg-300"
}
5 changes: 5 additions & 0 deletions easy-ui-icons/src/ViewList.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "view_list",
"style": "outlined",
"source": "@material-symbols/svg-300"
}
5 changes: 5 additions & 0 deletions easy-ui-icons/src/Widgets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "widgets",
"style": "outlined",
"source": "@material-symbols/svg-300"
}
40 changes: 40 additions & 0 deletions easy-ui-react/src/ForgeLayout/ForgeLayout.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import { Canvas, Meta, ArgTypes, Controls } from "@storybook/blocks";
import { ForgeLayout } from "./ForgeLayout";
import * as ForgeLayoutStories from "./ForgeLayout.stories";

<Meta of={ForgeLayoutStories} />

# ForgeLayout

`ForgeLayout` defines the header, main content, and multipage content areas of a Nexus product page.

<Canvas of={ForgeLayoutStories.Default} />

`ForgeLayout` is a compound component consisting of `ForgeLayout`, `ForgeLayout.Nav`, `ForgeLayout.Header`, and `ForgeLayout.Content`.

`ForgeLayout` also provides components for building navigation with `ForgeLayout.Nav`, `ForgeLayout.NavLink`, and `ForgeLayout.NavSection`.

`ForgeLayout` is highly composable. Subcomponents within a `ForgeLayout` can be replaced as needed. Subcomponents are simple lightweight wrappers with built-in styles and constraints.

## Test Mode

`ForgeLayout` supports an obvious visual indicator for test mode with the `mode="test"` prop.

<Canvas of={ForgeLayoutStories.TestMode} />

<Controls of={ForgeLayoutStories.TestMode} include={["mode"]} />

## Collapsed Navigation

`ForgeLayout` supports a distraction-free content mode with the `navState="collapsed"` prop.

<Canvas of={ForgeLayoutStories.Collapsed} />

<Controls of={ForgeLayoutStories.Collapsed} include={["navState"]} />

## Properties

### ForgeLayout

<ArgTypes of={ForgeLayout} />
100 changes: 100 additions & 0 deletions easy-ui-react/src/ForgeLayout/ForgeLayout.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
@use "../styles/common" as *;
@use "../styles/unstyled";

.ForgeLayout {
@include component-token("forge-layout", "header-height", 56px);
@include component-token("forge-layout", "shell-gutter", 20px);
@include component-token("forge-layout", "menu-border-color", transparent);
@include component-token(
"forge-layout",
"header-border-color",
design-token("color.neutral.300")
);
@include component-token("forge-layout", "header-border-width", 1px);

display: flex;
flex-direction: row;
align-items: flex-start;
padding-left: component-token("forge-layout", "shell-gutter");
padding-right: component-token("forge-layout", "shell-gutter");
gap: component-token("forge-layout", "shell-gutter");
min-height: 100svh;
background-color: design-token("color.neutral.025");
position: relative;
}

.modeTest {
@include component-token(
"forge-layout",
"menu-border-color",
design-token("color.warning.600")
);
@include component-token(
"forge-layout",
"header-border-color",
design-token("color.warning.600")
);
@include component-token("forge-layout", "header-border-width", 2px);
}

.backgroundDecoration01 {
background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzA5IiBoZWlnaHQ9IjI5NSIgdmlld0JveD0iMCAwIDMwOSAyOTUiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik00NC4xMTc1IDIyOC41NjdDMzQuNjg2OSAyMjYuOTU3IDI3LjcwMjEgMjE4LjkxOCAyNy40MjQgMjA5LjM1N0wyNS41MDk2IDE0My41NzNDMjUuMzM2NSAxMzcuNTU5IDI3Ljg2OTkgMTMxLjc4IDMyLjQxMTIgMTI3LjgzNEw2MS41MjM5IDEwMi41MzRDNjYuMDY1MiA5OC41ODc5IDcyLjEzOTggOTYuODg0NSA3OC4wNzQ4IDk3Ljg5NzhMMTA2LjE4NCAxMDIuNjk4TDExNi4zNjYgMTU0LjMzNkMxMTcuNzQ0IDE2MS4zMzQgMTIyLjczMyAxNjcuMDc2IDEyOS40NzMgMTY5LjQxN0wxNjEuMTU5IDE4MC40MzdMMTYxLjU1MiAxOTMuOTY3QzE2MS43MjkgMTk5Ljk4MiAxNTkuMTk1IDIwNS43NjEgMTU0LjY1NCAyMDkuNzA3TDEyNS41NDEgMjM1LjAwN0MxMjAuOTk3IDIzOC45NTIgMTE0LjkyMiAyNDAuNjU1IDEwOC45OTEgMjM5LjY0M0w0NC4xMTc1IDIyOC41NjdaIiBmaWxsPSIjRkI1QzU5Ii8+CjxwYXRoIGQ9Ik0yMDQuNjczIDE4NC44ODZDMTk5LjI0NCAxODkuNjAzIDE5MS43MDUgMTkxLjA1NSAxODQuOTEgMTg4LjY5NkwxNjEuMTU5IDE4MC40MzdMMTU5LjYzOCAxMjguMTgzQzE1OS4zNiAxMTguNjIyIDE1Mi4zNzkgMTEwLjU4NSAxNDIuOTQ4IDEwOC45NzRMMTA2LjE4NCAxMDIuNjk4TDEwNS4wMTMgOTYuNzUzM0MxMDMuNjIyIDg5LjY5NDIgMTA2LjExMiA4Mi40MzI2IDExMS41NDYgNzcuNzEzM0wxNTYuMDY0IDM5LjAyNzVDMTYxLjQ5MyAzNC4zMTA1IDE2OS4wMzIgMzIuODU3NyAxNzUuODI3IDM1LjIxNzZMMjMxLjI2NCA1NC40OTU3QzIzOC4wMDQgNTYuODM2OSAyNDIuOTkyIDYyLjU3OTMgMjQ0LjM3IDY5LjU3NzFMMjUyLjUzNSAxMTAuOTc4TDI0Ni40MzEgMTA2LjAwOEMyNDIuNjQzIDEwMi45MjQgMjM3LjE4OSAxMDMuMDE0IDIzMy41MDMgMTA2LjIxNUwyMDguOTM5IDEyNy41NjFDMjA1LjI1MyAxMzAuNzYyIDIwNC40MDggMTM2LjE1MiAyMDYuOTMyIDE0MC4zMjlMMjIwLjI5MyAxNjIuNDQ2QzIyMC44ODkgMTYzLjQzMiAyMjEuNjUzIDE2NC4zMSAyMjIuNTQ3IDE2NS4wMzlMMjI1LjEwOCAxNjcuMTI3TDIwNC42NzMgMTg0Ljg4NloiIGZpbGw9IiMxNjRERkYiLz4KPHBhdGggZD0iTTE2MS4xNTkgMTgwLjQzN0wxMjkuNDczIDE2OS40MTdDMTIyLjczMyAxNjcuMDc2IDExNy43NDQgMTYxLjMzNCAxMTYuMzY3IDE1NC4zMzZMMTA2LjE4NCAxMDIuNjk4TDE0Mi45NDggMTA4Ljk3NEMxNTIuMzc5IDExMC41ODQgMTU5LjM2IDExOC42MjIgMTU5LjYzOCAxMjguMTgzTDE2MS4xNTkgMTgwLjQzN1oiIGZpbGw9IiNGRURFREUiLz4KPHBhdGggZD0iTTI1NS41MDYgMTgxLjE0NkMyNTEuODIgMTg0LjM0OCAyNDYuMzYzIDE4NC40MzYgMjQyLjU3OCAxODEuMzUzTDIyNS4xMDggMTY3LjEyN0wyNDkuMTkxIDE0Ni4yQzI1NC42MjQgMTQxLjQ4IDI1Ny4xMTUgMTM0LjIxOSAyNTUuNzI0IDEyNy4xNkwyNTIuNTM1IDExMC45NzhMMjY2LjQ2MiAxMjIuMzIyQzI2Ny4zNTYgMTIzLjA1MSAyNjguMTIgMTIzLjkyOSAyNjguNzE2IDEyNC45MTZMMjgyLjA3NiAxNDcuMDMyQzI4NC42MDEgMTUxLjIwOSAyODMuNzUzIDE1Ni41OTggMjgwLjA2NyAxNTkuNzk5TDI1NS41MDYgMTgxLjE0NlYxODEuMTQ2WiIgZmlsbD0iIzAwRTVBRSIvPgo8cGF0aCBkPSJNMjI1LjEwOCAxNjcuMTI3TDIyMi41NDcgMTY1LjAzOUMyMjEuNjUzIDE2NC4zMSAyMjAuODg5IDE2My40MzIgMjIwLjI5MyAxNjIuNDQ1TDIwNi45MzIgMTQwLjMyOUMyMDQuNDA4IDEzNi4xNTIgMjA1LjI1MyAxMzAuNzYyIDIwOC45MzkgMTI3LjU2MUwyMzMuNTAzIDEwNi4yMTVDMjM3LjE4OSAxMDMuMDE0IDI0Mi42NDMgMTAyLjkyNCAyNDYuNDMxIDEwNi4wMDhMMjUyLjUzNSAxMTAuOTc4TDI1NS43MjQgMTI3LjE2QzI1Ny4xMTUgMTM0LjIxOSAyNTQuNjI0IDE0MS40OCAyNDkuMTkxIDE0Ni4yTDIyNS4xMDggMTY3LjEyN1YxNjcuMTI3WiIgZmlsbD0iI0ZGRjlGNSIvPgo8L3N2Zz4K"),
url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzEyIiBoZWlnaHQ9IjIyMyIgdmlld0JveD0iMCAwIDMxMiAyMjMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yNzAuNDE1IDEwLjI3MTNDMjc0LjU0NCAxMS4zOSAyNzcuNzcxIDE0LjYxMzYgMjc4Ljg5IDE4Ljc0NjJMMjg4LjA5OCA1Mi43MjlDMjg5LjIzMSA1Ni44OTMgMjg4LjA0MSA2MS4zNDYgMjg0Ljk5IDY0LjM5OUwyNzMuOTUxIDc1LjQzN0wyNTYuMDQyIDUzLjU3MkMyNTEuNDcgNDcuOTg0OSAyNDQuNjI4IDQ0Ljc0NyAyMzcuNDA2IDQ0Ljc0ODdMMTk3LjY4OCA0NC43NDQ3TDE5Ni42MzUgNDAuODU2N0MxOTUuNTA2IDM2LjY4OTkgMTk2LjY5MiAzMi4yMzk2IDE5OS43NDMgMjkuMTg2NkwyMjQuNzYzIDQuMTcwNDFDMjI3LjgxNCAxLjExNzQxIDIzMi4yNjYgLTAuMDY3MjkxOSAyMzYuNDMgMS4wNjA5MUwyNzAuNDE1IDEwLjI3MTNaIiBmaWxsPSIjRkI1QzU5Ii8+CjxwYXRoIGQ9Ik0zMDYuMDg0IDExNC42NThDMzEzLjM1NSAxMjMuNTM1IDMxMy4zNTUgMTM2LjMxMiAzMDYuMDgyIDE0NS4xOUwyNTYuMDQ1IDIwNi4yNzRDMjUxLjQ3MSAyMTEuODYxIDI0NC42MjggMjE1LjEwMSAyMzcuNDA4IDIxNS4wOTdMMTkxLjEzMSAyMTUuMDk5QzE4My45MTMgMjE1LjA5OCAxNzcuMDcxIDIxMS44NjEgMTcyLjQ5NSAyMDYuMjc2TDE1NS41ODIgMTg1LjYyOUMxNTYuODc1IDE4My43NTUgMTU4LjA5MyAxODEuODIgMTU5LjIzNSAxNzkuODI0QzE3NC4yODkgMTUzLjU3NyAxNzQuMjg4IDEyMS4zMTggMTU5LjIzNSA5NS4wNzJDMTU2LjQ0IDkwLjE5OCAxNTMuMjA2IDg1LjY3MSAxNDkuNTk2IDgxLjUyNkwxNzIuNDkzIDUzLjU3M0MxNzcuMDY2IDQ3Ljk4NjcgMTgzLjkwNSA0NC43NDg3IDE5MS4xMjcgNDQuNzQ2OUwxOTcuNjg0IDQ0Ljc0NjZMMjA1LjgzOSA3NC44NDFDMjA2Ljk2MiA3OC45NzIgMjEwLjE4NiA4Mi4xOTcgMjE0LjMxNiA4My4zMkwyNDguMyA5Mi41MjdDMjUyLjQ2OSA5My42NTYgMjU2LjkxNiA5Mi40NyAyNTkuOTY5IDg5LjQyMUwyNzMuOTQ3IDc1LjQzOUwzMDYuMDggMTE0LjY2TDMwNi4wODQgMTE0LjY1OFoiIGZpbGw9IiMxNjRERkYiLz4KPHBhdGggZD0iTTI1Ni4wNDIgNTMuNTcyTDI3My45NTEgNzUuNDM3TDI1OS45NzIgODkuNDE5QzI1Ni45MTkgOTIuNDY4IDI1Mi40NzMgOTMuNjU1IDI0OC4zMDMgOTIuNTI1TDIxNC4zMiA4My4zMThDMjEwLjE4OSA4Mi4xOTYgMjA2Ljk2NiA3OC45NyAyMDUuODQzIDc0Ljg0TDE5Ny42ODggNDQuNzQ0OUwyMzcuNDA2IDQ0Ljc0OUMyNDQuNjI4IDQ0Ljc0NzIgMjUxLjQ3IDQ3Ljk4NTEgMjU2LjA0MiA1My41NzJaIiBmaWxsPSIjRkVERURFIi8+CjxwYXRoIGQ9Ik0xNTkuMjM2IDk1LjA2N0MxNzQuMjkyIDEyMS4zMTYgMTc0LjI5MyAxNTMuNTc1IDE1OS4yMzkgMTc5LjgyMkMxNTguMDk2IDE4MS44MTggMTU2Ljg3OCAxODMuNzUzIDE1NS41ODYgMTg1LjYyN0wxMjIuNDYxIDE0NS4xODZDMTE1LjE5IDEzNi4zMDkgMTE1LjE5IDEyMy41MzIgMTIyLjQ2MyAxMTQuNjUzTDE0OS42MDMgODEuNTIzQzE1My4yMTMgODUuNjY4IDE1Ni40NDcgOTAuMTk0IDE1OS4yNCA5NS4wNjVMMTU5LjIzNiA5NS4wNjdaIiBmaWxsPSIjQ0NGQUVGIi8+CjxwYXRoIGQ9Ik0xNDkuNTk5IDgxLjUyNUwxMjIuNDU5IDExNC42NTZDMTE1LjE4NiAxMjMuNTM0IDExNS4xODYgMTM2LjMxMSAxMjIuNDU3IDE0NS4xODhMMTU1LjU4MiAxODUuNjI5QzEzOS43ODkgMjA4LjY2MiAxMTMuNTY5IDIyMi42MjMgODUuMzU3IDIyMi42MjJMODUuMzUzIDIyMi42MjRDNTQuODM2NiAyMjIuNjIzIDI2LjY1MzIgMjA2LjI5NyAxMS40NjgyIDE3OS44MjRDMy45NDI0IDE2Ni43MDMgMC4xNzczMDIgMTUyLjA3NiAwLjE3OTkwMiAxMzcuNDQ1QzAuMTc4ODAyIDEyMi44MTYgMy45NDAyIDEwOC4xOTEgMTEuNDY5MiA5NS4wNjZDMTkuMDgxOCA4MS43OTQgMjkuOTYwMyA3MS4wNzIgNDIuNzY1NiA2My42NzlDNTUuNTcwOCA1Ni4yODYgNzAuMTM2IDUyLjI2OCA4NS4zNTMgNTIuMjdDMTEwLjI1NSA1Mi4yNjkgMTMzLjYwMyA2My4xMzYgMTQ5LjU5OSA4MS41MjVaIiBmaWxsPSIjMDBFNUFFIi8+Cjwvc3ZnPgo=");
background-repeat: no-repeat;
background-position:
-40px -90px,
calc(100% + 24px) calc(100% + 24px);
}

.body {
flex: 1;
display: flex;
flex-direction: column;
}

.header {
display: flex;
flex-wrap: nowrap;
align-items: center;
justify-content: space-between;
min-width: 0;
border-bottom: 2px solid transparent;
z-index: calc(#{design-token("z-index.nav")} + 1);
}

.content {
padding-top: component-token("forge-layout", "shell-gutter");
padding-bottom: component-token("forge-layout", "shell-gutter");
}

.controls {
display: flex;
align-items: center;
gap: design-token("space.2");
}

.fauxContainer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;

display: flex;
flex-direction: column;
pointer-events: none;
}

.fauxHeader {
background-color: design-token("color.neutral.025");
border-bottom: component-token("forge-layout", "header-border-width") solid
component-token("forge-layout", "header-border-color");
z-index: design-token("z-index.nav");
}

.fauxHeader,
.header {
position: sticky;
top: 0;
height: component-token("forge-layout", "header-height");
}
Loading

0 comments on commit 72816da

Please sign in to comment.