diff --git a/docs/front-end/breakpoints.md b/docs/front-end/breakpoints.md index a316f4200..78b6d3a07 100644 --- a/docs/front-end/breakpoints.md +++ b/docs/front-end/breakpoints.md @@ -13,7 +13,9 @@ $breakpoints: ( 'x-large' '(min-width: 1280px)', // secondary breakpoints - use sparingly 'small' '(min-width: 410px)', - 'xx-large' '(min-width: 1800px)' + 'menu' '(min-width: 800px)', + 'xx-large' '(min-width: 1440px)', + 'xxx-large' '(min-width: 1800px)' ); ``` diff --git a/package-lock.json b/package-lock.json index 6e0997fd6..78572bcfb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "license": "UNLICENSED", "dependencies": { "js-cookie": "^3.0.5", - "lite-youtube-embed": "^0.3.2" + "lite-youtube-embed": "^0.3.2", + "swiper": "^11.2.1" }, "devDependencies": { "@types/jest": "^29.5.6", @@ -12434,6 +12435,24 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/swiper": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.1.tgz", + "integrity": "sha512-62G69+iQRIfUqTmJkWpZDcX891Ra8O9050ckt1/JI2H+0483g+gq0m7gINecDqMtDh2zt5dK+uzBRxGhGOOvQA==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index d06c1da4f..a5d21b808 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ }, "dependencies": { "js-cookie": "^3.0.5", - "lite-youtube-embed": "^0.3.2" + "lite-youtube-embed": "^0.3.2", + "swiper": "^11.2.1" } } diff --git a/tbx/project_styleguide/templates/patterns/atoms/sprites/sprites.html b/tbx/project_styleguide/templates/patterns/atoms/sprites/sprites.html index 1d83ea528..3268ce73c 100644 --- a/tbx/project_styleguide/templates/patterns/atoms/sprites/sprites.html +++ b/tbx/project_styleguide/templates/patterns/atoms/sprites/sprites.html @@ -39,6 +39,14 @@ + + + + + + + + diff --git a/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.html b/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.html index 60ff7d776..459b358cb 100644 --- a/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.html +++ b/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.html @@ -1,17 +1,28 @@ -
- {% if value.static_text %}{{ value.static_text }}{% endif %} +
+ {% if value.static_text %}

{{ value.static_text }}

{% endif %} {% if value.dynamic_text %} {% if value.dynamic_text|length > 1 %} - {# If there's more than one dynamic text, show the controls for the loop. #} -
    - {% for text in value.dynamic_text %} -
  • {{ text }}
  • - {% endfor %} -
+ {# If there's more than one dynamic text, show Swiper carousel structure and controls for the loop. #} +
+
    + {% for text in value.dynamic_text %} +
  • + {# Fixed px values required to control the font size for all browsers and prevent slide overflow#} +

    {{ text }}

    +
  • + {% endfor %} +
+
+
+ + + + +
{% else %} - {# If there's only one dynamic text, don't show the controls for the loop. #} + {# If there's only one dynamic text, don't show Swiper carousel structure and controls for the loop. #} {% for text in value.dynamic_text %} - {{ text }} +

{{ text }}

{% endfor %} {% endif %} {% endif %} diff --git a/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.yaml b/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.yaml index bd89cbbb3..8c3da66ed 100644 --- a/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.yaml +++ b/tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/dynamic_hero_block.yaml @@ -2,6 +2,8 @@ context: value: static_text: We help charities and nonprofits dynamic_text: - - 'future-proof your funding streams.' - - 'transform lives through digital innovation.' - 'increase supporter acquisition and retention.' + - 'deliver life-changing digital services.' + - 'scale social impact through technology.' + - 'transform lives through digital innovation.' + - 'future-proof your funding streams.' diff --git a/tbx/project_styleguide/templates/patterns/molecules/streamfield/stream_block_division.html b/tbx/project_styleguide/templates/patterns/molecules/streamfield/stream_block_division.html deleted file mode 100644 index 2d497788d..000000000 --- a/tbx/project_styleguide/templates/patterns/molecules/streamfield/stream_block_division.html +++ /dev/null @@ -1,7 +0,0 @@ -{% load wagtailcore_tags %} - -{% if value %} - {% for block in value %} - {% include_block block with unique_id=block.id %} - {% endfor %} -{% endif %} diff --git a/tbx/project_styleguide/templates/patterns/molecules/streamfield/stream_block_division.yaml b/tbx/project_styleguide/templates/patterns/molecules/streamfield/stream_block_division.yaml deleted file mode 100644 index a84df542a..000000000 --- a/tbx/project_styleguide/templates/patterns/molecules/streamfield/stream_block_division.yaml +++ /dev/null @@ -1,8 +0,0 @@ -context: - value: - - dummy - -tags: - include_block: - block with unique_id=block.id: - template_name: 'patterns/_pattern_library_only/streamfield/division_story_container.html' diff --git a/tbx/project_styleguide/templates/patterns/pages/divisions/division_page.html b/tbx/project_styleguide/templates/patterns/pages/divisions/division_page.html index 510baa7ea..ead81114b 100644 --- a/tbx/project_styleguide/templates/patterns/pages/divisions/division_page.html +++ b/tbx/project_styleguide/templates/patterns/pages/divisions/division_page.html @@ -4,13 +4,9 @@ {% block content %}
-
-

{{ page.title }}

+

{{ page.title }}

-

{{ page.label }}

-
- -
{% include_block page.hero %}
+
{% include_block page.hero %}
{% include_block page.body %}
diff --git a/tbx/project_styleguide/templates/patterns/styleguide/components/components.html b/tbx/project_styleguide/templates/patterns/styleguide/components/components.html index 3473469d2..c46af4656 100644 --- a/tbx/project_styleguide/templates/patterns/styleguide/components/components.html +++ b/tbx/project_styleguide/templates/patterns/styleguide/components/components.html @@ -174,6 +174,11 @@

Home page hero

{% include "patterns/molecules/home-page-hero/home-page-hero.html" %}
+
+

Dynamic hero

+ {% include "patterns/molecules/streamfield/blocks/dynamic_hero_block.html" %} +
+

Values block

{% include "patterns/molecules/streamfield/blocks/values_block.html" %} diff --git a/tbx/static_src/javascript/components/dynamic-hero.js b/tbx/static_src/javascript/components/dynamic-hero.js new file mode 100644 index 000000000..872de97b9 --- /dev/null +++ b/tbx/static_src/javascript/components/dynamic-hero.js @@ -0,0 +1,72 @@ +import Swiper from 'swiper'; +// eslint-disable-next-line import/no-unresolved +import { Autoplay } from 'swiper/modules'; + +export default class DynamicHero { + static selector() { + return '[data-dynamic-hero]'; + } + + constructor(node) { + this.node = node; + this.swiperContainer = this.node.querySelector('.swiper'); + this.slides = this.node.querySelectorAll('.swiper-slide'); + this.nextButton = this.node.querySelector('[data-dynamic-hero-next]'); + this.prevButton = this.node.querySelector('[data-dynamic-hero-prev]'); + this.pauseButton = this.node.querySelector('[data-dynamic-hero-pause]'); + this.playButton = this.node.querySelector('[data-dynamic-hero-play]'); + this.bindEvents(); + } + + bindEvents() { + if (!this.swiperContainer) { + return; + } + + // Check if the user prefers reduced motion - don't autoplay if they do + const isReduced = + window.matchMedia('(prefers-reduced-motion: reduce)').matches === + true; + + this.swiper = new Swiper(this.swiperContainer, { + modules: [Autoplay], + direction: 'vertical', + slidesPerView: 1, + speed: 1000, + loop: true, + autoplay: { + delay: 5000, + enabled: !isReduced, + }, + }); + + if (this.nextButton) { + this.nextButton.addEventListener('click', () => + this.swiper.slideNext(), + ); + } + + if (this.prevButton) { + this.prevButton.addEventListener('click', () => + this.swiper.slidePrev(), + ); + } + + if (this.pauseButton) { + this.pauseButton.addEventListener('click', () => { + this.swiper.autoplay.stop(); + this.pauseButton.classList.add('hidden'); + this.playButton.classList.remove('hidden'); + }); + } + + if (this.playButton) { + this.playButton.addEventListener('click', () => { + this.swiper.autoplay.start(); + this.swiper.slideNext(); + this.playButton.classList.add('hidden'); + this.pauseButton.classList.remove('hidden'); + }); + } + } +} diff --git a/tbx/static_src/javascript/main.js b/tbx/static_src/javascript/main.js index 8636b23d1..d6111e12e 100755 --- a/tbx/static_src/javascript/main.js +++ b/tbx/static_src/javascript/main.js @@ -8,6 +8,7 @@ import YouTubeConsentManager from './components/youtube-embed'; import Tabs from './components/tabs'; import TableHint from './components/table-hint'; import ModeSwitcher from './components/mode-switcher'; +import DynamicHero from './components/dynamic-hero'; // IE11 polyfills import foreachPolyfill from './polyfills/foreach-polyfill'; @@ -37,5 +38,6 @@ document.addEventListener('DOMContentLoaded', () => { initComponent(Tabs); initComponent(TableHint); initComponent(ModeSwitcher); + initComponent(DynamicHero); new DesktopCloseMenus(); }); diff --git a/tbx/static_src/sass/components/_dynamic-hero.scss b/tbx/static_src/sass/components/_dynamic-hero.scss new file mode 100644 index 000000000..fe67f58b8 --- /dev/null +++ b/tbx/static_src/sass/components/_dynamic-hero.scss @@ -0,0 +1,74 @@ +@use 'config' as *; + +// Set fixed height to prevent Swiper's infinite height issue https://swiperjs.com/get-started#swiper-css-stylessize +.dynamic-hero { + .swiper { + max-width: 100%; + height: 380px; + + @include media-query(medium) { + height: 240px; + } + + @include media-query(menu) { + height: 160px; + } + + @include media-query(large) { + height: 320px; + } + + @include media-query(xx-large) { + height: 250px; + } + } + + .swiper-slide { + padding-bottom: 20px; + } + + &__controls { + display: flex; + } + + &__control { + display: flex; + align-items: center; + justify-content: center; + height: 40px; + width: 40px; + opacity: 1; + visibility: visible; + transition: $transition; + + &:hover, + &:focus { + color: var(--color--link-interaction); + } + + &:focus { + @include focus-style(); + } + + &--prev { + transform: rotate(90deg); + } + + &--next { + transform: rotate(-90deg); + } + + &--pause, + &--play { + height: 40px; + width: 40px; + } + + &.hidden { + opacity: 0; + visibility: hidden; + pointer-events: none; + position: absolute; + } + } +} diff --git a/tbx/static_src/sass/components/_grid.scss b/tbx/static_src/sass/components/_grid.scss index 14da784e2..33edbc667 100644 --- a/tbx/static_src/sass/components/_grid.scss +++ b/tbx/static_src/sass/components/_grid.scss @@ -42,6 +42,15 @@ } } + &__dynamic-hero { + grid-column: 2 / span 4; + margin-bottom: $spacer-medium; + + @include media-query(large) { + grid-column: 2 / span 12; + } + } + &__intro { margin-bottom: $spacer-medium; grid-column: 2 / span 4; diff --git a/tbx/static_src/sass/components/_image.scss b/tbx/static_src/sass/components/_image.scss index 5185ae0d7..2ea8831fe 100644 --- a/tbx/static_src/sass/components/_image.scss +++ b/tbx/static_src/sass/components/_image.scss @@ -44,7 +44,7 @@ margin-right: $spacer-medium; } - @include media-query(xx-large) { + @include media-query(xxx-large) { margin-right: 0; } } diff --git a/tbx/static_src/sass/config/_variables.scss b/tbx/static_src/sass/config/_variables.scss index 09fc23188..e2a68a8d9 100755 --- a/tbx/static_src/sass/config/_variables.scss +++ b/tbx/static_src/sass/config/_variables.scss @@ -287,8 +287,9 @@ $breakpoints: ( 'x-large' '(min-width: 1280px)', // secondary breakpoints - use sparingly 'small' '(min-width: 410px)', - 'xx-large' '(min-width: 1800px)', - 'menu' '(min-width: 800px)' + 'menu' '(min-width: 800px)', + 'xx-large' '(min-width: 1440px)', + 'xxx-large' '(min-width: 1800px)' ); // Layout diff --git a/tbx/static_src/sass/main.scss b/tbx/static_src/sass/main.scss index fc56c53e1..20b3d85c4 100755 --- a/tbx/static_src/sass/main.scss +++ b/tbx/static_src/sass/main.scss @@ -1,5 +1,6 @@ // CSS from external vendors (available as npm) @use '../../../node_modules/lite-youtube-embed/src/lite-yt-embed.css'; +@use '../../../node_modules/swiper/swiper.scss'; // Base @use 'base/base'; @@ -19,6 +20,7 @@ @use 'components/contact-cta'; @use 'components/cookie-message'; @use 'components/division-signpost'; +@use 'components/dynamic-hero'; @use 'components/employee-owned-icon'; @use 'components/event-block'; @use 'components/featured-case-study';