├── .gitignore ├── mock.jpg ├── favicon.ico ├── images ├── hero.jpg ├── pull-1.jpg ├── footer-drupal.svg ├── background.svg ├── search.svg ├── search--white.svg └── rss.svg ├── fonts ├── metropolis │ ├── .DS_Store │ ├── Metropolis-Bold.woff │ ├── Metropolis-Bold.woff2 │ ├── Metropolis-Regular.woff │ └── Metropolis-Regular.woff2 └── lora │ ├── lora-v14-latin-700.woff │ ├── lora-v14-latin-700.woff2 │ ├── lora-v14-latin-italic.woff │ ├── lora-v14-latin-italic.woff2 │ ├── lora-v14-latin-regular.woff │ └── lora-v14-latin-regular.woff2 ├── CONTRIBUTING.md ├── src ├── css │ ├── components │ │ ├── byline.css │ │ ├── search-button-mobile.css │ │ ├── header-buttons-mobile.css │ │ ├── breadcrumb.css │ │ ├── hero.css │ │ ├── footer.css │ │ ├── social-container.css │ │ ├── nav-secondary.css │ │ ├── nav-button-mobile.css │ │ ├── nav-button-wide.css │ │ ├── embedded-media.css │ │ ├── nav-primary-button.css │ │ ├── table.css │ │ ├── header-search-narrow.css │ │ ├── main.css │ │ ├── header.css │ │ ├── header-search-wide.css │ │ └── nav-primary.css │ ├── base │ │ ├── _debug.css │ │ ├── forms.css │ │ ├── utility.css │ │ ├── layout.css │ │ ├── base.css │ │ ├── fonts.css │ │ └── variables.css │ └── style.css └── js │ ├── polyfills.js │ ├── search.js │ ├── second-level-navigation.js │ ├── navigation.js │ └── scripts.js ├── package.json ├── Gulpfile.js ├── README.md ├── LICENSE.txt └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /mock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/mock.jpg -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/favicon.ico -------------------------------------------------------------------------------- /images/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/images/hero.jpg -------------------------------------------------------------------------------- /images/pull-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/images/pull-1.jpg -------------------------------------------------------------------------------- /fonts/metropolis/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/metropolis/.DS_Store -------------------------------------------------------------------------------- /fonts/lora/lora-v14-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/lora/lora-v14-latin-700.woff -------------------------------------------------------------------------------- /fonts/lora/lora-v14-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/lora/lora-v14-latin-700.woff2 -------------------------------------------------------------------------------- /fonts/lora/lora-v14-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/lora/lora-v14-latin-italic.woff -------------------------------------------------------------------------------- /fonts/lora/lora-v14-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/lora/lora-v14-latin-italic.woff2 -------------------------------------------------------------------------------- /fonts/lora/lora-v14-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/lora/lora-v14-latin-regular.woff -------------------------------------------------------------------------------- /fonts/metropolis/Metropolis-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/metropolis/Metropolis-Bold.woff -------------------------------------------------------------------------------- /fonts/metropolis/Metropolis-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/metropolis/Metropolis-Bold.woff2 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | The contribution guide can be found here: https://www.drupal.org/docs/8/themes/olivero/how-to-contribute-to-olivero 2 | -------------------------------------------------------------------------------- /fonts/lora/lora-v14-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/lora/lora-v14-latin-regular.woff2 -------------------------------------------------------------------------------- /fonts/metropolis/Metropolis-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/metropolis/Metropolis-Regular.woff -------------------------------------------------------------------------------- /fonts/metropolis/Metropolis-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lullabot/olivero-poc/HEAD/fonts/metropolis/Metropolis-Regular.woff2 -------------------------------------------------------------------------------- /src/css/components/byline.css: -------------------------------------------------------------------------------- 1 | .hero__byline { 2 | color: var(--color--gray-20); 3 | font-size: 14px; 4 | line-height: var(--sp); 5 | 6 | a { 7 | font-weight: bold; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/css/base/_debug.css: -------------------------------------------------------------------------------- 1 | /* .mock { 2 | position: absolute; 3 | left: 1px; 4 | top: 1px; 5 | z-index: 50; 6 | opacity: 0.5; 7 | pointer-events: none; 8 | filter: invert(10%); 9 | } 10 | 11 | .site-branding { 12 | background: red !important; 13 | } */ 14 | -------------------------------------------------------------------------------- /src/css/components/search-button-mobile.css: -------------------------------------------------------------------------------- 1 | /* .mobile-search-button { 2 | display: none; 3 | -webkit-appearance: none; 4 | background: transparent; 5 | padding: 0 var(--sp); 6 | border: 0; 7 | cursor: pointer; 8 | 9 | @media (--nav-md) { 10 | display: block; 11 | } 12 | } */ 13 | -------------------------------------------------------------------------------- /src/css/components/header-buttons-mobile.css: -------------------------------------------------------------------------------- 1 | .mobile-buttons { 2 | grid-column: 4 / 7; 3 | -ms-grid-column-align: end; 4 | margin-left: auto; 5 | display: flex; 6 | 7 | @media (--sm) { 8 | align-self: end; 9 | } 10 | 11 | @media (--grid-md) { 12 | grid-column: 11 / 15; 13 | } 14 | 15 | @media (--nav) { 16 | body:not(.is-always-mobile-nav) & { 17 | display: none; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/css/components/breadcrumb.css: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | grid-column: 1 / 7; 3 | text-transform: uppercase; 4 | letter-spacing: 0.2em; 5 | font-size: 14px; 6 | line-height: var(--sp); 7 | 8 | @media (--grid-md) { /* 700px */ 9 | grid-column: 3 / 13; 10 | } 11 | 12 | @media (--lg) { 13 | grid-column: 3 / 11; 14 | } 15 | 16 | ul { 17 | margin: 0; 18 | padding: 0; 19 | list-style: none; 20 | } 21 | 22 | a { 23 | color: var(--color--blue-20); 24 | text-decoration: none; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/js/polyfills.js: -------------------------------------------------------------------------------- 1 | if (window.NodeList && !NodeList.prototype.forEach) { 2 | NodeList.prototype.forEach = function (callback, thisArg) { 3 | thisArg = thisArg || window; 4 | for (var i = 0; i < this.length; i++) { 5 | callback.call(thisArg, this[i], i, this); 6 | } 7 | }; 8 | } 9 | 10 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill 11 | if (!Element.prototype.matches) { 12 | Element.prototype.matches = Element.prototype.msMatchesSelector || 13 | Element.prototype.webkitMatchesSelector; 14 | } 15 | -------------------------------------------------------------------------------- /images/footer-drupal.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/css/components/hero.css: -------------------------------------------------------------------------------- 1 | .hero__content { 2 | grid-column: 1 / 7; 3 | 4 | @media (--grid-md) { /* 700px */ 5 | grid-column: 3 / 13; 6 | } 7 | 8 | @media (--lg) { 9 | grid-column: 3 / 11; 10 | } 11 | } 12 | 13 | .hero__img { 14 | grid-column: 1 / 7; 15 | margin: var(--sp2) 0; 16 | 17 | @media (--sm) { 18 | margin: var(--sp3) 0; 19 | } 20 | 21 | @media (--grid-md) { 22 | grid-column: 1 / 15; 23 | margin: var(--sp4) 0; 24 | } 25 | 26 | @media (--lg) { 27 | grid-column: 2 / 14; 28 | } 29 | 30 | img { 31 | width: 100%; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/css/base/forms.css: -------------------------------------------------------------------------------- 1 | .button--primary { 2 | display: inline-flex; 3 | align-items: center; 4 | padding: 0 18px; 5 | background-color: var(--color--blue-20); 6 | color: white; 7 | font-weight: bold; 8 | height: var(--sp2); 9 | border-radius: 2px; 10 | white-space: nowrap; 11 | transition: background-color 0.2s; 12 | text-decoration: none; 13 | 14 | &:focus, 15 | &:hover { 16 | background-color: var(--color--blue-50); 17 | } 18 | 19 | &:focus { 20 | outline: 2px solid var(--color--blue-70); 21 | outline-offset: 1px; 22 | } 23 | 24 | &:active { 25 | background-color: var(--color--blue-20); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /images/background.svg: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Olivero-PoC", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gulp build", 8 | "watch": "gulp watch", 9 | "start": "gulp watch" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@babel/core": "^7.6.0", 15 | "@babel/preset-env": "^7.6.0", 16 | "autoprefixer": "^9.6.1", 17 | "gulp": "^4.0.2", 18 | "gulp-babel": "^8.0.0", 19 | "gulp-postcss": "^8.0.0", 20 | "gulp-sourcemaps": "^2.6.5", 21 | "perfectionist": "^2.4.0", 22 | "postcss-calc": "^7.0.1", 23 | "postcss-import": "^12.0.1", 24 | "postcss-nested": "^4.1.2", 25 | "postcss-rtl": "^1.5" 26 | }, 27 | "devDependencies": { 28 | "postcss-custom-media": "^7.0.8", 29 | "postcss-custom-properties": "^9.0.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/css/base/utility.css: -------------------------------------------------------------------------------- 1 | .visually-hidden { 2 | position: absolute; 3 | clip: rect(1px, 1px, 1px, 1px); 4 | overflow: hidden; 5 | height: 1px; 6 | width: 1px; 7 | word-wrap: normal; 8 | 9 | &.focusable { 10 | &:focus { 11 | clip: auto; 12 | overflow: visible; 13 | height: auto; 14 | width: auto; 15 | word-wrap: normal; 16 | } 17 | } 18 | } 19 | 20 | .skip-link { 21 | z-index: 1; /* Appear above header */ 22 | outline: 0; 23 | text-decoration: none; 24 | 25 | @media (--nav) { 26 | body:not(.is-always-mobile-nav) & { 27 | margin-left: var(--content-left); 28 | padding: var(--sp); 29 | color: white; 30 | font-size: 14px; 31 | font-weight: bold; 32 | } 33 | } 34 | 35 | &:after { 36 | content: '➔'; 37 | margin-left: 5px; 38 | transition: 0.2s; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | @import 'base/_debug.css'; 2 | @import 'base/fonts.css'; 3 | @import 'base/variables.css'; 4 | @import 'base/base.css'; 5 | @import 'base/forms.css'; 6 | @import 'base/layout.css'; 7 | @import 'base/utility.css'; 8 | 9 | /* Components */ 10 | @import 'components/breadcrumb.css'; 11 | @import 'components/byline.css'; 12 | @import 'components/embedded-media.css'; 13 | @import 'components/header.css'; 14 | @import 'components/hero.css'; 15 | @import 'components/footer.css'; 16 | @import 'components/nav-button-mobile.css'; 17 | @import 'components/nav-button-wide.css'; 18 | @import 'components/nav-primary.css'; 19 | @import 'components/nav-primary-button.css'; 20 | @import 'components/nav-secondary.css'; 21 | @import 'components/header-buttons-mobile.css'; 22 | @import 'components/header-search-narrow.css'; 23 | @import 'components/header-search-wide.css'; 24 | @import 'components/social-container.css'; 25 | @import 'components/search-button-mobile.css'; 26 | @import 'components/main.css'; 27 | @import 'components/table.css'; 28 | -------------------------------------------------------------------------------- /src/css/base/layout.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding-left: var(--sp); 3 | padding-right: var(--sp); 4 | width: 100%; 5 | max-width: var(--max-width); 6 | 7 | @media (--nav) { 8 | body:not(.is-always-mobile-nav) & { 9 | padding-left: var(--sp2); 10 | padding-right: var(--sp2); 11 | } 12 | } 13 | } 14 | 15 | .grid-full { 16 | display: grid; 17 | grid-template-columns: repeat(var(--grid-col-count), minmax(0, 1fr)); 18 | grid-template-rows: 1fr; 19 | grid-column-gap: var(--grid-gap); 20 | 21 | @media (--grid-md) { 22 | grid-template-columns: repeat(var(--grid-col-count--md), minmax(0, 1fr)); 23 | grid-column-gap: var(--grid-gap--md); 24 | } 25 | } 26 | 27 | .page-wrapper { 28 | max-width: var(--max-bg-color); 29 | background: white; 30 | } 31 | 32 | .content { 33 | @media (--nav) { 34 | body:not(.is-always-mobile-nav) & { 35 | display: flex; 36 | flex-direction: row-reverse; 37 | } 38 | } 39 | } 40 | 41 | main { 42 | @media (--nav) { 43 | body:not(.is-always-mobile-nav) & { 44 | margin-right: auto; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/css/components/footer.css: -------------------------------------------------------------------------------- 1 | .site-footer { 2 | position: relative; /* stack above left social bar */ 3 | background: black; 4 | 5 | @media (--nav) { 6 | body:not(.is-always-mobile-nav) & { 7 | padding-left: var(--content-left); 8 | } 9 | } 10 | } 11 | 12 | .site-footer__inner { 13 | padding: var(--sp2) 0; 14 | background: linear-gradient(180deg, #0C0D0E 0%, #171E23 100%); 15 | 16 | @media (--nav) { 17 | body:not(.is-always-mobile-nav) & { 18 | padding-top: var(--sp4); 19 | padding-bottom: calc(13 * var(--sp)); 20 | } 21 | } 22 | } 23 | 24 | .powered-by { 25 | grid-column: 1 / -1; 26 | color: var(--color--gray-40); 27 | letter-spacing: 0.02em; 28 | line-height: var(--sp); 29 | font-size: 14px; 30 | 31 | @media(--nav) { 32 | body:not(.is-always-mobile-nav) & { 33 | grid-column-start: 3; 34 | } 35 | } 36 | 37 | a { 38 | color: white; 39 | 40 | &:hover, 41 | &:focus { 42 | text-decoration: none; 43 | } 44 | } 45 | 46 | img { 47 | display: inline-block; 48 | vertical-align: middle; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /images/search.svg: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/css/components/social-container.css: -------------------------------------------------------------------------------- 1 | .social-container { 2 | @media (--nav) { 3 | width: var(--content-left); 4 | flex-shrink: 0; 5 | background-color: var(--color--gray-95); 6 | } 7 | } 8 | 9 | .rotate { 10 | display: flex; 11 | align-items: center; 12 | justify-content: flex-end; 13 | 14 | @media (--nav) { 15 | writing-mode: vertical-rl; 16 | transform: rotate(180deg); 17 | width: var(--content-left); 18 | text-align: right; /* IE11 */ 19 | 20 | img { 21 | margin: 10px 5px 10px 10px; /* IE11 */ 22 | 23 | @supports (display: block) { /* Override IE11 values. */ 24 | margin: 10px; 25 | } 26 | } 27 | } 28 | } 29 | 30 | .social-label { 31 | font-size: 12px; 32 | letter-spacing: 4px; 33 | text-transform: uppercase; 34 | color: var(--color--gray-25); 35 | } 36 | 37 | .social-container__inner { 38 | padding: var(--sp0-5) var(--sp); 39 | background-color: var(--color--gray-95); 40 | 41 | @media (--nav) { 42 | position: relative; 43 | width: var(--content-left); 44 | padding: calc(9 * var(--sp)) 0; 45 | 46 | &.js-fixed { 47 | position: fixed; 48 | top: var(--sp6); 49 | left: 0; 50 | height: calc(100vh - 6 * var(--sp)) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /images/search--white.svg: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/css/base/base.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | font-family: var(--font-sans); 8 | line-height: var(--sp); 9 | background-color: #F7F9FA; 10 | background-image: url('/images/background.svg'); 11 | background-position: top left; 12 | 13 | &.js-fixed { 14 | position: fixed; 15 | overflow: hidden; 16 | 17 | &.is-always-mobile-nav { 18 | width: 100%; /* Prevent width from collapsing. */ 19 | } 20 | } 21 | } 22 | 23 | a { 24 | color: inherit; 25 | } 26 | 27 | img { 28 | display: block; 29 | max-width: 100%; 30 | } 31 | 32 | h1 { 33 | margin-top: var(--sp); 34 | margin-bottom: var(--sp); 35 | font-weight: bold; 36 | font-size: 24px; 37 | line-height: var(--sp1-5); 38 | letter-spacing: -0.01em; 39 | color: var(--color--gray-0); 40 | 41 | @media (--md) { 42 | margin-top: var(--sp2); 43 | margin-bottom: var(--sp2); 44 | font-size: 60px; 45 | line-height: var(--sp4); 46 | } 47 | } 48 | 49 | .overlay { 50 | display: none; 51 | position: fixed; 52 | top: 0; 53 | left: 0; 54 | width: 100%; 55 | height: 100vh; 56 | background: var(--color--blue-20); 57 | opacity: 0.2; 58 | 59 | .js-overlay-active & { 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /images/rss.svg: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/js/search.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | const searchWideButton = document.querySelector('.header-nav__search-button'); 3 | const searchWideWrapper = document.querySelector('.search-wide__wrapper'); 4 | const siteHeader = document.querySelector('.site-header'); 5 | 6 | function toggleSearchVisibility(visibility) { 7 | searchWideButton.setAttribute('aria-expanded', visibility == true); 8 | searchWideWrapper.addEventListener('transitionend', handleFocus, { once: true }); 9 | 10 | if (visibility == true) { 11 | searchWideWrapper.classList.add('is-active'); 12 | } 13 | else { 14 | searchWideWrapper.classList.remove('is-active'); 15 | } 16 | } 17 | 18 | drupalSettings.olivero.toggleSearchVisibility = toggleSearchVisibility; 19 | 20 | function handleFocus() { 21 | if (searchIsVisible()) { 22 | searchWideWrapper.querySelector('input[type="search"]').focus(); 23 | } 24 | else { 25 | searchWideButton.focus(); 26 | } 27 | } 28 | 29 | function searchIsVisible() { 30 | return searchWideWrapper.classList.contains('is-active'); 31 | } 32 | drupalSettings.olivero.searchIsVisible = searchIsVisible; 33 | 34 | document.addEventListener('click', e => { 35 | if (e.target.matches('.header-nav__search-button, .header-nav__search-button *')) { 36 | toggleSearchVisibility(!searchIsVisible()); 37 | } 38 | else if (searchIsVisible() && !e.target.matches('.search-wide__wrapper, .search-wide__wrapper *')) { 39 | toggleSearchVisibility(false); 40 | } 41 | }); 42 | })(); 43 | -------------------------------------------------------------------------------- /src/css/base/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'metropolis'; 3 | font-weight: normal; 4 | font-style: normal; 5 | font-display: swap; 6 | src: url('/fonts/metropolis/Metropolis-Regular.woff2') format('woff2'), 7 | url('/fonts/metropolis/Metropolis-Regular.woff') format('woff'); 8 | } 9 | 10 | @font-face { 11 | font-family: 'metropolis'; 12 | font-weight: 700; 13 | font-style: normal; 14 | font-display: swap; 15 | src: url('/fonts/metropolis/Metropolis-Bold.woff2') format('woff2'), 16 | url('/fonts/metropolis/Metropolis-Bold.woff') format('woff'); 17 | } 18 | 19 | /* lora-regular - latin */ 20 | @font-face { 21 | font-family: 'Lora'; 22 | font-style: normal; 23 | font-weight: 400; 24 | font-display: swap; 25 | src: local('Lora Regular'), local('Lora-Regular'), 26 | url('/fonts/lora/lora-v14-latin-regular.woff2') format('woff2'), 27 | url('/fonts/lora/lora-v14-latin-regular.woff') format('woff'); 28 | } 29 | /* lora-italic - latin */ 30 | @font-face { 31 | font-family: 'Lora'; 32 | font-style: italic; 33 | font-weight: 400; 34 | font-display: swap; 35 | src: local('Lora Italic'), local('Lora-Italic'), 36 | url('/fonts/lora/lora-v14-latin-italic.woff2') format('woff2'), 37 | url('/fonts/lora/lora-v14-latin-italic.woff') format('woff'); 38 | } 39 | /* lora-700 - latin */ 40 | @font-face { 41 | font-family: 'Lora'; 42 | font-style: normal; 43 | font-weight: 700; 44 | font-display: swap; 45 | src: local('Lora Bold'), local('Lora-Bold'), 46 | url('/fonts/lora/lora-v14-latin-700.woff2') format('woff2'), 47 | url('/fonts/lora/lora-v14-latin-700.woff') format('woff'); 48 | } 49 | -------------------------------------------------------------------------------- /src/css/components/nav-secondary.css: -------------------------------------------------------------------------------- 1 | .secondary-nav__wrapper { 2 | display: flex; 3 | margin: var(--sp2) 0; 4 | 5 | @media (--nav) { 6 | body:not(.is-always-mobile-nav) & { 7 | justify-content: flex-end; 8 | margin: 0; 9 | } 10 | } 11 | } 12 | 13 | .secondary-nav { 14 | text-transform: uppercase; 15 | letter-spacing: 0.07em; 16 | font-size: 14px; 17 | 18 | @media (--nav) { 19 | body:not(.is-always-mobile-nav) & { 20 | position: relative; 21 | display: flex; 22 | margin-left: var(--sp0-5); 23 | padding-left: var(--sp2); 24 | 25 | &:before { 26 | content: ''; 27 | position: absolute; 28 | left: 0; 29 | top: 50%; 30 | transform: translatey(-50%); 31 | height: var(--sp2); 32 | width: 2px; 33 | background-color: var(--color--gray-70); 34 | } 35 | } 36 | } 37 | 38 | ul { 39 | list-style: none; 40 | margin: 0; 41 | padding: 0; 42 | display: flex; 43 | align-items: center; 44 | } 45 | 46 | li:not(:last-child) { 47 | margin-right: var(--sp1-5); 48 | 49 | @media (--nav) { 50 | body:not(.is-always-mobile-nav) & { 51 | margin-right: var(--sp2); 52 | } 53 | } 54 | } 55 | 56 | a:not(.button--primary) { 57 | position: relative; 58 | display: inline-flex; 59 | align-items: center; 60 | height: var(--sp2); 61 | text-decoration: none; 62 | 63 | &:after { 64 | content: ''; 65 | position: absolute; 66 | left: 0; 67 | bottom: 0; 68 | width: 100%; 69 | height: 0; 70 | border-top: solid 2px currentColor; 71 | opacity: 0; 72 | transform: translatey(5px); 73 | transition: opacity 0.2s, transform 0.2s; 74 | } 75 | 76 | &:focus, 77 | &:hover { 78 | &:after { 79 | opacity: 0.8; 80 | transform: translatey(0); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const sourcemaps = require('gulp-sourcemaps'); 3 | 4 | /** 5 | * JS Compilation 6 | */ 7 | gulp.task('js', () => { 8 | const babel = require('gulp-babel'); 9 | // const eslint = require('gulp-eslint'); 10 | 11 | return gulp.src('./src/js/*.js') 12 | .pipe(sourcemaps.init()) 13 | // .pipe(eslint()) 14 | // .pipe(eslint.format()) 15 | // .pipe(eslint.failAfterError()) 16 | .pipe(babel({ 17 | presets: ['@babel/preset-env'] 18 | })) 19 | .pipe(sourcemaps.write('.')) 20 | .pipe(gulp.dest('./dist/')) 21 | }); 22 | 23 | /** 24 | * CSS Compilation 25 | */ 26 | gulp.task('css', () => { 27 | const autoprefixer = require('autoprefixer') 28 | const postcss = require('gulp-postcss'); 29 | const rtl = require("postcss-rtl"); 30 | const nested = require('postcss-nested'); 31 | const calc = require('postcss-calc'); 32 | const atImport = require('postcss-import'); 33 | const perfectionist = require('perfectionist'); 34 | const postcssCustomMedia = require('postcss-custom-media'); 35 | const postcssCustomProperties = require('postcss-custom-properties'); 36 | 37 | 38 | return gulp.src('./src/css/style.css') 39 | .pipe(sourcemaps.init()) 40 | .pipe(postcss([ 41 | atImport(), 42 | nested(), 43 | postcssCustomMedia(), 44 | postcssCustomProperties({ 45 | preserve: false 46 | }), 47 | calc(), 48 | autoprefixer({ 49 | grid: 'no-autoplace' 50 | }), 51 | perfectionist({ 52 | indentSize: 2 53 | }), 54 | rtl(), 55 | ])) 56 | .pipe(sourcemaps.write('.')) 57 | .pipe(gulp.dest('./dist/')) 58 | }); 59 | 60 | function watchFiles() { 61 | gulp.watch(['./src/css/**/*.css'], css); 62 | gulp.watch(['./src/js/**/*.js'], js); 63 | } 64 | 65 | const css = gulp.series(['css']); 66 | const js = gulp.series(['js']); 67 | const build = gulp.series(gulp.parallel(css, js)); 68 | 69 | exports.watch = gulp.series([build, watchFiles]); 70 | exports.build = build; 71 | -------------------------------------------------------------------------------- /src/css/components/nav-button-mobile.css: -------------------------------------------------------------------------------- 1 | 2 | /* Mobile */ 3 | .mobile-nav-button { 4 | position: relative; 5 | z-index: 10; /* appear above mobile nav */ 6 | align-self: center; 7 | display: flex; 8 | align-items: center; 9 | -webkit-appearance: none; 10 | background: transparent; 11 | position: relative; 12 | width: var(--sp2); 13 | height: var(--sp2); 14 | margin-left: auto; 15 | padding: 0; 16 | border: none; 17 | cursor: pointer; 18 | 19 | @media (--sm) { 20 | display: inline-flex; 21 | width: auto; 22 | padding-left: var(--sp); 23 | } 24 | } 25 | 26 | /* Text that says "menu" */ 27 | .mobile-nav-button__label { 28 | position: absolute; 29 | clip: rect(1px, 1px, 1px, 1px); 30 | overflow: hidden; 31 | height: 1px; 32 | width: 1px; 33 | word-wrap: normal; 34 | 35 | @media (--sm) { 36 | position: static; 37 | clip: auto; 38 | overflow: visible; 39 | height: auto; 40 | width: auto; 41 | text-transform: uppercase; 42 | letter-spacing: 0.07em; 43 | font-size: 14px; 44 | font-weight: 600; 45 | margin-right: 12px; 46 | } 47 | } 48 | 49 | .mobile-nav-button__icon { 50 | position: relative; 51 | width: var(--sp2); 52 | height: 3px; 53 | background-color: var(--color--blue-50); 54 | 55 | &:before { 56 | content: ''; 57 | position: absolute; 58 | top: -8px; 59 | left: 0; 60 | height: 3px; 61 | width: 100%; 62 | background-color: var(--color--blue-50); 63 | transition: all 0.2s; 64 | } 65 | 66 | &:after { 67 | content: ''; 68 | position: absolute; 69 | top: auto; 70 | left: 0; 71 | bottom: -8px; 72 | height: 3px; 73 | width: 100%; 74 | background-color: var(--color--blue-50); 75 | transition: all 0.2s; 76 | } 77 | 78 | .mobile-nav-button[aria-expanded="true"] & { 79 | background-color: transparent; 80 | 81 | &:before { 82 | top: 0; 83 | transform: rotate(-45deg); 84 | } 85 | 86 | &:after { 87 | top: 0; 88 | transform: rotate(45deg); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Olivero - Proof of Concept HTML 2 | 3 | ## Overview 4 | This repository contains prototyping work for the new Drupal new front-end theme called Olivero. The new design that we're proposing in Olivero includes new components/features that are different from its predecessor. We'd like to utilize this static HTML page as a way to validate the new features and help answer potential UI/UX issues that might arise during the design process. 5 | 6 | The following are key items that we're examining: 7 | - Investigate the use of the header on scroll interaction on mobile and tablet devices. 8 | - Validate the use of the CSS grid in legacy browsers such as Internet Explorer 11 and identify if progressive enhancement features will need to be accounted for. 9 | - Verify that the markup is semantic and meets the accessibility requirements. 10 | 11 | This proof of concept is still a work-in-progress and not all of the elements meet accessibility guidelines. Our plan is to give a full sign-off from the Drupal accessibility team, which will hopefully alleviate last-minute time-crunches when the patch is submitted to Drupal core. 12 | 13 | ## Example 14 | 15 | The proof of concept is currently hosted on Netlify. Please check out the full build at https://olivero-poc.netlify.com. 16 | 17 | ## Reporting bugs 18 | 19 | If you need to submit a bug report for this POC, create an issue in the [Olivero issue queue](https://www.drupal.org/project/issues/olivero?component=Proof+of+Concept), with `Proof of Concept` selected as the component. Please follow the Drupal.org issue guidelines as it helps us and the community better understand your report, reproduce the bug, and find related issues. 20 | 21 | ## Installation 22 | 23 | ### Prerequisites: 24 | 25 | - Node (12+) 26 | 27 | ### Install Dev Dependencies 28 | 29 | `$ npm install` 30 | 31 | ## Usage 32 | 33 | `$ npm start` or `$ gulp watch` 34 | 35 | This will watch for any styles and JS changes. 36 | 37 | `$ npm run build` or `$ gulp build` 38 | 39 | This will handle the compilation of all the CSS files, creation of accompanying source maps, concatenation of all CSS files into one CSS stylesheet within the `/dist` directory. 40 | -------------------------------------------------------------------------------- /src/css/components/nav-button-wide.css: -------------------------------------------------------------------------------- 1 | /* Desktop */ 2 | 3 | .nav-primary__button { 4 | display: none; 5 | 6 | @media (--nav) { 7 | visibility: hidden; 8 | display: flex; 9 | flex-shrink: 0; 10 | align-items: center; 11 | justify-content: center; 12 | width: var(--content-left); 13 | height: calc(6 * var(--sp)); 14 | border: 0; 15 | cursor: pointer; 16 | background-color: var(--color--blue-50); 17 | outline: 0; 18 | 19 | &:focus { 20 | border: solid 1px transparent; /* Will show in IE/Edge high contrast mode. */ 21 | } 22 | 23 | body:not(.is-always-mobile-nav) .js-fixed & { 24 | visibility: visible; 25 | } 26 | 27 | body.is-always-mobile-nav & { 28 | visibility: hidden; 29 | } 30 | } 31 | } 32 | 33 | .nav-primary__icon { 34 | position: relative; 35 | width: var(--sp2); 36 | height: 21px; 37 | pointer-events: none; 38 | transform-style: preserve-3d; 39 | transition: transform 1s; 40 | opacity: 0; 41 | transition: all 0.2s; 42 | 43 | .js-fixed & { 44 | opacity: 1; 45 | } 46 | 47 | [aria-expanded="true"] & { 48 | > div:nth-child(1) { 49 | top: 9px; 50 | transform: rotate(-45deg); 51 | } 52 | 53 | > div:nth-child(2) { 54 | opacity: 0; 55 | } 56 | 57 | > div:nth-child(3) { 58 | top: 9px; 59 | transform: rotate(45deg); 60 | } 61 | } 62 | 63 | > div { 64 | height: 0; 65 | border-top: solid 3px white; 66 | 67 | &:nth-child(1) { 68 | position: absolute; 69 | top: 0; 70 | left: 0; 71 | height: 0; 72 | width: 100%; 73 | background-color: white; 74 | transition: all 0.2s; 75 | } 76 | 77 | &:nth-child(2) { 78 | position: absolute; 79 | top: 9px; 80 | left: 0; 81 | height: 0; 82 | width: 100%; 83 | background-color: white; 84 | transition: opacity 0.2s; 85 | } 86 | 87 | &:nth-child(3) { 88 | position: absolute; 89 | top: auto; 90 | left: 0; 91 | bottom: 0; 92 | height: 0; 93 | width: 100%; 94 | background-color: white; 95 | transition: all 0.2s; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/css/components/embedded-media.css: -------------------------------------------------------------------------------- 1 | figure { 2 | background: var(--color--gray-95); 3 | } 4 | 5 | figcaption { 6 | padding: var(--sp0-5); 7 | font-family: var(--font-serif); 8 | font-size: 14px; 9 | font-style: italic;; 10 | line-height: var(--sp); 11 | color: var(--color--gray-10); 12 | 13 | @media (--sm) { 14 | padding: var(--sp); 15 | } 16 | } 17 | 18 | .pull-right { 19 | margin: var(--sp3) 0; 20 | max-width: 100%; 21 | 22 | @media (--grid-md) { 23 | float: right; 24 | width: calc(5 * var(--grid-col-width--md) + 4 * var(--grid-gap--md)); 25 | margin: var(--sp) 0; 26 | margin-right: calc(-2 * ((var(--grid-col-width--md) + var(--grid-gap--md)))); 27 | margin-left: var(--sp); 28 | } 29 | 30 | @media (--lg) { 31 | width: calc(5 * var(--grid-col-width--lg) + 4 * var(--grid-gap--lg)); 32 | margin-right: calc(-2 * ((var(--grid-col-width--lg) + var(--grid-gap--lg)))); 33 | } 34 | 35 | @media (--nav) { 36 | width: calc(4 * var(--grid-col-width--nav) + 3 * var(--grid-gap--nav)); 37 | margin-right: calc(-3 * ((var(--grid-col-width--nav) + var(--grid-gap--nav)))); 38 | } 39 | 40 | @media (--grid-max) { 41 | width: calc(4 * var(--grid-col-width--max) + 3 * var(--grid-gap--max)); 42 | margin-right: calc(-3 * ((var(--grid-col-width--max) + var(--grid-gap--max)))); 43 | } 44 | } 45 | 46 | .pull-left { 47 | margin: var(--sp3) 0; 48 | max-width: 100%; 49 | 50 | @media (--grid-md) { 51 | float: left; 52 | width: calc(5 * var(--grid-col-width--md) + 4 * var(--grid-gap--md)); 53 | margin: var(--sp) 0; 54 | margin-left: calc(-2 * ((var(--grid-col-width--md) + var(--grid-gap--md)))); 55 | margin-right: var(--sp); 56 | } 57 | 58 | @media (--lg) { 59 | width: calc(5 * var(--grid-col-width--lg) + 4 * var(--grid-gap--lg)); 60 | margin-left: calc(-1 * ((var(--grid-col-width--lg) + var(--grid-gap--lg)))); 61 | } 62 | 63 | @media (--nav) { 64 | width: calc(4 * var(--grid-col-width--nav) + 3 * var(--grid-gap--nav)); 65 | margin-left: calc(-1 * ((var(--grid-col-width--nav) + var(--grid-gap--nav)))); 66 | } 67 | 68 | @media (--grid-max) { 69 | width: calc(4 * var(--grid-col-width--max) + 3 * var(--grid-gap--max)); 70 | margin-left: calc(-1 * ((var(--grid-col-width--max) + var(--grid-gap--max)))); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/css/components/nav-primary-button.css: -------------------------------------------------------------------------------- 1 | /* 2 | Button that expands second level nav when clicked. 3 | */ 4 | .primary-nav__button-toggle { 5 | -webkit-appearance: none; 6 | border: 0; 7 | background: transparent; 8 | position: relative; 9 | width: var(--sp2); 10 | height: var(--sp2); 11 | padding: 0; 12 | text-indent: -999px; 13 | overflow: hidden; 14 | cursor: pointer; 15 | 16 | @media (--nav) { 17 | body:not(.is-always-mobile-nav) & { 18 | align-self: stretch; 19 | width: calc(var(--sp2) + 8px); 20 | height: auto; 21 | margin-right: calc(-1 * var(--sp2)); 22 | 23 | &:focus { 24 | outline: 0; 25 | border: 0; 26 | 27 | .icon--menu-toggle { 28 | border: solid 1px transparent; 29 | background-color: var(--color--blue-50); 30 | 31 | &:after { 32 | border-color: white; 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | .icon--menu-toggle { 40 | position: absolute; 41 | top: 50%; 42 | left: 50%; 43 | transform: translate(-50%, -50%); 44 | width: 16px; 45 | height: 16px; 46 | border-radius: 2px; 47 | transition: background-color 0.2s; 48 | 49 | @media (--nav) { 50 | body:not(.is-always-mobile-nav) & { 51 | left: 3px; 52 | transform: translatey(-50%); 53 | } 54 | } 55 | 56 | &:before, 57 | &:after { 58 | content: ''; 59 | position: absolute; 60 | top: 50%; 61 | left: 50%; 62 | transform: translate(-50%, -50%); 63 | width: var(--sp); 64 | height: 0; 65 | border-top: solid 3px var(--color--blue-50); 66 | 67 | @media (--nav) { 68 | body:not(.is-always-mobile-nav) & { 69 | content: none; 70 | } 71 | } 72 | } 73 | 74 | &:after { 75 | transform: translate(-50%, -50%) rotate(90deg); 76 | transition: opacity 0.2s; 77 | 78 | @media (--nav) { 79 | body:not(.is-always-mobile-nav) & { 80 | content: ''; 81 | top: calc(50% - 2px); 82 | left: 3px; 83 | height: 8px; 84 | width: 8px; 85 | border-right: solid 2px currentColor; 86 | border-bottom: solid 2px currentColor; 87 | border-top: none; 88 | transform: translatey(-50%) rotate(45deg); 89 | background: transparent; 90 | opacity: 0.8; 91 | } 92 | } 93 | } 94 | } 95 | 96 | 97 | &[aria-expanded="true"] { 98 | .icon--menu-toggle:after { 99 | opacity: 0; 100 | 101 | @media (--nav) { 102 | body:not(.is-always-mobile-nav) & { 103 | opacity: 0.8; 104 | } 105 | } 106 | } 107 | } 108 | 109 | html:not(.js) & { 110 | display: none; /* Hide if JS is disabled */ 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/js/second-level-navigation.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | const isDesktopNav = drupalSettings.olivero.isDesktopNav; 4 | const secondLevelNavMenus = document.querySelectorAll('.primary-nav--level-1 .has-children'); 5 | 6 | // Add hover and click event listeners onto each subnav parent and it's button. 7 | secondLevelNavMenus.forEach(el => { 8 | const button = el.querySelector('.primary-nav__button-toggle'); 9 | button.addEventListener('click', e => { 10 | const topLevelMenuITem = e.currentTarget.parentNode; 11 | toggleSubNav(topLevelMenuITem); 12 | }); 13 | 14 | el.addEventListener('mouseover', e => { 15 | if (isDesktopNav()) { 16 | toggleSubNav(e.currentTarget, true); 17 | } 18 | }); 19 | 20 | el.addEventListener('mouseout', e => { 21 | if (isDesktopNav()) { 22 | toggleSubNav(e.currentTarget, false); 23 | } 24 | }); 25 | }); 26 | 27 | /** 28 | * Close all second level subnav menus. 29 | */ 30 | function closeAllSubNav() { 31 | secondLevelNavMenus.forEach(el => { 32 | toggleSubNav(el, false); 33 | }); 34 | } 35 | 36 | drupalSettings.olivero.closeAllSubNav = closeAllSubNav; 37 | 38 | /** 39 | * Checks if any subnavigation items are currently active. 40 | * @returns {boolean} 41 | */ 42 | function areAnySubnavsOpen() { 43 | let subNavsAreOpen = false; 44 | 45 | secondLevelNavMenus.forEach(el => { 46 | const button = el.querySelector('.primary-nav__button-toggle'); 47 | const state = button.getAttribute('aria-expanded') === 'true'; 48 | 49 | if (state) { 50 | subNavsAreOpen = true; 51 | } 52 | }); 53 | 54 | return subNavsAreOpen; 55 | } 56 | 57 | drupalSettings.olivero.areAnySubnavsOpen = areAnySubnavsOpen; 58 | 59 | /** 60 | * Shows and hides the specified menu item's second level submenu. 61 | * 62 | * @param {element} topLevelMenuITem - the