diff --git a/bedrock/mozorg/templates/mozorg/home/home-m24.html b/bedrock/mozorg/templates/mozorg/home/home-m24.html index 9c43b5e422b..3083c1789aa 100644 --- a/bedrock/mozorg/templates/mozorg/home/home-m24.html +++ b/bedrock/mozorg/templates/mozorg/home/home-m24.html @@ -54,3 +54,9 @@ {% include 'includes/protocol/footer/footer.html' %} {% endwith %} {% endblock %} + +{% if switch('m24-hero-animation') %} + {% block js %} + {{ js_bundle('m24-animation') }} + {% endblock %} +{% endif %} diff --git a/bedrock/mozorg/templates/mozorg/home/includes/m24/hero.html b/bedrock/mozorg/templates/mozorg/home/includes/m24/hero.html index a69f790c276..e5391c517e2 100644 --- a/bedrock/mozorg/templates/mozorg/home/includes/m24/hero.html +++ b/bedrock/mozorg/templates/mozorg/home/includes/m24/hero.html @@ -4,16 +4,48 @@ file, You can obtain one at https://mozilla.org/MPL/2.0/. #} -
-
-
- -
+
+
+

{{ ftl('m24-home-welcome-to-mozilla') }}

{{ ftl('m24-home-from-trustworthy-tech') }}

{{ ftl('m24-home-learn-about-us') }}

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/l10n/en/ui.ftl b/l10n/en/ui.ftl index 96bcbe92da6..b3c725e26ce 100644 --- a/l10n/en/ui.ftl +++ b/l10n/en/ui.ftl @@ -20,6 +20,8 @@ ui-show-all = Show All ui-hide-all = Hide All ui-learn-more = Learn more ui-view = View +ui-pause-animation = Pause animation +ui-play-animation = Play animation # An accessible label used to describe the purpose of a cross-promotional page element. ui-promo-label = Promotion diff --git a/media/css/m24/flag.scss b/media/css/m24/flag.scss index 8ad503f3c46..618da08fa93 100644 --- a/media/css/m24/flag.scss +++ b/media/css/m24/flag.scss @@ -6,7 +6,7 @@ .m24-c-flag { @include container; - padding-top: $spacer-2xl; + padding-top: $spacer-xl; padding-bottom: $spacer-xl; } @@ -25,12 +25,79 @@ margin-bottom: $spacer-xl; } +.m24-c-flag-cta { + margin-bottom: 0; +} + .m24-c-flag-media { - display: block; - margin-bottom: 16px; + --delay: 1s; // leave some space to read before introducing movement + --wave-h: 8px; - img { + svg { + transform: translateZ(0); width: 100px; + height: auto; + fill: $m24-color-green; // zilla green + } +} + +.m24-c-flag-button { + $font-size: 0.75; + background-color: $m24-color-dark-mid-gray; + border: none; + box-shadow: none; + color: $m24-color-white; + font-family: $primary-font; + font-size: calc(#{$font-size} * 1rem); + font-weight: 600; + line-height: $font-size; + padding: 8px; + cursor: pointer; + transition: background-color $fast; + + &:hover, + &:focus { + background-color: $m24-color-black; // inverts to white in dark theme section + } + + &:active { + background-color: $m24-color-dark-mid-gray; + } +} + +.m24-c-flag-button-pause, +.m24-c-flag-button-play { + align-items: center; + display: flex; + gap: 8px; +} + +.m24-c-flag-button-pause { + .m24-c-flag-button-text { + position: relative; + top: 0.0175rem; // flex alignment isn't totally centered, so we're manually adjusting + } +} + +@media screen and (max-width: #{$screen-lg - 1}) { + .m24-c-flag-cta { + margin-bottom: $spacer-xl; + } + + .m24-c-flag-button { + border-radius: 50%; + display: block; + margin-bottom: $spacer-md; + margin-inline-start: auto; + } + + .m24-c-flag-button-text { + @include visually-hidden; + } + + .m24-c-flag-media { + width: fit-content; + margin-inline-start: auto; } } @@ -44,21 +111,28 @@ @media #{$mq-lg} { .m24-c-flag { @include grid; + grid-template-rows: [button-row-start] auto [button-row-end] auto; padding-bottom: $spacer-2xl; } + .m24-c-flag-button { + grid-column: 1 / -1; + grid-row: button-row; + justify-self: end; + } + .m24-c-flag-subtitle { font-size: 24px; } .m24-c-flag-media { grid-column: 10/12; - grid-row: 1/2; + grid-row-start: button-row-end; display: flex; place-content: center center; margin-bottom: 0; - img { + svg { width: 100%; max-width: 216px; } @@ -66,7 +140,7 @@ .m24-c-flag-text { grid-column: 2/9; - grid-row: 1/2 + grid-row-start: button-row-end; } .m24-c-flag-cta { @@ -86,3 +160,143 @@ .m24-c-products { background-color: $m24-color-white; } + +// static fallback +.m24-c-flag-media-static { + display: none; +} + +// When no JS or M24_HERO_ANIMATION switch is off +// - hide animation SVG and button +// - show static SVG +.no-js, +[data-m24-hero-animation="false"] { + .m24-c-flag-button { + visibility: hidden; // reserve space + } + + .m24-c-flag-media-animation { + display: none; + } + + .m24-c-flag-media-static { + display: block; + } +} + +/* +Note from Inclusive Components, "Changing labels": https://inclusive-components.design/toggle-button/ +As a rule of thumb, you should never change pressed state and label together. +If the label changes, the button has already changed state in a sense, +just not via explicit WAI-ARIA state management. +*/ +[data-animation-running="false"] { + .m24-c-flag-button-pause { + display: none; + } + + .flag-one, + .flag-two, + .flag-three, + .flag-four, + .flag-five { + animation-play-state: paused; + } +} + +[data-animation-running="true"] { + .m24-c-flag-button-play { + display: none; + } + + .flag-one, + .flag-two, + .flag-three, + .flag-four, + .flag-five { + animation-play-state: running; + } +} + +/* Common Element Styles */ +.flag, +.flag-one, +.flag-two, +.flag-three, +.flag-four, +.flag-five, +.pole, +.head, +.pole-one, +.pole-two, +.eye, +.mouth { + shape-rendering: crispEdges; + -webkit-font-smoothing: none; + -moz-osx-font-smoothing: none; +} + +/* Wave Animations Base */ +.flag-one, +.flag-two, +.flag-three, +.flag-four, +.flag-five { + animation: 1s var(--delay) $bezier infinite; + will-change: transform; + filter: url("#dilate"); +} + +/* Flag Animation Names */ +.flag-one { animation-name: flag-one; } +.flag-two { animation-name: flag-two; } +.flag-three { animation-name: flag-three; } +.flag-four { animation-name: flag-four; } +.flag-five { animation-name: flag-five; } + + +/* Flag Movement Keyframes */ +@keyframes flag-one { + 0%, 15% { + transform: translate3d(0, 0, 0); + animation-timing-function: ease-in; +} + 16%, 40% { transform: translate3d(0, var(--wave-h), 0); } + 41%, 65% { transform: translate3d(0, 0, 0); } + 66%, 91% { transform: translate3d(0, calc(-1 * var(--wave-h)), 0); } + 92%, 100% { transform: translate3d(0, 0, 0); } +} + +@keyframes flag-two { + 0%, 24% { transform: translate3d(0, 0, 0); } + 25%, 49% { transform: translate3d(0, var(--wave-h), 0); } + 50%, 74% { transform: translate3d(0, 0, 0); } + 75%, 99% { transform: translate3d(0, calc(-1 * var(--wave-h)), 0); } + 100% { transform: translate3d(0, 0, 0); } +} + +@keyframes flag-three { + 0%, 7% { transform: translate3d(0, calc(-1 * var(--wave-h)), 0); } + 8%, 32% { transform: translate3d(0, 0, 0); } + 33%, 57% { transform: translate3d(0, var(--wave-h), 0); } + 58%, 82% { transform: translate3d(0, 0, 0); } + 83%, 100% { transform: translate3d(0, calc(-1 * var(--wave-h)), 0); } +} + +@keyframes flag-four { + 0%, 15% { transform: translate3d(0, calc(-1 * var(--wave-h)), 0); } + 16%, 49% { transform: translate3d(0, 0, 0); } + 50%, 65% { transform: translate3d(0, var(--wave-h), 0); } + 66%, 100% { transform: translate3d(0, 0, 0); } +} + +@keyframes flag-five { + 0%, 24% { transform: translate3d(0, calc(-1 * var(--wave-h)), 0); } + 25%, 49% { transform: translate3d(0, 0, 0); } + 50%, 74% { transform: translate3d(0, var(--wave-h), 0); } + + 75%, 100% { + transform: translate3d(0, 0, 0); + animation-timing-function: ease-out; + } +} diff --git a/media/img/home/2024/hero-symbol-white.svg b/media/img/home/2024/hero-symbol-white.svg deleted file mode 100644 index 93be3814d15..00000000000 --- a/media/img/home/2024/hero-symbol-white.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/media/img/m24/icon-pause.svg b/media/img/m24/icon-pause.svg new file mode 100644 index 00000000000..bd6393c733c --- /dev/null +++ b/media/img/m24/icon-pause.svg @@ -0,0 +1 @@ + diff --git a/media/img/m24/icon-play.svg b/media/img/m24/icon-play.svg new file mode 100644 index 00000000000..707ee641df0 --- /dev/null +++ b/media/img/m24/icon-play.svg @@ -0,0 +1 @@ + diff --git a/media/js/m24/animation-play-state.es6.js b/media/js/m24/animation-play-state.es6.js new file mode 100644 index 00000000000..274c661c22e --- /dev/null +++ b/media/js/m24/animation-play-state.es6.js @@ -0,0 +1,36 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +const ANIMATION_RUNNING = 'data-animation-running'; + +function togglePlayState(e) { + const animationContainer = e.target.closest(`[${ANIMATION_RUNNING}]`); + if (animationContainer.getAttribute(ANIMATION_RUNNING) === 'true') { + animationContainer.setAttribute(ANIMATION_RUNNING, 'false'); + } else { + animationContainer.setAttribute(ANIMATION_RUNNING, 'true'); + } +} + +function init() { + // play animations if motion is allowed + if (window.Mozilla.Utils.allowsMotion()) { + const animationContainers = document.querySelectorAll( + `[${ANIMATION_RUNNING}]` + ); + animationContainers.forEach((container) => + container.setAttribute(ANIMATION_RUNNING, 'true') + ); + } + + // play or pause animations on button click + const playStateButtons = document.querySelectorAll('.js-animation-button'); + playStateButtons.forEach((button) => + button.addEventListener('click', (e) => togglePlayState(e)) + ); +} + +init(); diff --git a/media/static-bundles.json b/media/static-bundles.json index d7f90a58d5b..4375ca53a3d 100644 --- a/media/static-bundles.json +++ b/media/static-bundles.json @@ -1849,6 +1849,12 @@ ], "name": "monitor-banner" }, + { + "files": [ + "js/m24/animation-play-state.es6.js" + ], + "name": "m24-animation" + }, { "files": [ "js/mozorg/rise25/rise25.js"