Banner

A banner should contain a singular slide or several slides and a call to action, which should contain a title and a lead sentence.

Due to the usability considerations of carousels avoid adding important information or links within the slides that is not available elsewhere on the page, as some users may have difficulty discovering that information or even operating the carousel. Do not add more than 5 slides to the banner, as it is unlikely many users will interact with a carousel at all.

When to use

Use on home pages or section pages and provide links that are related to the context of the current section or page.

When not to use

Do not use on pages that are not home pages or section pages.

Image guidance

General image guidance is available on the image documentation page.

Images are displayed at various sizes dependent on viewing device and orientation. Banner images should always have a 2:1 (width:height) aspect ratio. The required dimension for a banner image is 1200px x 600px. If smaller images are used, they will be stretched and will suffer from degradation in quality, so it is essential to use the correct size of image.

CMS systems which render images to specific sizes (eg. by utilising scripts which specify sizing parameters) should set the dimensions to be 1200px x 600px.

Image specific context parameters are:

  • ‘src’ - relative path to image (Required)
  • ‘alt’ - descriptive text (Required)

A full context example is provided below.

Configuration

Configuration guidance

Within the banners array, each “slide” should be contained within the items array and include a title a lead, an img object with alt and src, and a link which contains the url and the link text.

Configuration example

'banners': [
  {
    'items': [
      {
        'title': 'Lorem Ipsum Dolor sit Amet Consectetural',
        'lead': 'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas repellendus necessitatibus harum quo, nemo magni, dolorem natus atque provident suscipit itaque sit perspiciatis!',
        'img': {
          'src': '/placeholders/banner/banner-01.jpg',
          'alt': 'University campus'
        },
        'link': {
          'text': 'Some call to action',
          'url': '/some-cta-link'
        }
      },
      ...
    ]
  }
]
{% for banner in banners %}
  <section aria-label="Featured" class="uol-banner-outer">
    <div class="uol-banner-container {{ 'uol-banner-container--multiple' if banner.items.length > 1 }}">
      <{{ 'ol' if banner.items.length > 1 else 'div'}} tabindex="0" class="uol-banner {{ 'uol-banner--multiple' if banner.items.length > 1 }}">
        {% for item in banner.items %}
          <{{ 'li' if banner.items.length > 1 else 'div'}} class="uol-banner__item">
            <h3 class="uol-banner__item__title">{{ item.title }}</h3>

            {% if item.img.src %}
              <img class="uol-banner__item__img" src="{{ item.img.src }}" alt="{{ item.img.alt }}" loading="lazy">
            {% endif %}

            {% if item.lead %}
              <p class="uol-banner__item__lead">{{ item.lead }}</p>
            {% endif %}

            {% if item.link %}
              <a class="uol-banner__item__link" href="{{ item.link.url }}">{{ item.link.text }}</a>
            {% endif %}
          </{{ 'li' if banner.items.length > 1 else 'div'}}>
        {% endfor %}
      </{{ 'ol' if banner.items.length > 1 else 'div'}}>
    </div>
  </section>
{% endfor %}
<section aria-label="Featured" class="uol-banner-outer">
    <div class="uol-banner-container ">
        <div tabindex="0" class="uol-banner ">

            <div class="uol-banner__item">
                <h3 class="uol-banner__item__title">Lorem Ipsum Dolor sit Amet Consectetural</h3>

                <img class="uol-banner__item__img" src="/placeholders/banner/banner-01.jpg" alt="University campus" loading="lazy">

                <p class="uol-banner__item__lead">Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas repellendus necessitatibus harum quo, nemo magni, dolorem natus atque provident suscipit itaque sit perspiciatis!</p>

                <a class="uol-banner__item__link" href="/some-cta-link">Some call to action</a>

            </div>

        </div>
    </div>
</section>
  • Content:
    - [ ] Add Aria notice
    - [x] Scroll into view on focus
    - [x] Handle focus if currently focused button becomes disabled
    
  • URL: /components/raw/uol-banner/TODo.md
  • Filesystem Path: src/library/02-components/banner/TODo.md
  • Size: 118 Bytes
  • Content:
    .uol-banner-outer {
      background: $color-black;
      overflow: hidden;
    }
    
    .uol-banner-container {
      box-sizing: border-box;
      max-width: $site-container-full;
      margin: 0 auto;
      position: relative;
      color: $color-white;
    
      @include media(">=uol-media-l") {
        padding: 0 $spacing-4;
      }
    }
    
      .uol-banner-container--multiple {
        @include media(">=uol-media-l") {
          padding-bottom: $spacing-4;
        }
      }
    
    .uol-banner {
      list-style: none;
      margin: 0;
      padding: 0 0 $spacing-6;
      display: flex;
      height: auto;
    }
    
      .uol-banner--multiple {
        @include ds-scrollbars($horizontal: true);
    
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        scroll-behavior: smooth;
    
        > * {
          flex: 0 0 auto;
        }
    
        > * + * {
          margin-left: 1rem;
        }
    
        @include media(">=uol-media-l") {
          max-width: calc(100% - 5rem);
        }
      }
    
     .uol-banner__item {
       position: relative;
       display: flex;
       flex-direction: column;
       box-sizing: border-box;
       padding: 0 $spacing-4 $spacing-6;
       width: 100%;
       transition: opacity 0.2s ease;
    
       @include media(">=uol-media-l") {
        width: 100%;
        display: block;
        padding-bottom: $spacing-4;
    
        // Keep min ratio to avoid crop of absolutely positioned .uol-banner__item__img
        &::before {
          content: "";
          display: block;
          width: 0;
          height: 0;
          padding-bottom: 33%;
          float: right;
        }
      }
    
       .uol-banner--multiple & {
        scroll-snap-align: start;
        width: 87%;
    
        @include media(">=uol-media-m") {
          width: 90%;
        }
    
        @include media(">=uol-media-l") {
          width: 100%;
          display: block;
        }
       }
     }
    
      .uol-banner__item--unfocused {
        opacity: 0.5;
      }
    
      .uol-banner__item__title {
        @extend .uol-typography-heading-2;
    
        margin: $spacing-5 0 $spacing-2;
    
        @include media(">=uol-media-s") {
          margin-top: $spacing-6;
        }
    
        @include media(">=uol-media-l") {
          width: 36%;
          margin-top: $spacing-7;
        }
    
        @include media(">=uol-media-xl") {
          width: 28%;
          margin-top: $spacing-8;
        }
      }
    
      .uol-banner__item__lead {
        @extend .uol-typography-paragraph;
    
        margin: 0 0 $spacing-2;
    
        @include media(">=uol-media-l") {
          width: 36%;
        }
    
        @include media(">=uol-media-xl") {
          width: 28%;
        }
      }
    
      .uol-banner__item__img {
        order: -1;
        width: calc(100% + #{$spacing-4} * 2);
        margin: 0 -#{$spacing-4};
        height: auto;
        min-height: 1px; // IE11 hack - don't ask why this works
    
        @include media(">=uol-media-l") {
          width: 62%;
          position: absolute;
          top: $spacing-6;
          right: 0;
        }
    
        @include media(">=uol-media-xl") {
          width: 70%;
          top: 0;
        }
      }
    
      .uol-banner__item__link {
        display: inline-block;
        margin: $spacing-2 0 0;
        font-size: 1.125rem;
        font-weight: $font-weight-medium--sans-serif;
    
        @include media(">=uol-media-m") {
          font-weight: $font-weight-bold--sans-serif;
        }
    
        @include media(">=uol-media-l") {
          max-width: 36%;
        }
    
        @include media(">=uol-media-xl") {
          max-width: 28%;
        }
    
        &::before {
          content: "";
          position: absolute;
          top: 0;
          right: 0;
          bottom: 0;
          left: 0;
        }
    
        .js & {
          text-decoration: none;
    
          &:hover,
          &:focus {
            text-decoration: underline;
            text-decoration-color: $color-brand--bright;
          }
        }
    
        svg {
          position: relative;
          margin-top: -0.35em;
          top: 0.35em;
          left: 0;
          margin-left: 0.5em;
          fill: $color-brand--bright;
          transition: all 0.3s ease 0.2s;
    
          @media (-ms-high-contrast: active) {
            fill: windowText;
          }
        }
    
        &:hover,
        &:focus {
          svg {
            left: 0.4em;
          }
        }
      }
    
      .uol-banner__button {
        // Override icon-button specificity
        .js .uol-banner-container &.uol-icon--icon-only {
          position: absolute;
        }
    
        right: calc(13% - #{$spacing-2});
        transform: translateX(50%);
        transition: all 0.5s ease;
    
        &:disabled {
          opacity: 0.7;
        }
    
        @include media(">=uol-media-m") {
          right: calc(10% - #{$spacing-2});
        }
    
        @include media(">=uol-media-l") {
          right: 3.125rem;
        }
      }
    
        .uol-banner__button--prev {
          top: $spacing-6;
          @include button_focus(-5px, true);
    
          @include media(">=uol-media-xs") {
            top: calc(15% - 30px);
          }
    
          @include media(">=uol-media-s") {
            top: calc(20% - 30px);
          }
    
          @include media(">=uol-media-l") {
            top: 33%;
            transform: translateX(50%) translateY(-50%);
          }
        }
    
        .uol-banner__button--next {
          top: 5.5rem;
          @include button_focus(-5px, true);
    
          @include media(">=uol-media-xs") {
            top: calc(15% + 30px);
          }
    
          @include media(">=uol-media-s") {
            top: calc(20% + 30px);
          }
    
          @include media(">=uol-media-l") {
            top: calc(33% + 4rem);
            transform: translateX(50%) translateY(-50%);
          }
        }
    
      .uol-banner__counter {
        position: absolute;
        bottom: $spacing-6;
        left: $spacing-4;
        font-variant-numeric: lining-nums;
    
        @include media(">=uol-media-l") {
          left: auto;
          bottom: auto;
          top: 20%;
          right: $spacing-7;
          transform: translateX(30%);
          padding-bottom: 0.2em;
          border-bottom: 1px solid $color-border;
        }
      }
    
        .uol-banner__counter__current {
          color: $color-brand--bright;
        }
    
        .uol-banner__counter__total {
          &::before {
            content: " / ";
          }
        }
    
  • URL: /components/raw/uol-banner/_banner.scss
  • Filesystem Path: src/library/02-components/banner/_banner.scss
  • Size: 5.5 KB
  • Content:
    // TODO: Arrows: Should be a global utility
    export const bannerArrows = () => {
    
      const svgRightArrow = `<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" focusable="false" aria-hidden="true">
        <path d="M0 0h24v24H0z" fill="none"/>
        <path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/>
      </svg>`
    
      const links = document.querySelectorAll('.uol-banner__item__link')
    
      links.forEach( (item) => {
        const innerTextArray = item.innerText.trim().split(' ')
    
        // Wrap last word and svg in no-wrap span to avoid wrapping
        item.innerHTML = '<span role="text">' + innerTextArray.slice(0, -1).join(' ') + ' <span class="no-wrap">' + innerTextArray[innerTextArray.length - 1] + '' + svgRightArrow + '</span><span>'
      })
    }
    
    
    export const bannerCarousel = () => {
    
      const banners = document.querySelectorAll('.uol-banner--multiple')
    
      banners.forEach((banner) => {
        const container = banner.closest('.uol-banner-container')
        const items = banner.querySelectorAll('.uol-banner__item')
    
        // Create Previous button
        const buttonPrev = document.createElement('button')
        buttonPrev.type = 'button'
        buttonPrev.innerText = 'Select previous banner item'
        buttonPrev.classList.add('uol-button')
        buttonPrev.classList.add('uol-button--bright')
        buttonPrev.classList.add('uol-icon')
        buttonPrev.classList.add('uol-icon--icon-only')
        buttonPrev.classList.add('uol-icon--mdiArrowLeft')
        buttonPrev.classList.add('uol-banner__button')
        buttonPrev.classList.add('uol-banner__button--prev')
        buttonPrev.disabled = true
    
        // Create Next button
        const buttonNext = document.createElement('button')
        buttonNext.type = 'button'
        buttonNext.innerText = 'Select next banner item'
        buttonNext.classList.add('uol-button')
        buttonNext.classList.add('uol-button--bright')
        buttonNext.classList.add('uol-icon')
        buttonNext.classList.add('uol-icon--icon-only')
        buttonNext.classList.add('uol-icon--mdiArrowRight')
        buttonNext.classList.add('uol-banner__button')
        buttonNext.classList.add('uol-banner__button--next')
        buttonNext.dataset.galleryModalTarget = 1
    
        // Append both buttons
        container.appendChild(buttonPrev)
        container.appendChild(buttonNext)
    
        // Append skip link
    
        const skipLink = document.createElement('a')
        skipLink.classList.add('uol-skip-link')
        skipLink.setAttribute('href', '#main')
        skipLink.innerText = 'Skip carousel'
        banner.insertAdjacentElement('beforebegin', skipLink)
    
    
        // Create counter
        const counter = document.createElement('span')
        counter.classList.add('uol-banner__counter')
        counter.setAttribute('role', 'status')
        counter.innerHTML = `
          <span role="text">
            <span class="hide-accessible">Item </span>
            <span class="uol-banner__counter__current">1</span>
            <span class="hide-accessible"> of </span>
            <span class="uol-banner__counter__total">${items.length}</span>
          </span>
        `
    
        // Append counter
        container.appendChild(counter)
    
        // Remove tabindex from scrollable container - not needed when JS enabled
        banner.removeAttribute('tabindex')
    
        // Get counter current so we can update it on scroll
        const countContainer = container.querySelector('.uol-banner__counter__current')
    
        // Fade out all but first
        items.forEach((item) => {
          item.classList.add('uol-banner__item--unfocused')
        })
        items[0].classList.remove('uol-banner__item--unfocused')
    
        let isScrolling = null;
    
        banner.addEventListener('scroll', () => {
    
          let bannerWidth = banner.clientWidth
    
          window.clearTimeout( isScrolling )
    
          isScrolling = setTimeout(() => {
    
            items.forEach((item, itemIndex) => {
              let itemLeft = item.getBoundingClientRect().x
              if (!itemLeft) // Handle IE11
                  itemLeft = item.getBoundingClientRect().left
    
    
              // If item is more than half in view
              if (itemLeft >= -10 && itemLeft < (bannerWidth / 2) ) {
                  // Remove unfocused class
                  item.classList.remove('uol-banner__item--unfocused')
    
                  // Update the current counter
                  countContainer.innerText = itemIndex + 1
    
                  // Update buttons
                  if (itemIndex == 0) {
                      buttonNext.disabled = false
                      if (document.activeElement === buttonPrev) {
                        buttonNext.focus()
                      }
                      buttonPrev.disabled = true
                  } else if (itemIndex == items.length - 1 ) {
                      buttonPrev.disabled = false
                      if (document.activeElement === buttonNext) {
                        buttonPrev.focus()
                      }
                      buttonNext.disabled = true
                  } else {
                      buttonPrev.disabled = false
                      buttonNext.disabled = false
                  }
    
                  buttonPrev.dataset.galleryModalTarget = parseFloat(itemIndex) - 1
                  buttonNext.dataset.galleryModalTarget = parseFloat(itemIndex) + 1
              } else {
                // Add unfocused class
                item.classList.add('uol-banner__item--unfocused')
              }
            })
    
          }, 100);
        })
    
        // Listen for button clicks
        container.querySelectorAll('.uol-banner__button').forEach( (navBtn) => {
          navBtn.addEventListener('click', () => {
            let targetItem = items[navBtn.dataset.galleryModalTarget]
            banner.scrollLeft = targetItem.offsetLeft
          })
        })
    
        // Scroll to item on link focus
        items.forEach( (item) => {
          const itemLink = item.querySelector('.uol-banner__item__link')
          if (itemLink) {
            itemLink.addEventListener('focus', () => {
              banner.scrollLeft = item.offsetLeft
            })
          }
        })
    
      })
    }
    
  • URL: /components/raw/uol-banner/banner.module.js
  • Filesystem Path: src/library/02-components/banner/banner.module.js
  • Size: 5.8 KB
{
  "banners": [
    {
      "items": [
        {
          "title": "Lorem Ipsum Dolor sit Amet Consectetural",
          "lead": "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas repellendus necessitatibus harum quo, nemo magni, dolorem natus atque provident suscipit itaque sit perspiciatis!",
          "img": {
            "src": "/placeholders/banner/banner-01.jpg",
            "alt": "University campus"
          },
          "link": {
            "text": "Some call to action",
            "url": "/some-cta-link"
          }
        }
      ]
    }
  ]
}