Skip to content

Commit

Permalink
Feat(web): Introduce Vertical Navigation #DS-1627
Browse files Browse the repository at this point in the history
  • Loading branch information
crishpeen committed Jan 28, 2025
1 parent 0db1de3 commit c04d9bf
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 54 deletions.
19 changes: 15 additions & 4 deletions packages/web/src/scss/components/Navigation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,17 @@ It consists of these parts:

The `Navigation` is a `nav` wrapper for lists of actions or other navigation components.

The `Navigation` component can be horizontal or vertical.

```html
<nav class="Navigation" aria-label="Main Navigation">
<nav class="Navigation Navigation--horizontal" aria-label="Horizontal Main Navigation">
<ul>
<li>
<!-- action -->
</li>
</ul>
</nav>
<nav class="Navigation Navigation--vertical" aria-label="Vertical Main Navigation">
<ul>
<li>
<!-- action -->
Expand Down Expand Up @@ -66,10 +75,12 @@ The `NavigationAction` is component that is styled to be used as a navigation ac
<a class="NavigationAction" href="#">Link</a>
```

It can obtain `selected` or `disabled` states by adding the respective classes.
It can obtain `selected` or `disabled` states by adding the respective classes. The selected visual state
is also turned on by the `aria-expanded` attribute.

```html
<a class="NavigationAction NavigationAction--selected" href="#" aria-current="page">Selected Link</a>
<button class="NavigationAction" type="button" aria-expanded="true">Expanded Aria Button</button>
<span class="NavigationAction NavigationAction--disabled">Disabled Link</span>
```

Expand All @@ -85,7 +96,7 @@ inherit the height of the `Header`.
With NavigationAction components:

```html
<nav class="Navigation" aria-label="Main Navigation">
<nav class="Navigation Navigation--horizontal" aria-label="Main Navigation">
<ul>
<li class="NavigationItem NavigationItem--alignmentYCenter">
<a class="NavigationAction NavigationAction--selected" href="#" aria-current="page">Selected Link</a>
Expand All @@ -103,7 +114,7 @@ With NavigationAction components:
With Buttons:

```html
<nav class="Navigation" aria-label="Secondary Navigation">
<nav class="Navigation Navigation--horizontal" aria-label="Secondary Navigation">
<ul>
<li class="NavigationItem NavigationItem--alignmentYCenter">
<a href="#" class="Button Button--medium Button--primary">Button</a>
Expand Down
17 changes: 13 additions & 4 deletions packages/web/src/scss/components/Navigation/_Navigation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@
@use '../../tools/reset';
@use 'theme';

.Navigation,
.Navigation > ul {
.Navigation--horizontal,
.Navigation--horizontal > ul {
display: flex;
}

.Navigation:has(.NavigationAction) {
height: var(--#{tokens.$css-variable-prefix}navigation-height, theme.$height);
.Navigation--vertical,
.Navigation--vertical > ul {
display: grid;
}

.Navigation--horizontal:has(.NavigationAction) {
height: var(--#{tokens.$css-variable-prefix}navigation-height, theme.$horizontal-height);
}

.Navigation:not(:has(.NavigationAction)),
.Navigation:not(:has(.NavigationAction)) > ul {
gap: theme.$spacing;
}

.Navigation--vertical:has(.NavigationAction) > ul {
gap: theme.$spacing-dense;
}

.Navigation > ul {
@include reset.list();
}
98 changes: 77 additions & 21 deletions packages/web/src/scss/components/Navigation/_NavigationAction.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 1. We need to set the top border too, otherwise the content won't be aligned properly.
// 1. We want specific typography for nested items in the Navigation component.
// 2. In the vertical Navigation component, the stripe is placed outside the NavigationAction element,
// so we need to disable pointer events on it.
// 3. We want the selected and expanded states to override the hover and active states.

@use '@tokens' as tokens;
@use '../../tools/reset';
@use '../../tools/typography';
@use 'theme';
Expand All @@ -10,53 +12,107 @@
}

.NavigationAction {
@include typography.generate(theme.$action-typography);

position: relative;
display: flex;
gap: theme.$action-gap;
align-items: center;
justify-content: center;
padding-inline: theme.$action-spacing;
color: theme.$action-color-state-default;
border-top: theme.$action-border-bottom-width theme.$action-border-bottom-style transparent; // 1.
border-bottom: theme.$action-border-bottom-width theme.$action-border-bottom-style
theme.$action-border-bottom-color-state-default;
background-color: theme.$action-background-color-state-default;

&::after {
content: '';
position: absolute;
background-color: theme.$action-stripe-background-color-state-default;
}

@media (hover: hover) {
&:hover {
text-decoration: none;
color: theme.$action-color-state-hover;
border-bottom-color: theme.$action-border-bottom-color-state-hover;
background-color: theme.$action-background-color-state-hover;
}
}

&:active {
text-decoration: none;
color: theme.$action-color-state-active;
border-bottom-color: theme.$action-border-bottom-color-state-active;
background-color: theme.$action-background-color-state-active;
}
}

.NavigationAction--selected {
// stylelint-disable-next-line selector-max-compound-selectors, selector-max-specificity -- 1.
.Navigation--vertical
> ul
ul
.NavigationAction:not(:is(.NavigationAction--selected, .NavigationAction[aria-expanded='true'])) {
@include typography.generate(theme.$vertical-action-typography-nested);
}

.NavigationAction--selected,
.NavigationAction[aria-expanded='true'] {
color: theme.$action-color-state-selected;
border-bottom-color: theme.$action-border-bottom-color-selected-state-default;
background-color: theme.$action-background-color-state-selected;

@media (hover: hover) {
&:hover {
border-bottom-color: theme.$action-border-bottom-color-selected-state-hover;
}
}

&:active {
border-bottom-color: theme.$action-border-bottom-color-selected-state-active;
&::after {
background-color: theme.$action-stripe-background-color-selected-state-default;
}
}

.NavigationAction--disabled {
color: theme.$action-color-state-disabled;
pointer-events: none;
}

.Navigation--horizontal .NavigationAction {
@include typography.generate(theme.$horizontal-action-typography);

justify-content: center;
padding-inline: theme.$action-spacing-dense;

&::after {
inset-inline: 0;
bottom: 0;
height: theme.$horizontal-action-stripe-size;
}
}

.Navigation--vertical .NavigationAction {
@include typography.generate(theme.$vertical-action-typography);

justify-content: space-between;
padding-inline: theme.$action-spacing;
padding-block: theme.$action-spacing-dense;
border-radius: theme.$vertical-action-border-radius;

&::after {
inset-block: 0;
left: calc(-1 * #{theme.$spacing});
width: theme.$vertical-action-stripe-size;
pointer-events: none; // 2.
}
}

@media (hover: hover) {
.Navigation--horizontal .NavigationAction:hover::after {
background-color: theme.$horizontal-action-stripe-background-color-state-hover;
}
}

.Navigation--horizontal .NavigationAction:active::after {
background-color: theme.$horizontal-action-stripe-background-color-state-active;
}

// stylelint-disable selector-max-specificity -- 3.
.Navigation--horizontal .NavigationAction--selected,
.Navigation--horizontal .NavigationAction[aria-expanded='true'] {
@media (hover: hover) {
&:hover::after {
background-color: theme.$action-stripe-background-color-selected-state-hover;
}
}

&:active::after {
background-color: theme.$action-stripe-background-color-selected-state-active;
}
}
// stylelint-enable selector-max-specificity
20 changes: 15 additions & 5 deletions packages/web/src/scss/components/Navigation/_NavigationItem.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
@use '@tokens' as tokens;
// 1. We don't want to add inline padding to nested NavigationItems

@use '../../tools/dictionaries';
@use 'theme';

.NavigationItem {
display: flex;
}

@include dictionaries.generate-alignments(
$class-name: 'NavigationItem',
$dictionary-values: theme.$item-alignment-dictionary,
Expand All @@ -15,3 +12,16 @@
.NavigationItem:has(.NavigationAction) {
align-items: stretch;
}

.Navigation--horizontal .NavigationItem {
display: flex;
}

.Navigation--vertical .NavigationItem {
display: grid;

// stylelint-disable-next-line selector-max-class, selector-max-specificity -- 1.
&:not(& .NavigationItem) {
padding-inline: theme.$spacing;
}
}
38 changes: 25 additions & 13 deletions packages/web/src/scss/components/Navigation/_theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,40 @@

@use '@tokens' as tokens;

$spacing-dense: tokens.$space-400;
$spacing: tokens.$space-700;
$height: 50px;

$action-typography: tokens.$body-small-semibold;
$action-gap: tokens.$space-600;
$action-spacing: tokens.$space-600;
$horizontal-height: 50px;

$item-alignment-dictionary: center, stretch; // 1.

$horizontal-action-typography: tokens.$body-small-semibold;
$vertical-action-typography: tokens.$body-medium-semibold;
$vertical-action-typography-nested: tokens.$body-medium-regular;
$action-gap: tokens.$space-700;
$action-spacing-dense: tokens.$space-600;
$action-spacing: tokens.$space-700;

$action-color-state-default: tokens.$component-header-item-state-default;
$action-color-state-hover: tokens.$component-header-item-state-hover;
$action-color-state-active: tokens.$component-header-item-state-active;
$action-color-state-selected: tokens.$component-header-item-state-selected;
$action-color-state-disabled: tokens.$disabled-content;
$action-border-bottom-width: tokens.$border-width-200;
$action-border-bottom-style: solid;
$action-border-bottom-color-state-default: transparent;
$action-border-bottom-color-state-hover: tokens.$component-header-stripe-state-unselected;
$action-border-bottom-color-state-active: tokens.$component-header-stripe-state-unselected;
$action-border-bottom-color-selected-state-default: tokens.$component-header-stripe-state-selected;
$action-border-bottom-color-selected-state-hover: tokens.$component-header-stripe-state-selected;
$action-border-bottom-color-selected-state-active: tokens.$component-header-stripe-state-selected;

$vertical-action-border-radius: tokens.$radius-200;

$action-background-color-state-default: tokens.$component-header-item-background-state-default;
$action-background-color-state-hover: tokens.$component-header-item-background-state-hover;
$action-background-color-state-active: tokens.$component-header-item-background-state-active;
$action-background-color-state-selected: tokens.$component-header-item-background-state-selected;

$item-alignment-dictionary: center, stretch; // 1.
$action-stripe-background-color-state-default: transparent;
$action-stripe-background-color-selected-state-default: tokens.$component-header-stripe-state-selected;
$action-stripe-background-color-selected-state-hover: tokens.$component-header-stripe-state-selected;
$action-stripe-background-color-selected-state-active: tokens.$component-header-stripe-state-selected;

$horizontal-action-stripe-background-color-state-hover: tokens.$component-header-stripe-state-unselected;
$horizontal-action-stripe-background-color-state-active: tokens.$component-header-stripe-state-unselected;
$horizontal-action-stripe-size: 2px;

$vertical-action-stripe-size: 4px;
Loading

0 comments on commit c04d9bf

Please sign in to comment.