From 161459448a097c5e0c0502ff0d2d6736ce516036 Mon Sep 17 00:00:00 2001 From: Wesley Bomar Date: Wed, 18 Aug 2021 14:45:14 -0500 Subject: [PATCH 1/5] GH-101: Refactor Portal Link Fixes 758e2ff CAVEAT: Font is not always consistent between states and sites. --- .../css/src/_imports/trumps/s-header.css | 112 +++++++++--------- .../src/_imports/trumps/s-header.twig.html | 7 +- taccsite_cms/templates/header.html | 4 +- 3 files changed, 61 insertions(+), 62 deletions(-) diff --git a/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css b/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css index 8fdb87031..785bf030e 100644 --- a/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css +++ b/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css @@ -277,6 +277,10 @@ Styleguide Trumps.Scopes.Header @custom-selector :--navbar-toggle .navbar-toggler; +@custom-selector :--navbar-toggle__text + .navbar-toggler-text; +@custom-selector :--navbar-toggle__icon + .navbar-toggler-icon; @custom-selector :--navbar-toggle--opened .navbar-toggler[aria-expanded="true"]; @custom-selector :--navbar-toggle--closed @@ -291,16 +295,19 @@ Styleguide Trumps.Scopes.Header /* Layout */ /* To layout the toggle as a "mobile button" */ - .s-header :--navbar-toggle--opened, - .s-header :--navbar-toggle--closed { + .s-header :--navbar-toggle { /* WARNING: Do not set `display` outside of (--nav-compressed) */ /* FAQ: The button as Bootstrap `.navbar-toggler` uses `display: none` */ display: grid; } .s-header :--navbar-toggle--opened { @extend %x-mobile-button--only-icon; } .s-header :--navbar-toggle--closed { @extend %x-mobile-button--no-toggle; } - .s-header :--navbar-toggle::after { @extend %x-mobile-button__text; } - .s-header :--navbar-toggle::before { @extend %x-mobile-button__icon; } + .s-header :--navbar-toggle__icon { @extend %x-mobile-button__icon; } + .s-header :--navbar-toggle__text { @extend %x-mobile-button__text; } + /* AFTER GH-101: Remove this ruleset */ + .s-header :--navbar-toggle:not(.gh-101)::after { + @extend %x-mobile-button__text; + } @@ -346,20 +353,28 @@ Styleguide Trumps.Scopes.Header /* Icon */ /* To overwrite Bootstrap */ - .s-header .navbar-toggler-icon { display: none; } + .s-header :--navbar-toggle__icon { background-image: none !important; } /* To swap Bootstrap icon for Cortal icon */ /* HACK: To apply icon styles without using a class in the markup */ /* FAQ: For dynamic markup change, Portal & Docs markup must be updated */ /* SEE: https://getbootstrap.com/docs/4.0/components/collapse/#events */ - .s-header :--navbar-toggle { @extend [class*=" icon-"]; } - .s-header :--navbar-toggle::before { @extend .icon; } + .s-header :--navbar-toggle__icon { + @extend [class*=" icon-"]; + @extend .icon; + } /* To change icon without using dynamic icon classes */ - .s-header :--navbar-toggle--opened { @extend .icon-close; } - .s-header :--navbar-toggle--closed { @extend .icon-burger; } - /* To stretch icon to mimic icon in design (which is not available yet) */ - .s-header :--navbar-toggle--closed::before { transform: scaleX(1.5); } + .s-header :--navbar-toggle--opened :--navbar-toggle__icon { + @extend .icon-close; + } + .s-header :--navbar-toggle--closed :--navbar-toggle__icon { + @extend .icon-burger; + } + /* To stretch icon to mimic icon in design */ + .s-header :--navbar-toggle--closed :--navbar-toggle__icon { + transform: scaleX(1.5); + } /* To overwrite `.icon` */ - .s-header :--navbar-toggle::before { + .s-header :--navbar-toggle__icon { font-size: 30px; /* WARNING: Do not use `rem` until `html { 62.5%; }` */ } @@ -367,16 +382,21 @@ Styleguide Trumps.Scopes.Header /* Text */ - .s-header :--navbar-toggle::after { - content: attr(data-icon-label); + /* To hide text of button when menu is open */ + .s-header :--navbar-toggle--opened :--navbar-toggle__text { + display: none; } - /* To apply style to old markup (see "Portal & Docs Selectors") */ - /* FAQ: This selector does not use `:--for-portal` but has similar purpose */ - .s-header :--navbar-toggle:not([data-icon-label])::after { + + /* AFTER GH-101: Remove this block i.e. rely only on `[data-icon-label]` */ + /* To create fake text that does not exist in old markup */ + /* FAQ: The `:not(.gh-101)` applies style only to old markup */ + .s-header :--navbar-toggle:not(.gh-101)::after { content: 'Menu'; } - /* FAQ: Only the closed state is supposed to have text */ - .s-header :--navbar-toggle--opened::after { display: none; } + /* To hide fake text of button when menu is open */ + .s-header :--navbar-toggle--opened:not(.gh-101)::after { + display: none; + } } @@ -563,6 +583,8 @@ Styleguide Trumps.Scopes.Header .nav-item.active .nav-link, .nav-item.dropdown.show .nav-link; */ +@custom-selector :--portal-nav-link + .s-portal-nav .nav-link; @custom-selector :--portal-nav-link--user .s-portal-nav .nav-link.dropdown-toggle; @custom-selector :--portal-nav-link--guest @@ -583,7 +605,7 @@ Styleguide Trumps.Scopes.Header display: flex; align-items: center; } -.s-header .s-portal-nav .nav-link { +.s-header :--portal-nav-link { /* To horizontally center align icon, text, and toggle as a group */ justify-content: center; @@ -601,7 +623,7 @@ Styleguide Trumps.Scopes.Header .s-header .nav-link { height: 100%; } - .s-header .s-portal-nav .nav-link { + .s-header :--portal-nav-link { background-color: env(--header-portal-nav-bkgd-color); } } @@ -612,7 +634,7 @@ Styleguide Trumps.Scopes.Header .s-header .s-cms-nav .nav-link { font-size: 2em; } - .s-header .s-portal-nav .nav-link { + .s-header :--portal-nav-link { /* To align portal nav link to the right */ margin-left: auto; } @@ -638,9 +660,9 @@ Styleguide Trumps.Scopes.Header @media (--nav-expanded) { - .s-header .s-portal-nav .nav-link, + .s-header :--portal-nav-link, /* To overwrite Bootstrap (see "Portal & Docs Selectors") */ - :--for-portal .s-portal-nav .nav-link { + :--for-portal :--portal-nav-link { /* To match space between logo and left side of window */ --nav-link-horz-pad: env(--header-navbar-normal-left-pad); } @@ -656,7 +678,7 @@ Styleguide Trumps.Scopes.Header /* To match design of collapsed navbar Portal Nav but take liberty with when */ @media (--nav-compressed) /* dev liberty */ { - .s-header .s-portal-nav .nav-link { + .s-header :--portal-nav-link { min-width: calc(2 * env(--header-navbar-toggle-width)); /* dev liberty */ padding-top: 0; /* to overwrite Bootstrap */ @@ -677,40 +699,18 @@ Styleguide Trumps.Scopes.Header /* Layout */ /* To layout each link as a "mobile button" */ - .s-header .s-portal-nav .nav-link { + .s-header :--portal-nav-link { display: grid; } - .s-header :--portal-nav-link--user { - @extend %x-mobile-button--has-toggle; - } - .s-header :--portal-nav-link--guest { - @extend %x-mobile-button--no-toggle; - } - .s-header :--portal-nav-link__icon { - @extend %x-mobile-button__icon; - } - .s-header .s-portal-nav .nav-link::before { + .s-header :--portal-nav-link--user { @extend %x-mobile-button--has-toggle;} + .s-header :--portal-nav-link--guest { @extend %x-mobile-button--no-toggle; } + .s-header :--portal-nav-link__icon { @extend %x-mobile-button__icon; } + .s-header :--portal-nav-link__text { @extend %x-mobile-button__text; } + .s-header :--portal-nav-link::after { @extend %x-mobile-button__toggle; } + /* AFTER GH-101: Remove this ruleset */ + .s-header :--portal-nav-link:not(.gh-101)::before { @extend %x-mobile-button__text; } - .s-header .s-portal-nav .nav-link::after { - @extend %x-mobile-button__toggle; - } - - - - /* Text */ - - /* To apply style to old markup (see "Portal & Docs Selectors") */ - /* FAQ: This selector does not use `:--for-portal` but has similar purpose */ - .s-header :--portal-nav-link--guest::before { - content: 'Log In'; - } - /* Use content for text, not actual text */ - /* FAQ: For consistency with `:--navbar-toggle` */ - /* FAQ: Because the text need not be selectable anyway */ - .s-header :--portal-nav-link__text { display: none; } - /* To hide the stray text */ - .s-header :--portal-nav-link--guest { font-size: 0; } } @@ -1158,7 +1158,7 @@ Styleguide Trumps.Scopes.Header - height for portal nav would hide its dropdown menu */ .s-header .s-search-bar::part(form), - .s-header .s-portal-nav .nav-link { + .s-header :--portal-nav-link { height: var(--header-navbar-height); } diff --git a/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.twig.html b/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.twig.html index 214208ab9..ce1a35409 100644 --- a/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.twig.html +++ b/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.twig.html @@ -10,8 +10,6 @@ - multiple internal scope (`s-`) classes (only via `./s-portal-nav.html`) (only via `./s-cms-nav.html`) -- FontAwesome class names - (only via `./s-portal-nav.html`) - illegally-nested markup (`ul > div > li`) (only via `./s-portal-nav.html`) --> @@ -26,7 +24,7 @@ + + {% if settings.PORTAL %} + {% include "nav_portal.html" with className="navbar-nav" settings=settings only %} + {% endif %} From b2e59dba11c6056344b99c36d8858c17619aa4e0 Mon Sep 17 00:00:00 2001 From: Wesley Bomar Date: Thu, 9 Sep 2021 14:53:51 -0500 Subject: [PATCH 4/5] GH-101: New mobile portal and nav menu design Known Issues: 1. On CMS & Docs, `s-header__menu-backdrop` adds gap in navigation bar. 2. On CMS, mobile navbar opens and closes too slow. 3. The login link is a bit too wide. 4. Code is difficult to follow (but documented). --- .../_imports/components/bootstrap.navbar.css | 17 + .../src/_imports/trumps/for-portal.header.css | 21 +- .../css/src/_imports/trumps/s-header.css | 628 +++++++++++------- .../site_cms/css/src/_themes/constants.json | 6 +- .../static/site_cms/css/src/site.header.css | 1 + .../static/site_cms/js/site.header.js | 43 ++ taccsite_cms/templates/header.html | 21 +- 7 files changed, 493 insertions(+), 244 deletions(-) create mode 100644 taccsite_cms/static/site_cms/css/src/_imports/components/bootstrap.navbar.css create mode 100644 taccsite_cms/static/site_cms/js/site.header.js diff --git a/taccsite_cms/static/site_cms/css/src/_imports/components/bootstrap.navbar.css b/taccsite_cms/static/site_cms/css/src/_imports/components/bootstrap.navbar.css new file mode 100644 index 000000000..df4d086c8 --- /dev/null +++ b/taccsite_cms/static/site_cms/css/src/_imports/components/bootstrap.navbar.css @@ -0,0 +1,17 @@ +/* +Navbar (Bootstrap) + +Add to Bootstrap styles. See: + +- [Bootstrap Grid](https://getbootstrap.com/docs/4.0/components/navbar/) + +Styleguide Components.Bootstrap.Navbar +*/ + +/* To support collapsing navbar horizontally */ +/* SEE: https://stackoverflow.com/a/50064701/11817077 */ +.collapsing.width { + transition-property: width, visibility; + width: 0; + height: auto; +} diff --git a/taccsite_cms/static/site_cms/css/src/_imports/trumps/for-portal.header.css b/taccsite_cms/static/site_cms/css/src/_imports/trumps/for-portal.header.css index 0c472a3ac..bb9c4fcb3 100644 --- a/taccsite_cms/static/site_cms/css/src/_imports/trumps/for-portal.header.css +++ b/taccsite_cms/static/site_cms/css/src/_imports/trumps/for-portal.header.css @@ -7,12 +7,22 @@ Styleguide Trumps.ForOtherSites.Portal */ @import url("_imports/tools/selectors.css"); + + +/* Selectors */ + +/* HACK: Relying on definition from `s-header.css` */ +/* @custom-selector :--portal-button */ + + + /* Ammend `s-header.css` (for Header because of Body) */ @media (--nav-compressed) { /* To position navbar menus/buttons based on window width not parent width */ /* FAQ: Portal at body min. width places menu buttons outside narrow screen */ + /* NOTE: If Portal had no horz. body scroll, then we would not need this */ :--for-portal .s-header :--navbar-toggle { position: absolute; /* assumes Bootstrap `.navbar { position: relative }` */ width: env(--header-navbar-toggle-width); @@ -30,15 +40,10 @@ Styleguide Trumps.ForOtherSites.Portal - env(--header-navbar-toggle-width--portal-user) ); /* FAQ: = window width – its own width (not others, cuz it's the last) */ } - :--for-portal .s-header .navbar-collapse { - width: calc( - 100vw - - env(--header-navbar-mobile-menu-offset) - ); /* FAQ: = window width – offset from window */ - } - /* WARNING: Only Portal may use this cuz only its markup knows login state */ - :--for-portal .s-header.is-portal-user :--portal-nav-link { + /* To prevent negligible width change from browser dynamic calculation */ + /* CAVEAT: Only Portal may use this cuz only its markup knows login state */ + :--for-portal .s-header.is-portal-user :--portal-button { width: env(--header-navbar-toggle-width--portal-user); } diff --git a/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css b/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css index 92beafcec..cf1ed70b1 100644 --- a/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css +++ b/taccsite_cms/static/site_cms/css/src/_imports/trumps/s-header.css @@ -34,6 +34,105 @@ Styleguide Trumps.Scopes.Header +/* + * MIXINS + */ + + + + + +/* To create noticable line between each item */ +%s-header-nav-link-border { + /* To ensure any background color does not cover box-shadow */ + position: relative; + + box-shadow: + /* x pos. */ calc(var(--nav-link-horz-pad) / 2) + /* y pos. */ calc(-1 * (2px + calc(var(--nav-link-horz-pad) / 2))) + /* blur */ 0 + /* spread */ calc(-1 * calc(var(--nav-link-horz-pad) / 2)) + /* color */ var(--nav-line-color); +} + +/* To create negligible line between each item */ +%s-header-menu-link-border { + /* To ensure any background color does not cover box-shadow */ + position: relative; + + box-shadow: + /* x pos. */ 0 + /* y pos. */ calc(-1 * (1px + var(--menu-link-horz-pad))) + /* blur */ 0 + /* spread */ calc(-1 * var(--menu-link-horz-pad)) + /* color */ var(--menu-line-color); +} + +%s-header-mobile-menu { + /* Layout: To stretch menu to cover most of page */ + position: fixed; + top: 0; + right: 0; + height: 100vh; + width: calc(100vw - env(--header-navbar-mobile-menu-offset)); + + /* Layout: To be above body content on Portal & Docs (not harmful on CMS) */ + z-index: 2; /* 2 overlaps `.s-header__menu-backdrop` on page with scrollbar */ + + /* Style */ + padding-top: env(--header-brandbar-mobile-height); + background-color: env(--header-link-bkgd-color); + border-left: env(--border-width--normal) solid var(--global-color-primary--dark); +} + + + + + +/* + * JAVASCRIPT + */ + + + + + +@media (--nav-compressed) { + + /* To allow a click outside a menu to close the menu */ + .s-header .show + .s-header__menu-backdrop { + /* Layout: To stretch element to always span full screen size */ + position: fixed; + top: 0; + left: 0; + height: 100vh; + width: env(--header-navbar-mobile-menu-offset); + + /* Layout: To be above body content on Portal & Docs (not harmful on CMS) */ + z-index: 1; + + /* Style: To blur and/or lighten the content behind the element */ + @supports (backdrop-filter: none) { + & { + /* To blur and lighten content behind the element */ + backdrop-filter: blur(18px) brightness(85%); + background-color: transparent; + } + } + @supports not (backdrop-filter: none) { + & { + /* To lighten content behind the element */ + background-color: rgba(var(--global-color-primary--x-light-rgb), 0.75); + } + } + } + +} + + + + + /* * GENERAL */ @@ -54,9 +153,20 @@ Styleguide Trumps.Scopes.Header +/* Elements */ + +/* To overwrite html-elements.css */ +.s-header a:active { + position: unset; + top: unset; +} + + + + + /* Variables */ -/* FAQ: These have `--header` prefix because they can be used outside header */ @media (--nav-expanded) { :root { --header-brandbar-height: env(--header-brandbar-normal-height); @@ -64,6 +174,16 @@ Styleguide Trumps.Scopes.Header --portal-navlink-max-width: none; } + + .s-header { + --menu-link-horz-pad: 11px; + --menu-line-color: env(--header-bkgd-color); + + --nav-line-color: transparent; + --nav-link-vert-pad: unset; + /* SEE: "Nav: Variables (Addendum)" */ + /* --nav-link-horz-pad: … */ + } } @media (--nav-compressed) { :root { @@ -72,6 +192,16 @@ Styleguide Trumps.Scopes.Header --portal-navlink-max-width: env(--portal-navlink-mobile-max-width); } + + .s-header { + --menu-link-horz-pad: 24px; + --menu-line-color: transparent; + + --nav-line-color: env(--header-accent-color); + --nav-link-vert-pad: 12px; + /* To line up navbar left-hand content with logo */ + --nav-link-horz-pad: calc(env(--header-navbar-toggle-width) / 2); + } } @@ -217,10 +347,18 @@ Styleguide Trumps.Scopes.Header /* FAQ: Set height of descendants, but not of CMS and Portal and mobile navs */ :--nav-bar-fake-bkgd, #header-logo, +/* FAQ: We set height of immediate content of elements, because: + - height for navbar elements is not respected as navbar collapses + - height for portal nav would hide its dropdown menu */ .s-header :--navbar-toggle, -.s-header .s-search-bar { +.s-header .navbar-collapse:not(.show) .s-search-bar::part(form), +.s-header :--portal-button { height: var(--header-navbar-height); } +.s-header .navbar-collapse:is(.show, .collapsing) .s-search-bar::part(form), +.s-header :--portal-message { + height: env(--header-navbar-normal-height); +} /* To support shrinking and truncation of certain nav elements */ /* SEE: https://css-tricks.com/flexbox-truncated-text/ */ @@ -259,10 +397,12 @@ Styleguide Trumps.Scopes.Header /* Layout */ - /* To push it (and everything to its right) to the right of the navbar */ - .s-header :--navbar-toggle { margin-left: auto; } + /* To push toggle (and everything to its right) to the right of the navbar */ + .s-header .navbar-collapse:not(.show) + :--navbar-toggle { + margin-left: auto; + } - /* To layout the toggle as a "mobile button" */ + /* To layout the toggle content for a "mobile button" */ .s-header :--navbar-toggle { /* WARNING: Do not set `display` outside of (--nav-compressed) */ /* FAQ: The button as Bootstrap `.navbar-toggler` uses `display: none` */ @@ -376,40 +516,13 @@ Styleguide Trumps.Scopes.Header @media (--nav-compressed) { - /* Style */ - + /* To make the navbar act similar to an overlay menu */ + /* CAVEAT: If menu is open, and layout switches from collapsed to expanded to collapsed, then menu will remain open upon return to collapsed layout. */ .s-header .navbar-collapse { - padding-top: 12px; + /* Layout */ + @extend %s-header-mobile-menu; - @supports (backdrop-filter: none) { - & { - backdrop-filter: blur(18px) brightness(85%); - background-color: rgba(var(--global-color-primary--x-dark-rgb), 0.6); - } - } - @supports not (backdrop-filter: none) { - & { - background-color: rgba(var(--global-color-primary--x-dark-rgb), 0.95); - } - } - - border: env(--border-width--normal) solid var(--global-color-primary--dark); - } - - - - /* Layout */ - - /* HACK: To make the navbar act similar to a dropdown */ - /* TODO: We should either use dropdown or create TACC nav component (GH-12) */ - /* CAVEAT: If menu is open, and layout switches from collapsed to expanded to collapsed, then menu will remain open upon return to collapsed layout. - /* CAVEAT: If menu is open, and user clicks anywhere outside the navbar, then menu remains open. But a real dropdown would be closed after such a click. */ - .s-header .navbar-collapse { - position: absolute; /* assumes Bootstrap `.navbar { position: relative }` */ - top: 100%; - left: env(--header-navbar-mobile-menu-offset); - right: 0; - z-index: 1; /* To be above main content in Portal & Docs */ + transition-duration: 0s; /* to overwrite Bootstrap */ } } @@ -422,27 +535,6 @@ Styleguide Trumps.Scopes.Header -/* Nav: Variables */ - -@media (--nav-expanded) { - .s-header .navbar-nav { - --nav-line-color: transparent; - --nav-link-vert-pad: unset; - /* SEE: "Nav: Variables (Addendum)" */ - /* --nav-link-horz-pad: … */ - } -} -@media (--nav-compressed) { - .s-header .navbar-nav { - --nav-line-color: env(--header-accent-color); - --nav-link-vert-pad: 0.425em; - /* To line up navbar left-hand content with logo */ - --nav-link-horz-pad: calc(env(--header-navbar-toggle-width) / 2); - } -} - - - /* Nav: Variables (Addendum) */ /* To reduce space between nav links when space is limited */ @@ -450,18 +542,18 @@ Styleguide Trumps.Scopes.Header @media (--nav-expanded) and (--nav-wide-and-below) { /* TODO: Hardcode `is-on-portal` in Portal & Docs markup, then remove the `:--for-cms` selector prefix */ - :--for-cms .s-header:not(.is-on-portal) .navbar-nav { + :--for-cms .s-header:not(.is-on-portal) { --nav-link-horz-pad: 1em; } } /* FAQ: Reduce space at wider breakpoint when Portal Nav is present */ @media (--nav-expanded) and (--nav-x-wide-and-below) { - .s-header.is-on-portal .navbar-nav, + .s-header.is-on-portal, /* To apply style to old markup (see "Portal & Docs Selectors") */ /* TODO: Hardcode `is-on-portal` in Portal & Docs markup, then remove the `:--for-*` selectors */ - :--for-docs .s-header .navbar-nav, - :--for-portal .s-header .navbar-nav { + :--for-docs .s-header, + :--for-portal .s-header { --nav-link-horz-pad: 1em; } } @@ -472,19 +564,19 @@ Styleguide Trumps.Scopes.Header /* To have 15px of space for 12px font */ /* TODO: Hardcode `is-on-portal` in Portal & Docs markup, then remove the `:--for-cms` selector prefix */ - :--for-cms .s-header:not(.is-on-portal) .navbar-nav { + :--for-cms .s-header:not(.is-on-portal) { --nav-link-horz-pad: 1.5em; } } /* FAQ: Increase space at wider breakpoint when Portal Nav is present */ @media (--nav-expanded) and (--nav-x-wide-and-above) { /* To have 15px of space for 12px font */ - .s-header.is-on-portal .navbar-nav, + .s-header.is-on-portal, /* To apply style to old markup (see "Portal & Docs Selectors") */ /* TODO: Hardcode `is-on-portal` in Portal & Docs markup, then remove the `:--for-*` selectors */ - :--for-docs .s-header .navbar-nav, - :--for-portal .s-header .navbar-nav { + :--for-docs .s-header, + :--for-portal .s-header { --nav-link-horz-pad: 1.5em; } } @@ -542,8 +634,9 @@ Styleguide Trumps.Scopes.Header } /* To match design */ - .s-cms-nav { - padding-top: 8px; + .s-cms-nav, + .s-portal-nav .dropdown-menu { + padding-top: 5px; } #header-logo { @@ -575,43 +668,37 @@ Styleguide Trumps.Scopes.Header */ @custom-selector :--portal-nav-link .s-portal-nav .nav-link; -@custom-selector :--portal-nav-link--user - .s-portal-nav .nav-link.dropdown-toggle; -@custom-selector :--portal-nav-link--guest - .s-portal-nav .nav-link:not(.dropdown-toggle); -@custom-selector :--portal-nav-link__icon - .s-portal-nav .nav-link .icon; -@custom-selector :--portal-nav-link__text - .s-portal-nav .nav-link .nav-text; - +@custom-selector :--portal-button + .s-portal-nav .dropdown:not(.show) .nav-link, + .s-portal-nav .nav-item:not(.dropdown) .nav-link; +@custom-selector :--portal-button--user + .s-portal-nav .dropdown:not(.show) .nav-link.dropdown-toggle; +@custom-selector :--portal-button--guest + .s-portal-nav .nav-item:not(.dropdown) .nav-link:not(.dropdown-toggle); +@custom-selector :--portal-button__icon + .s-portal-nav .dropdown:not(.show) .nav-link .icon, + .s-portal-nav :not(.dropdown) .nav-link .icon; +@custom-selector :--portal-button__text + .s-portal-nav .dropdown:not(.show) .nav-link .nav-text, + .s-portal-nav :not(.dropdown) .nav-link .nav-text; +@custom-selector :--portal-message + .s-portal-nav .dropdown.show .nav-link; +@custom-selector :--portal-message__icon + .s-portal-nav .dropdown.show .nav-link .icon; +@custom-selector :--portal-message__text + .s-portal-nav .dropdown.show .nav-link .nav-text; /* Nav Link: General */ -/* To style the link */ -.s-header :--portal-nav-link { - background-color: env(--header-portal-nav-bkgd-color); -} - /* To layout the link */ .s-header .nav-link { /* To support truncation */ - /* To vertically center text and icons (if any) */ display: flex; + /* To vertically center text and icons (if any) */ align-items: center; } -.s-header :--portal-nav-link { - /* To horizontally center align icon, text, and toggle as a group */ - justify-content: center; - - /* To prevent expansion upon dropdown menu open */ - width: var(--portal-navlink-max-width); - - /* To prevent wrapping of this as it slides into place */ - /* NOTE: This affects username also, but it should not have any whitespace */ - white-space: nowrap; -} /* Nav Link: General: Desktop */ @@ -645,7 +732,15 @@ Styleguide Trumps.Scopes.Header padding-bottom: var(--nav-link-vert-pad); } -/* Nav Link: Spacing: Desktop */ + + +/* Nav Link: Portal */ + +.s-header :--portal-button { + background-color: env(--header-portal-nav-bkgd-color); +} + +/* Nav Link: Portal: Desktop */ @media (--nav-expanded) { @@ -656,65 +751,113 @@ Styleguide Trumps.Scopes.Header --nav-link-horz-pad: env(--header-navbar-normal-left-pad); } + .s-header :--portal-nav-link { + /* To horizontally center align icon, text, and toggle as a group */ + justify-content: center; + + /* To prevent expansion upon dropdown menu open */ + max-width: var(--portal-navlink-max-width); + + /* To prevent wrapping of this as it slides into place */ + /* NOTE: This affects username also, but it should not have any whitespace */ + white-space: nowrap; + } + + /* CAVEAT: The selector must be specific enough to override Bootstrap */ + .s-header :--portal-nav-link, + /* To overwrite Bootstrap (see "Portal & Docs Selectors") */ + :--for-portal .s-header :--portal-nav-link { + padding-right: var(--nav-link-horz-pad, 0); + padding-left: var(--nav-link-horz-pad, 0); + } + + /* Icon */ + .s-header :--portal-nav-link__icon { margin-right: 0.5em; } } -/* Nav Link: Spacing: Mobile */ +/* Nav Link: Portal: Mobile */ -/* To match design of collapsed navbar Portal Nav but take liberty with when */ -@media (--nav-compressed) /* dev liberty */ { +@media (--nav-compressed) { - .s-header :--portal-nav-link { - padding-top: 0; /* to overwrite Bootstrap */ - padding-bottom: 0; /* to overwrite Bootstrap */ - /* NOTE: This is for the edge case that username exceeds expectation */ - --nav-link-horz-pad: 12px; + .s-header :--portal-nav-link/*,*//* ???: Is below selector necessary here? */ + /* To overwrite Bootstrap (see "Portal & Docs Selectors") */ + /* :--for-portal :--portal-nav-link */ { + /* To match design */ + --nav-link-vert-pad: 0; } -} - - + .s-header :--portal-button { + width: fit-content; -/* Nav Link: Icon (Mobile-Only) */ + /* To avoid wrapping of button */ + /* FAQ: Wrap happens if logo is wide and button layout is not flexible */ + /* NOTE: The `clamp()` solution avoids using third media query for navbar */ + padding-right: clamp( + var(--nav-link-horz-pad, 0), + 3vw, + env(--header-navbar-mobile-right-pad) + ); /* FAQ: The widths must prevent wrap when `#header-logo` is max. width */ + padding-left: var(--nav-link-horz-pad, 0); -@media (--nav-compressed) { + /* NOTE: This is for the edge case that username exceeds expectation */ + --nav-link-horz-pad: 12px; + } - /* Layout */ + /* Icon */ - /* To layout each link as a "mobile button" */ - .s-header :--portal-nav-link { + /* To make link appear as a button when menu is closed */ + .s-header :--portal-button { display: grid; } - .s-header :--portal-nav-link--user { @extend %x-mobile-button--has-toggle;} - .s-header :--portal-nav-link--guest { @extend %x-mobile-button--no-toggle; } - .s-header :--portal-nav-link__icon { @extend %x-mobile-button__icon; } - .s-header :--portal-nav-link__text { @extend %x-mobile-button__text; } - .s-header :--portal-nav-link::after { @extend %x-mobile-button__toggle; } + .s-header :--portal-button--user { @extend %x-mobile-button--has-toggle;} + .s-header :--portal-button--guest { @extend %x-mobile-button--no-toggle; } + .s-header :--portal-button__icon { @extend %x-mobile-button__icon; } + .s-header :--portal-button__text { @extend %x-mobile-button__text; } + .s-header :--portal-button::after { @extend %x-mobile-button__toggle; } /* AFTER GH-101: Remove this ruleset */ - .s-header :--portal-nav-link:not(.gh-101)::before { + .s-header :--portal-button:not(.gh-101)::before { @extend %x-mobile-button__text; } + /* To make link appear as a welcome message when menu is open */ + .s-header :--portal-message__icon { + margin-right: 0.875em; /* To approximate design via "pretty" `em` value */ + } + .s-header :--portal-message__text { + font-size: 18px; /* WARNING: Do not use `rem` until `html { 62.5%; }` */ + font-weight: var(--regular); + } + .s-header :--portal-message__text::before { + content: 'Welcome, '; + } + .s-header :--portal-message::after { + display: none; + } + } /* Nav Link: Separator */ -/* To create line between each item */ -.s-header .nav-item + .nav-item .nav-link { - /* To ensure any background color does not cover box-shadow */ - position: relative; +/* Nav Link: Separator: Desktop */ - box-shadow: - /* x pos. */ calc(var(--nav-link-horz-pad) / 2) - /* y pos. */ calc(-1 * (2px + calc(var(--nav-link-horz-pad) / 2))) - /* blur */ 0 - /* spread */ calc(-1 * calc(var(--nav-link-horz-pad) / 2)) - /* color */ var(--nav-line-color); +@media (--nav-expanded) { + .s-header .nav-item + .nav-item .nav-link { + @extend %s-header-nav-link-border; + } +} + +/* Nav Link: Separator: Mobile */ + +@media (--nav-compressed) { + .s-header .nav-item + .nav-item:not(.dropdown.show) .nav-link { + @extend %s-header-nav-link-border; + } } @@ -785,37 +928,25 @@ Styleguide Trumps.Scopes.Header -/* Dropdown: Variables */ - -@media (--nav-expanded) { - .s-header .dropdown { - --menu-link-horz-pad: 11px; - --menu-line-color: env(--header-bkgd-color); - } -} -@media (--nav-compressed) { - .s-header .dropdown { - --menu-link-horz-pad: 26px; - --menu-line-color: transparent; - } -} - - - /* Dropdown: Toggle */ .s-header .dropdown-toggle::after { @extend %x-arrow; - --size: 0.375em; --line: 0.1em; /* to get 1px at 12px text and 2px at 24px text */ --color: env(--header-text-color); + + position: relative; /* to allow use of `top` (to vertically align) */ +} + +/* Dropdown: Toggle: Layout: Default */ + +.s-header .dropdown-toggle::after { + --size: 0.375em; --nudge: 0.24em; margin-left: 0.6em; - position: relative; /* to allow use of `top` (to vertically align) */ } -/* To vertically align with text */ .s-header .dropdown-toggle[aria-expanded="true"]::after { @extend %x-arrow--up; top: var(--nudge); @@ -825,19 +956,42 @@ Styleguide Trumps.Scopes.Header bottom: var(--nudge); } +/* Dropdown: Toggle: Layout: Mobile */ + +@media (--nav-compressed) { + + /* CMS Menu */ + + .s-header .s-cms-nav .dropdown-toggle::after { + --size: 0.2em; + --nudge: 0.1em; + } + .s-header .s-cms-nav .dropdown-toggle[aria-expanded="true"]::after { + @extend %x-arrow--left; + margin-right: calc(-1 * var(--size)); /* to pull text closer */ + left: calc((-1 * var(--size) * 2) - var(--nudge)); /* to align arrow */ + order: -1; /* move to far left */ + top: auto; /* overwrite default */ + } + .s-header .s-cms-nav .dropdown-toggle[aria-expanded="false"]::after { + @extend %x-arrow--right; + margin-left: auto; + right: var(--nudge); + /* order: 2; /* retain position */ + bottom: auto; /* overwrite default */ + } + +} + /* Dropdown Menu */ -/* To style menu */ -/* To overwrite Bootstrap */ -/* FAQ: Bootstrap has styles we do not want a value for, so `unset` */ .s-header .dropdown-menu { - margin: unset; - - border-radius: unset; + margin: unset; /* to undo Bootstrap */ + border-radius: unset; /* to undo Bootstrap */ } @@ -846,7 +1000,7 @@ Styleguide Trumps.Scopes.Header @media (--nav-expanded) { - /* Dropdowns */ + /* All Dropdowns */ /* To overwrite Bootstrap */ .s-header .dropdown-menu { @@ -858,7 +1012,8 @@ Styleguide Trumps.Scopes.Header font-size: 12px; /* WARNING: Do not use `rem` until `html { 62.5%; }` */ } - /* To layout menu */ + /* Portal Nav */ + .s-header .s-portal-nav .dropdown-menu { /* To stretch dropdown width to match the width of its parent */ min-width: 100%; @@ -872,9 +1027,12 @@ Styleguide Trumps.Scopes.Header @media (--nav-compressed) { + /* CMS Menu & Portal Nav */ + /* To overwrite Bootstrap */ .s-header .s-cms-nav .dropdown-menu { padding: 0 0 0.5em 0; + margin: unset; /* to undo Bootstrap */ background-color: transparent; border: none; @@ -882,24 +1040,68 @@ Styleguide Trumps.Scopes.Header font-size: 18px; /* WARNING: Do not use `rem` until `html { 62.5%; }` */ } .s-header .s-portal-nav .dropdown-menu { - padding: 0; + /* padding-top: unset; /* to undo Bootstrap *//* FAQ: Set elsewhere by us */ + padding-bottom: unset; /* to undo Bootstrap */ - background-color: env(--header-link-bkgd-color); + background-color: transparent; border: none; font-size: 24px; /* WARNING: Do not use `rem` until `html { 62.5%; }` */ } - /* To layout menu */ - /* To restore Bootstrap behavior: - - expanded navbar dropdown - - not collapsed navbar push-down */ - /* SEE: (Bootstrap) `.navbar-expand-xl .navbar-nav .dropdown-menu-right` */ - .s-header .s-portal-nav .dropdown-menu { - position: absolute; + /* CMS Nav */ + + .s-header .s-cms-nav .dropdown-menu { + @extend %s-header-nav-link-border; - right: 0; - left: auto; + /* To allow `position: relative` to not move the menu */ + /* SEE: `_navbar.scss` + .navbar-nav .dropdown-menu { position: static; float: none; } */ + top: 0; + } + + /* Portal Nav */ + + /* To make the navbar act similar to an overlay menu */ + /* CAVEAT: If menu is open, and layout switches from collapsed to expanded to collapsed, then menu will remain open upon return to collapsed layout. */ + .s-header .s-portal-nav .dropdown.show { + /* Layout */ + @extend %s-header-mobile-menu; + + left: auto; /* to overwrite Bootstrap */ + } + .s-header .s-portal-nav .dropdown.show .dropdown-menu { + flex-grow: 1; + } + + /* Nested Dropdown */ + + /* To hide sibling dropdowns */ + /* CAVEAT: Relies on JavaScript specific to `s-cms-nav` (fallback: `… ~ …` */ + .s-header .js-has-open-menu .dropdown:not(.show), + /* CAVEAT: Does not hide previous siblings (solution: `js-has-open-menu`) */ + .s-header .nav-item.dropdown.show ~ .nav-item { + display: none; + } + + /* To provide a back button */ + .s-header .s-cms-nav .dropdown-toggle[aria-expanded="true"] { + flex-wrap: wrap; + } + .s-header .s-cms-nav .dropdown-toggle[aria-expanded="true"]::after, + .s-header .s-cms-nav .dropdown-toggle[aria-expanded="true"]::before { + /* To approximate design */ + margin-bottom: 32px; + } + .s-header .s-cms-nav .dropdown-toggle[aria-expanded="true"]::before { + content: 'All'; + flex-grow: 1; + + /* Style */ + font-size: 18px; /* WARNING: Do not use `rem` until `html { 62.5%; }` */ + } + .s-header .s-cms-nav .dropdown-toggle[aria-expanded="true"] .nav-text { + flex-basis: 100%; } } @@ -917,7 +1119,7 @@ Styleguide Trumps.Scopes.Header .s-header .dropdown-item { border-left: env(--border-width--x-thick) solid transparent; - font-weight: env(--medium); + font-weight: env(--bold); } @@ -928,7 +1130,7 @@ Styleguide Trumps.Scopes.Header --nav-link-vert-pad: 0.4em; /* To overwrite Bootstrap */ - padding: var(--nav-link-vert-pad) var(--menu-link-horz-pad); + padding: var(--nav-link-vert-pad) var(--menu-link-horz-pad, 0); } @@ -936,16 +1138,24 @@ Styleguide Trumps.Scopes.Header /* Dropdown Item: Separator */ /* To create line between each item */ -.s-header .dropdown-item + .dropdown-item { - /* To ensure any background color does not cover box-shadow */ - position: relative; +.s-header .s-cms-nav .dropdown-item + .dropdown-item { + @extend %s-header-menu-link-border; +} - box-shadow: - /* x pos. */ 0 - /* y pos. */ calc(-1 * (1px + var(--menu-link-horz-pad))) - /* blur */ 0 - /* spread */ calc(-1 * var(--menu-link-horz-pad)) - /* color */ var(--menu-line-color); +/* Dropdown Item: Separator: Desktop */ + +@media (--nav-expanded) { + .s-header .s-portal-nav .dropdown-item + .dropdown-item { + @extend %s-header-menu-link-border; + } +} + +/* Dropdown Item: Separator: Mobile */ + +@media (--nav-compressed) { + .s-header .s-portal-nav .dropdown-item + .dropdown-item { + @extend %s-header-nav-link-border; + } } @@ -1014,17 +1224,16 @@ Styleguide Trumps.Scopes.Header } @media (--nav-compressed) { .s-header .s-search-bar { - /* NOTE: Deviates from design cuz "Mobile Nav Top Row" height deviates */ - --input-height: 38px; + --input-height: 45px; /* WARNING: Do not use `rem` until `html { 62.5%; }` */ --button-font-size: 24px; --input-font-size: 18px; /* To match design */ - --space-after-search: var(--space-before-search); + --space-after-search: 0; - /* To line up logo with navbar left-hand content */ - --space-before-search: env(--header-navbar-mobile-left-pad); + /* To let padding manage space */ + --space-before-search: 0; } } @@ -1121,56 +1330,23 @@ Styleguide Trumps.Scopes.Header @media (--nav-compressed) { - /* To move portal nav & search bar to top of navbar */ - /* FAQ: The `.show` & `.collapsing` allow Bootstrap `display: none` to work */ - .s-header .navbar-collapse.show, - .s-header .navbar-collapse.collapsing { + /* To move portal nav & search bar to top of menu */ + .s-header .navbar-collapse:is(.show, .collapsing), + .s-header .s-portal-nav .dropdown.show { display: flex; flex-direction: column; } - .s-header .s-search-bar { order: -2; } - - - - /* To layout the button */ - .s-header :--portal-nav-link { - width: fit-content; - - /* To avoid wrapping of button */ - /* FAQ: Wrap happens if logo is wide and button layout is not flexible */ - /* NOTE: The `clamp()` solution avoids using third media query for navbar */ - padding-right: clamp( - var(--nav-link-horz-pad), - 3vw, - env(--header-navbar-mobile-right-pad) - ); /* FAQ: The widths must prevent wrap when `#header-logo` is max. width */ - } + .s-header .navbar-collapse:is(.show, .collapsing) .s-search-bar { order: -2; } + .s-header :--portal-message { order: -1; } - /* To layout the button (height) */ - /* FAQ: We set height of immediate content of elements, because: - - height for elements is not respected as navbar collapses - - height for portal nav would hide its dropdown menu - */ - .s-header .s-search-bar::part(form), - .s-header :--portal-nav-link { - height: var(--header-navbar-height); - } + /* To create a bar at top of menu */ + .s-header .navbar-collapse:is(.show, .collapsing) .s-search-bar, + .s-header :--portal-message { + padding-left: var(--menu-link-horz-pad, 0); + padding-right: var(--menu-link-horz-pad, 0); - /* To make a fake background */ - /* HACK: Create background with a pseudo element that matches "row" height */ - .s-header .s-portal-nav { position: relative; } - .s-header .s-portal-nav::before { - content: ''; - position: absolute; - left: 0; - top: 0; - height: 100%; - width: 100%; - z-index: -1; background-color: env(--header-bkgd-color); } - /* To ensure search bar can be focused i.e. is above the fake background */ - .s-header .s-search-bar { z-index: 1; } } @@ -1210,7 +1386,7 @@ Styleguide Trumps.Scopes.Header /* To add space between icon and text for ONLY portal nav top-level and CMS */ .s-header .s-portal-nav .dropdown-item .icon, .s-header .s-cms-nav .icon { - margin-right: 0.5em; + display: none; } } diff --git a/taccsite_cms/static/site_cms/css/src/_themes/constants.json b/taccsite_cms/static/site_cms/css/src/_themes/constants.json index 88a165b43..58a3140e3 100644 --- a/taccsite_cms/static/site_cms/css/src/_themes/constants.json +++ b/taccsite_cms/static/site_cms/css/src/_themes/constants.json @@ -19,8 +19,8 @@ "--header-font-family": "Roboto, sans-serif", "--header-accent-color": "#877453", "--header-brandbar-normal-height": "40px", - "--header-brandbar-mobile-height": "33px", - "--header-navbar-normal-height": "55px", + "--header-brandbar-mobile-height": "32px", + "--header-navbar-normal-height": "56px", "--header-navbar-mobile-height": "48px", "--header-navbar-toggle-width": "48px", "// To offer a static width for var-width element": "use only as needed", @@ -28,7 +28,7 @@ "--header-navbar-toggle-width--portal-user": "110px", "--header-navbar-mobile-right-pad": "28px", - "--header-navbar-mobile-left-pad": "23px", + "--header-navbar-mobile-left-pad": "24px", "--header-navbar-mobile-menu-offset": "67px", "// To line up logo with navbar left-hand content": "", diff --git a/taccsite_cms/static/site_cms/css/src/site.header.css b/taccsite_cms/static/site_cms/css/src/site.header.css index 89def7aa3..d6339e15b 100644 --- a/taccsite_cms/static/site_cms/css/src/site.header.css +++ b/taccsite_cms/static/site_cms/css/src/site.header.css @@ -17,6 +17,7 @@ /* COMPONENTS */ +@import url("_imports/components/bootstrap.navbar.css"); @import url("_imports/components/c-branding.css"); @import url("_imports/components/c-logo.css"); diff --git a/taccsite_cms/static/site_cms/js/site.header.js b/taccsite_cms/static/site_cms/js/site.header.js new file mode 100644 index 000000000..867bcbd67 --- /dev/null +++ b/taccsite_cms/static/site_cms/js/site.header.js @@ -0,0 +1,43 @@ +/** + * Header-relevant scripts that must only run after header imports are complete + * (and cannot be placed after relevant markup without requiring duplication) + * @module + */ +// FAQ: Use of jQuery should remain limited to directly hooking into Bootstrap + + + +const ELEMENT_NAV_PORTAL = document.querySelector('.s-header .s-portal-nav'); +const ELEMENT_NAV_CMS = document.querySelector('.s-header .s-cms-nav'); + + + +// To prevent Portal menu from closing if user clicks on it +// NOTE: Wes tested this in different locations with unsuccessful results: +// - within portal nav markup, did not work across all sites +// - after portal nav container, needed duplicate across all sites +$(ELEMENT_NAV_PORTAL).on('click', '.dropdown.show', (showEvent) => { + showEvent.stopPropagation(); +}); + + + +// FAQ: This allows menu style differences based on child dropdown state +/** + * To add classname, for CSS, to menu, when a child dropdown is open + * @param {HTMLElement} menu - The parent menu + */ +function updateMenuClassname(menu) { + const openMenuClassname = 'js-has-open-menu'; + const openMenus = menu.querySelectorAll('.dropdown.show'); + const hasOpenMenus = (openMenus && openMenus.length > 0); + + if (hasOpenMenus) + menu.classList.add(openMenuClassname); + else + menu.classList.remove(openMenuClassname); +} + +$(ELEMENT_NAV_CMS).on('hidden.bs.dropdown shown.bs.dropdown', () => { + updateMenuClassname(ELEMENT_NAV_CMS); +}); diff --git a/taccsite_cms/templates/header.html b/taccsite_cms/templates/header.html index cb6938dc5..e457ad0e8 100644 --- a/taccsite_cms/templates/header.html +++ b/taccsite_cms/templates/header.html @@ -1,6 +1,7 @@ {# WARNING: Some markup is duplicated in other repositories #} {# SEE: https://confluence.tacc.utexas.edu/x/LoCnCQ #} {# FAQ: Extra lines exist to ease CMS/Portal/Guide template comparison #} +{% load static %} {% include "header_branding.html" %} @@ -12,14 +13,8 @@ {% include "header_logo.html" %} - - - -