diff --git a/packages/components/src/components/post-header/post-header.scss b/packages/components/src/components/post-header/post-header.scss index 8bea0377cf..933157d216 100644 --- a/packages/components/src/components/post-header/post-header.scss +++ b/packages/components/src/components/post-header/post-header.scss @@ -9,11 +9,21 @@ :host { --global-header-height: 72px; + --global-header-minimal-height: 24px; --main-header-height: 56px; --header-height: calc(var(--global-header-height) + var(--main-header-height)); + @include media.min(lg) { + display: block; + position: sticky; + inset-inline: 0; + inset-block-start: calc(-1 * (var(--global-header-height) + var(--main-header-height) - var(--global-header-minimal-height))); + box-shadow: var(--post-core-elevation-3); + } + @include media.max(lg) { --global-header-height: 64px; + --main-header-height: 48px; } } @@ -31,18 +41,17 @@ justify-content: space-between; align-items: center; position: sticky; - z-index: 2; - padding-inline-start: 4px; - padding-inline-end: 12px; - + padding-inline: var(--post-core-dimension-4); height: var(--global-header-height); + z-index: 1; @include media.max(lg) { - top: 0; + inset-block-start: 0; } @include media.min(lg) { - top: calc((var(--global-header-height) - 24px) * -1); + padding-inline-end: var(--post-core-dimension-12); + top: calc((var(--global-header-height) - var(--global-header-minimal-height)) * -1); } } @@ -53,7 +62,7 @@ slot[name='post-logo'] { .global-sub { display: flex; align-items: center; - gap: 2rem; + gap: var(--post-core-dimension-24); height: var(--global-header-height); } @@ -65,7 +74,7 @@ slot[name='post-logo'] { flex: 1 0 auto; height: var(--global-header-height); width: var(--global-header-height); - min-height: 24px; + min-height: var(--global-header-minimal-height); align-self: flex-end; @include media.min(lg) { @@ -82,25 +91,38 @@ slot[name='post-logo'] { } .title-header { - position: relative; - z-index: 1; display: flex; align-items: center; - padding-inline: 12px; - background: white; + gap: var(--post-core-dimension-4); height: var(--main-header-height); + background: var(--post-core-color-brand-white); + + @include media.min(lg) { + padding: var(--post-core-dimension-18) var(--post-core-dimension-16) var(--post-core-dimension-4) var(--post-core-dimension-12); + } @include media.max(lg) { - border-bottom: 1px solid black; + position: sticky; + z-index: 1; + inset-block-start: var(--global-header-height); + padding-inline: var(--post-core-dimension-8) var(--post-core-dimension-16); } } + :host(:not(:has([slot='title']))) .title-header { display: none; } ::slotted(h1) { margin: 0 !important; - font-size: 28px !important; + + @include media.min(lg) { + font-size: var(--post-core-font-size-28) !important; + } + + @include media.max(lg) { + font-size: var(--post-core-font-size-20) !important; + } } .mobile-toggle { @@ -111,36 +133,35 @@ slot[name='post-logo'] { .navigation { background: var(--post-core-color-brand-white); - box-shadow: var(--post-core-elevation-3); + + @include media.min(lg) { + position: sticky; + z-index: 1; + inset-block-start: var(--global-header-minimal-height); + } } // only for tablet and mobile @include media.max(lg) { .navigation { - position: absolute; + position: fixed; inset-inline: 0; inset-block-end: calc(100vh - var(--header-height)); - transition: transform animation.$transition-time-area-medium animation.$transition-easing-accelerate; - - &.extended { - transform: translateY(100%); - transition-timing-function: animation.$transition-easing-decelerate; - } + box-shadow: var(--post-core-elevation-3); + min-height: var(--post-core-dimension-10); // needed for the box-shadow to always show + max-height: calc(100vh - var(--header-height)); + overflow: auto; } ::slotted(post-mainnavigation), .navigation-footer { - display: flex; + display: none; flex-direction: column; padding-block: var(--post-core-dimension-16) var(--post-core-dimension-24); padding-inline: var(--post-core-dimension-32); - opacity: 0; - transition-property: opacity; - transition-delay: animation.$transition-time-area-medium; .navigation.extended & { - opacity: 1; - transition-delay: 0s; + display: flex; } } diff --git a/packages/components/src/components/post-header/post-header.tsx b/packages/components/src/components/post-header/post-header.tsx index ac74a52518..01fa94263b 100644 --- a/packages/components/src/components/post-header/post-header.tsx +++ b/packages/components/src/components/post-header/post-header.tsx @@ -1,7 +1,8 @@ -import { Component, h, Host, State, Element, Method } from '@stencil/core'; +import { Component, h, Host, State, Element, Method, Watch } from '@stencil/core'; import { throttle } from 'throttle-debounce'; import { version } from '@root/package.json'; import { SwitchVariant } from '@/components'; +import { slideDown, slideUp } from '@/animations/slide'; type DEVICE_SIZE = 'mobile' | 'tablet' | 'desktop' | null; @@ -11,11 +12,8 @@ type DEVICE_SIZE = 'mobile' | 'tablet' | 'desktop' | null; styleUrl: './post-header.scss', }) export class PostHeader { - @Element() host: HTMLPostHeaderElement; - @State() device: DEVICE_SIZE = null; - @State() mobileMenuExtended: boolean = false; - private scrollParent = null; + private mobileMenu: HTMLElement; private throttledScroll = () => this.handleScrollEvent(); private throttledResize = throttle(50, () => this.handleResize()); @@ -27,14 +25,30 @@ export class PostHeader { this.handleScrollEvent(); } + @Element() host: HTMLPostHeaderElement; + + @State() device: DEVICE_SIZE = null; + @State() mobileMenuExtended: boolean = false; + + @Watch('mobileMenuExtended') + frozeBody(isMobileMenuExtended: boolean) { + document.body.style.overflow = isMobileMenuExtended ? 'hidden' : ''; + } + /** * Toggles the mobile navigation. */ @Method() async toggleMobileMenu() { - if (this.device !== 'desktop') { - this.mobileMenuExtended = !this.mobileMenuExtended; + if (this.device === 'desktop') return; + + if (this.mobileMenuExtended) { + await slideUp(this.mobileMenu).finished; + } else { + slideDown(this.mobileMenu); } + + this.mobileMenuExtended = !this.mobileMenuExtended; } private handleScrollEvent() { @@ -125,7 +139,6 @@ export class PostHeader { -
@@ -133,8 +146,7 @@ export class PostHeader {
- -
+
(this.mobileMenu = el)} class={navigationClasses.join(' ')}> {(this.device === 'mobile' || this.device === 'tablet') && ( diff --git a/packages/components/src/components/post-language-switch/post-language-switch.scss b/packages/components/src/components/post-language-switch/post-language-switch.scss index d4c9266c8a..f0cc236912 100644 --- a/packages/components/src/components/post-language-switch/post-language-switch.scss +++ b/packages/components/src/components/post-language-switch/post-language-switch.scss @@ -2,6 +2,7 @@ @use '@swisspost/design-system-styles/functions/tokens'; @use '@swisspost/design-system-styles/mixins/button' as button-mx; @use '@swisspost/design-system-styles/mixins/utilities' as utilities-mx; +@use '@swisspost/design-system-styles/components/header/mixins' as header-mx; tokens.$default-map: components.$post-button; @@ -16,25 +17,16 @@ tokens.$default-map: components.$post-button; .post-language-switch-trigger { cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - border-width: tokens.get('button-border-width'); - border-radius: tokens.get('button-border-radius-round'); - background-color: transparent; - font-family: inherit; - font-weight: tokens.get('button-label-font-weight'); - @include button-mx.button-size(sm); - - &:disabled { - border-style: tokens.get('button-border-style-disabled'); - @include button-mx.button-variant-def('disabled', 'tertiary'); - } + @include button-mx.reset-button; + @include header-mx.subsidiary-header-interactive; @include utilities-mx.focus-style; - @include button-mx.button-variant-def('enabled', 'tertiary'); - @include utilities-mx.not-disabled-hover() { @include button-mx.button-variant-def('hover', 'tertiary'); } + + post-icon { + height: var(--post-core-dimension-16); + width: var(--post-core-dimension-16); + } } diff --git a/packages/components/src/components/post-language-switch/post-language-switch.tsx b/packages/components/src/components/post-language-switch/post-language-switch.tsx index a34fe37267..c26cf8b7f8 100644 --- a/packages/components/src/components/post-language-switch/post-language-switch.tsx +++ b/packages/components/src/components/post-language-switch/post-language-switch.tsx @@ -130,7 +130,7 @@ export class PostLanguageSwitch { aria-label={`${this.caption}, ${this.description}`} > {this.activeLang.toUpperCase()} - + diff --git a/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss b/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss index 7ac5a14022..a29c54d64d 100644 --- a/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss +++ b/packages/components/src/components/post-mainnavigation/post-mainnavigation.scss @@ -1,11 +1,10 @@ @use '@swisspost/design-system-styles/mixins/button'; @use '@swisspost/design-system-styles/mixins/icons'; @use '@swisspost/design-system-styles/mixins/media'; -@use '@swisspost/design-system-styles/mixins/utilities'; -@use '@swisspost/design-system-styles/functions/icons' as icon-fn; @use '@swisspost/design-system-styles/functions/tokens'; @use '@swisspost/design-system-styles/tokens/elements'; @use '@swisspost/design-system-styles/variables/animation'; +@use '@swisspost/design-system-styles/components/header/mixins' as header-mx; $nav-height: var(--post-core-dimension-56); @@ -111,26 +110,7 @@ post-mainnavigation { post-list-item { a, button { - width: 100%; - height: var(--post-core-dimension-48); - padding-inline-end: var(--post-core-dimension-6); - gap: var(--post-core-dimension-16); - border-block: var(--post-core-dimension-1) solid transparent; - border-block-end-color: currentColor; - font-weight: var(--post-core-font-weight-700); - - &:hover, - &.selected { - border-block-width: var(--post-core-dimension-3); - } - - &:hover::after { - content: ''; - display: block; - @include icons.icon(3020); - width: var(--post-core-dimension-24); - height: var(--post-core-dimension-24); - } + @include header-mx.mobile-header-interactive; } } } diff --git a/packages/components/src/index.html b/packages/components/src/index.html index dfa6592340..112991f4e6 100644 --- a/packages/components/src/index.html +++ b/packages/components/src/index.html @@ -22,12 +22,16 @@ - = Menu + + Menu + + + Application title diff --git a/packages/documentation/src/stories/components/header/components/header.markup.ts b/packages/documentation/src/stories/components/header/components/header.markup.ts index 5c890e2916..37813dd19d 100644 --- a/packages/documentation/src/stories/components/header/components/header.markup.ts +++ b/packages/documentation/src/stories/components/header/components/header.markup.ts @@ -6,12 +6,16 @@ export default html` - = Menu + + Menu + + + diff --git a/packages/styles/src/components/_index.scss b/packages/styles/src/components/_index.scss index 2f9f104eca..54a7d43734 100644 --- a/packages/styles/src/components/_index.scss +++ b/packages/styles/src/components/_index.scss @@ -27,6 +27,7 @@ @use 'switch'; @use 'form-hint'; @use 'form-input'; +@use 'header'; @use 'icon-button'; @use 'icon-close-button'; @use 'lead'; diff --git a/packages/styles/src/components/header/_mixins.scss b/packages/styles/src/components/header/_mixins.scss new file mode 100644 index 0000000000..c033808a6c --- /dev/null +++ b/packages/styles/src/components/header/_mixins.scss @@ -0,0 +1,34 @@ +@use '../../mixins/icons'; + +@mixin subsidiary-header-interactive() { + text-decoration: none; + display: inline-flex; + align-items: center; + gap: var(--post-core-dimension-6); + border-radius: var(--post-core-dimension-24); + font-size: var(--post-core-font-size-16); + padding: var(--post-core-dimension-3) var(--post-core-dimension-10); +} + +@mixin mobile-header-interactive() { + width: 100%; + height: var(--post-core-dimension-48); + padding-inline-end: var(--post-core-dimension-6); + gap: var(--post-core-dimension-16); + border-block: var(--post-core-dimension-1) solid transparent; + border-block-end-color: currentColor; + font-weight: var(--post-core-font-weight-700); + + &:hover, + &.selected { + border-block-width: var(--post-core-dimension-3); + } + + &:hover::after { + content: ''; + display: block; + @include icons.icon(3020); + width: var(--post-core-dimension-24); + height: var(--post-core-dimension-24); + } +} diff --git a/packages/styles/src/components/header/index.scss b/packages/styles/src/components/header/index.scss new file mode 100644 index 0000000000..674e266c8e --- /dev/null +++ b/packages/styles/src/components/header/index.scss @@ -0,0 +1,60 @@ +@use '../../mixins/media'; +@use '../../mixins/utilities'; + +@use 'mixins' as *; + +post-header { + ul[slot="meta-navigation"] { + gap: var(--post-core-dimension-4); + + @include media.max(lg) { + flex-direction: column; + + a, + button { + justify-content: space-between; + border-radius: 0; + @include mobile-header-interactive; + + @include utilities.focus-style-custom { + border-radius: var(--post-core-dimension-4); + } + } + } + } + + a, + post-togglebutton { + &:not(post-mainnavigation *) { + @include subsidiary-header-interactive; + + @include media.min(sm) { + post-icon { + height: var(--post-core-dimension-22); + width: var(--post-core-dimension-22); + } + } + + @include media.max(sm) { + padding: var(--post-core-dimension-8); + + post-icon { + height: var(--post-core-dimension-24); + width: var(--post-core-dimension-24); + } + + .visually-hidden-sm { + @include utilities.visuallyhidden; + } + } + } + } + + a.selected, + post-togglebutton[aria-pressed='true'] { + &:not(post-mainnavigation *) { + color: var(--post-core-color-brand-white); + background: var(--post-core-color-sandgrey-100); + } + } +}