]> _ Git - odl.git/commitdiff
Wait #4781 @26
authorStephen Cameron <stephen@cubedesigners.com>
Thu, 16 Dec 2021 20:35:12 +0000 (21:35 +0100)
committerStephen Cameron <stephen@cubedesigners.com>
Thu, 16 Dec 2021 20:35:12 +0000 (21:35 +0100)
21 files changed:
resources/css/app.css
resources/css/common/animations.css [new file with mode: 0644]
resources/css/common/global.css
resources/css/common/menu.css [deleted file]
resources/css/common/overlays.css [new file with mode: 0644]
resources/js/media-library.js
resources/js/search.js
resources/views/components/header.blade.php [new file with mode: 0644]
resources/views/components/media-library/filter-interface.blade.php
resources/views/components/media-library/filter-list.blade.php
resources/views/components/media-library/index.blade.php
resources/views/components/media-library/player-icons.blade.php
resources/views/components/media-library/player.blade.php
resources/views/components/search.blade.php
resources/views/front/home.blade.php
resources/views/front/media-library.blade.php
resources/views/front/resources.blade.php
resources/views/front/splash.blade.php
resources/views/layouts/app.blade.php
resources/views/layouts/base.blade.php
tailwind.config.js

index d12f1b77c869bd5dd2d355b46a51abaaa72f3ecc..61c266b1eabbd38a25078a3fc72ea6c96847ed6c 100644 (file)
@@ -1,6 +1,7 @@
 @import "tailwindcss/base";
 @import 'common/fonts.css';
-@import 'common/menu.css';
+@import 'common/animations.css';
+@import 'common/overlays.css';
 @import 'common/plyr.css';
 @import 'common/global.css';
 
diff --git a/resources/css/common/animations.css b/resources/css/common/animations.css
new file mode 100644 (file)
index 0000000..9de2f1c
--- /dev/null
@@ -0,0 +1,59 @@
+/* Common values */
+.animate {
+    animation-duration: var(--animation-duration, 0.5s);
+    animation-delay: var(--animation-delay, 0s);
+    animation-fill-mode: var(--animation-fill-mode, both);
+}
+
+/*============================================================*/
+
+.animate-in-up {
+    @apply ease-out-quint;
+    animation-name: animate-in-up;
+    transform: translateY(var(--start-translate-y, 100%));
+}
+
+@keyframes animate-in-up {
+    0% {
+        transform: translateY(var(--start-translate-y, 100%));
+    }
+    100% {
+        transform: translateY(var(--end-translate-y, 0));
+    }
+}
+
+/*============================================================*/
+
+.animate-scale-x {
+    @apply ease-out-quart;
+    animation-name: animate-scale-x;
+    transform: scaleX(var(--start-scale-x, 0));
+}
+
+@keyframes animate-scale-x {
+    0% {
+        transform: scaleX(var(--start-scale-x, 0));
+    }
+    100% {
+        transform: scaleX(var(--start-scale-x, 1));
+    }
+}
+
+/*============================================================*/
+
+.animate-fade-in {
+    @apply ease-out-quart;
+    animation-name: animate-fade-in;
+    opacity: 0;
+}
+
+@keyframes animate-fade-in {
+    0% {
+        opacity: 0;
+    }
+    100% {
+        opacity: 1;
+    }
+}
+
+/*============================================================*/
index 21e8a5d6bdf221320b2a5f6703634133a664fbde..ef7245594188074684dc96864e87e7c638efa051 100644 (file)
@@ -2,6 +2,11 @@ html, body {
     min-height: 100%;
 }
 
+body.overlay-open {
+    /* Prevent scrollbars and scrolling when overlays are open */
+    overflow: hidden;
+}
+
 b, strong {
     @apply font-semibold;
 }
diff --git a/resources/css/common/menu.css b/resources/css/common/menu.css
deleted file mode 100644 (file)
index aa9d510..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-body.menu-open {
-    /* Prevent scrollbars and scrolling when menu is open */
-    max-height: 100vh;
-    overflow: hidden;
-}
-
-.menu-open .site-header {
-    /* Header becomes fixed so that all elements are positioned at top, regardless of scroll position */
-    @apply text-white;
-    position: fixed;
-    width: calc(100% - 4rem); /* Full width minus 2rem padding on each side */
-}
-
-.menu-open .header-logo {
-    @apply text-white;
-}
diff --git a/resources/css/common/overlays.css b/resources/css/common/overlays.css
new file mode 100644 (file)
index 0000000..bbd93f3
--- /dev/null
@@ -0,0 +1,6 @@
+.overlay {
+    @apply fixed top-0 left-0 w-screen h-screen;
+    @apply flex flex-col items-center justify-center;
+    @apply px-30;
+    @apply transition ease-out-cubic duration-500;
+}
index 0397ee3132ed136c831cf95749c9c67b094b4d92..313442eb5d68c6b2a5e9d4b24725082b6f28d394 100644 (file)
@@ -4,7 +4,6 @@
 
 export default (options = {}) => ({
     filters: options.filters || {}, // Filters JSON array is passed in from HTML
-    filtersOpen: false, // Visibility of the filters overlay
     activeTypeFilters: [],
     activeThemeFilters: [],
     isotope: {}, // Holds the Isotope instance
@@ -13,7 +12,6 @@ export default (options = {}) => ({
         percentPosition: true,
     },
 
-    playerOpen: false, // Visibility of the media player overlay
     player: {}, // Holds the Plyr instance
     playerOptions: { // Settings used for Plyr instantiation (https://github.com/sampotts/plyr#options)
         debug: false,
@@ -65,6 +63,10 @@ export default (options = {}) => ({
         // Initialise Isotope
         this.isotope = new Isotope(element, this.isotopeOptions);
 
+        // Entry animation: unhide grid and then tell Isotope to reveal items that were hidden during init
+        element.classList.remove('opacity-0');
+        this.isotope.revealItemElements(document.querySelectorAll('.media-item'));
+
         // Update Isotope whenever active filters change
         this.$watch('activeTypeFilters', () => this.applyFilters());
         this.$watch('activeThemeFilters', () => this.applyFilters());
index 9e1b943565e9f66333ed7c5e434cc37ef566f252..1d4a06f464c684c914b9caf039c3a5c57077cf34 100644 (file)
@@ -18,7 +18,7 @@ export default (searchData = []) => ({
 
     init() {
         this.miniSearch = new MiniSearch(this.setup);
-        this.miniSearch.addAll(searchData);
+        this.miniSearch.addAllAsync(searchData); // Load asynchronously for better performance
     },
 
     get results() {
diff --git a/resources/views/components/header.blade.php b/resources/views/components/header.blade.php
new file mode 100644 (file)
index 0000000..a377f5a
--- /dev/null
@@ -0,0 +1,75 @@
+@props([
+    'main' => false,
+    'class' => '',
+])
+
+
+<header style="width: var(--content-width)"
+        class="{{ $main ? 'site-header relative' : 'absolute top-8 left-8 right-8' }}
+               {{ $class }}
+               flex justify-between
+               transition-colors duration-500
+               z-20">
+
+    {{-- LEFT SIDE --}}
+    {{-- Menu Toggle --}}
+    <a href="#" @click.prevent="menuOpen = !menuOpen">
+
+        {{-- Burger Menu Icon --}}
+        <svg x-show="!menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 33.494 26.495">
+            <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+                <path d="M1 1h31.494"/>
+                <path d="M1 13.248h31.494"/>
+                <path d="M1 25.495h31.494"/>
+            </g>
+        </svg>
+
+        {{-- Close Icon --}}
+        {{-- This icon is given the same width and height as the burger icon so it aligns properly (paths will be centred in the canvas) --}}
+        <svg x-cloak x-show="menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 25.098 25.098">
+            <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+                <path d="m1.414 23.683 22.27-22.27"/>
+                <path d="m1.414 1.414 22.27 22.27"/>
+            </g>
+        </svg>
+
+        <span class="ml-14">Menu</span>
+    </a>
+
+    {{-- CENTRE --}}
+    <div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
+        @if (isset($center))
+            {{ $center }}
+        @else
+            {{-- VEOLIA Logo --}}
+            <x-link class="header-logo {{ $logoClass ?? 'text-red' }}" href="/accueil">
+                <svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="130" height="31.878" viewBox="0 0 130 31.878"><path d="M13.704 31.795A16.13 16.13 0 0 1 .278 19.079a19.191 19.191 0 0 1-.189-5.063A16.014 16.014 0 0 1 13.598.139a19.613 19.613 0 0 1 5.536.127 16.063 16.063 0 0 1 11.993 10.7 15.252 15.252 0 0 1 .794 4.983 16.007 16.007 0 0 1-12.536 15.626 23.994 23.994 0 0 1-5.681.22Zm1.658-7.262a19.68 19.68 0 0 1-2.509-3.99 4.469 4.469 0 0 1-.4-2.247 3.587 3.587 0 0 1 .346-2.02 3.386 3.386 0 0 1 3.156-2.1c2.061-.012 3.576 1.758 3.567 4.166q-.009 2.367-2.468 5.525l-.947 1.215.914-.083a11.289 11.289 0 0 0 7.03-3.344 9.91 9.91 0 0 0 2.146-3.057 9.655 9.655 0 0 0 1.066-4.821 9.577 9.577 0 0 0-.262-2.748 11.391 11.391 0 0 0-12.229-8.536 11.425 11.425 0 0 0-9.762 8.536 13.065 13.065 0 0 0 .01 5.46 11.419 11.419 0 0 0 8.977 8.38 11.273 11.273 0 0 0 1.33.167l.468.011Zm63.629.811a12.141 12.141 0 0 1-3.307-1.111 7.234 7.234 0 0 1-3.274-5.455 16.528 16.528 0 0 1 .2-4.7 7.168 7.168 0 0 1 2.883-4.3 11.8 11.8 0 0 1 6.823-1.261c2.5.255 4.008.871 5.278 2.153a6.778 6.778 0 0 1 1.844 3.475 16.166 16.166 0 0 1 .051 5.776 6.754 6.754 0 0 1-5.2 5.254 16.019 16.019 0 0 1-5.298.17Zm4.092-3.055a3.074 3.074 0 0 0 1.737-1.57 7.571 7.571 0 0 0 .736-3.75c0-3.9-1.372-5.6-4.516-5.6a4.05 4.05 0 0 0-3.041.95c-1.035.93-1.478 2.323-1.478 4.647 0 3.219 1 5.013 3.1 5.541a7.933 7.933 0 0 0 3.461-.221Zm-39.092 2.622c-.062-.158-1.636-3.728-3.5-7.932s-3.387-7.689-3.387-7.744a6.066 6.066 0 0 1 1.548-.1c1.387 0 1.6.029 2.079.28a2.662 2.662 0 0 1 .841.753c.171.26 1.2 2.562 2.278 5.114s2.006 4.715 2.053 4.8a40.742 40.742 0 0 0 2.044-4.732c1.078-2.693 2.076-5.068 2.218-5.278a2.56 2.56 0 0 1 .789-.661c.484-.255.684-.28 2.246-.28h1.715l-3.31 7.323c-1.821 4.027-3.445 7.5-3.609 7.715-.506.666-1.126.9-2.6.966-1.278.068-1.298.062-1.406-.223Zm17.04-.007a6.749 6.749 0 0 1-4.9-5.126 12.989 12.989 0 0 1 0-5.393 7.238 7.238 0 0 1 3.253-4.507c1.366-.728 1.823-.787 6.226-.8l3.978-.008v2.893l-3.438.06-3.438.06-.794.391a2.765 2.765 0 0 0-1.13.877 4.562 4.562 0 0 0-.661 2.012v.274l4.55.032 4.55.032v2.772l-4.55.032-4.55.032v.266a5.392 5.392 0 0 0 .2.964 3.036 3.036 0 0 0 1.826 2.043c.647.257.9.276 4.094.318l3.405.045v2.873l-4.008-.013c-2.2-.007-4.279-.065-4.611-.128Zm35.54-.1a8.52 8.52 0 0 1-1.373-.486 5.251 5.251 0 0 1-1.992-2.091c-.544-1.188-.585-1.718-.59-7.609V9.073h1.266c1.094 0 1.323.036 1.688.264.806.505.777.3.844 5.943l.06 5.123.3.536a1.956 1.956 0 0 0 .9.844c.586.3.662.309 3.526.347l2.927.04v2.874l-3.345-.007a16.7 16.7 0 0 1-4.207-.233Zm9.869-7.2c.032-7.081.045-7.46.263-7.781.382-.563.851-.73 2.212-.785l1.235-.05v7.433c0 8.2.026 7.911-.759 8.39-.32.195-.609.235-1.685.236h-1.3Zm5.7 7.3c.034-.088 1.464-3.387 3.178-7.331 3.49-8.032 3.517-8.08 4.711-8.388a7.507 7.507 0 0 1 3.108-.019c.1.119 6.534 14.861 6.847 15.676.071.186-.066.2-1.505.2-2.346 0-2.719-.236-3.635-2.308l-.552-1.248-3.217-.032-3.217-.032-.124.325c-.068.179-.361.82-.652 1.426a3.114 3.114 0 0 1-1.039 1.455c-.5.345-.552.355-2.238.393-1.4.032-1.716.008-1.666-.121Zm11.231-6.518c-.92-2.207-2.181-5.116-2.249-5.19a38.91 38.91 0 0 0-2.253 4.949l-.164.392h2.365c1.844 0 2.35-.034 2.301-.151Z"/></svg>
+            </x-link>
+        @endif
+    </div>
+
+    {{-- RIGHT SIDE --}}
+    <div>
+        @if (isset($right))
+            {{ $right }}
+        @else
+            {{-- Search Icon --}}
+            {{-- Main header icon is hidden when overlay opens because it shifts position --}}
+            {{-- when body scrolling is disabled (due to being position: fixed) --}}
+            <a href="#" @click.prevent="openSearch()" @if($main) x-show="!overlayOpen" @endif>
+                <svg xmlns="http://www.w3.org/2000/svg"
+                     class="stroke-current {{ $main ? 'fixed top-8 right-8' : 'absolute top-0 right-0' }}"
+                     width="30"
+                     height="30"
+                     viewBox="0 0 29.414 29.414">
+                    <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+                        <path d="M1 12.25A11.25 11.25 0 1 0 12.25 1 11.25 11.25 0 0 0 1 12.25Z"/>
+                        <path d="m28 28-7.794-7.794"/>
+                    </g>
+                </svg>
+                <span class="mr-14">Recherche</span>
+            </a>
+        @endif
+    </div>
+
+</header>
index eb6bb4f50fb2ff25513352cb2bbeb1e16cfa5880..2522931f77ba66153b13cde6b5efee711763f339 100644 (file)
@@ -2,22 +2,21 @@
 
 @aware(['types', 'themes'])
 
-<div class="filters-overlay
-            fixed top-0 left-0 w-screen h-screen
-            flex items-center
+<div class="overlay filters-overlay
             bg-blue text-white
-            px-30
-            z-20
-            transition ease-out-cubic duration-500"
+            z-20"
      x-show="filtersOpen"
-     x-transition:enter-start="opacity-0"
-     x-transition:enter-end="opacity-100"
-     x-transition:leave-start="opacity-100"
-     x-transition:leave-end="opacity-0"
+     x-transition:enter.opacity.duration.500ms
+     x-transition:leave.opacity.duration.200ms
      x-cloak>
 
     {{-- Close --}}
-    <x-close @click.prevent="filtersOpen = false" />
+    <div x-show="filtersOpen"
+         x-transition:enter.opacity.duration.500ms.delay.300ms
+         x-transition:leave.opacity.duration.0ms.delay.0ms
+         class="absolute top-8 right-8">
+        <x-close @click.prevent="filtersOpen = false" class="flex" />
+    </div>
 
     <div class="w-full">
 
index 9d20d4536752795c1aaf0b590696aed9010745e2..9fdcd023db38f5f299ac43a5c2d04f87fc993a4d 100644 (file)
@@ -1,14 +1,16 @@
 {{-- FILTERS --}}
-<div class="mt-10 space-x-2 space-y-2">
+<div class="mt-8 space-x-2 space-y-2">
 
     {{-- FILTER INTERFACE BUTTON --}}
     <a @click.prevent="filtersOpen = true"
        href="#"
        class="inline-block py-4 px-6 rounded-full
-                      bg-black hover:bg-blue
-                      mt-2 {{-- Margin top added here so layout doesn't shift when last filter is removed --}}
-           border border-black hover:border-blue {{-- Needs border so it matches the height of filter buttons --}}
-           text-white font-secondary font-medium leading-none">
+              bg-black hover:bg-blue
+              mt-2 {{-- Margin top added here so layout doesn't shift when last filter is removed --}}
+              border border-black hover:border-blue {{-- Needs border so it matches the height of filter buttons --}}
+              text-white font-secondary font-medium leading-none
+              animate animate-fade-in"
+       style="--animation-duration: 0.5s; --animation-delay: 0.5s;">
         Filtrer
     </a>
 
index 72e188a87c926216831f3905c31ad78ab053df14..820e27f9aa79900c3784c85743f3fd97f50eaea8 100644 (file)
 
     {{-- MEDIA LIBRARY GRID --}}
     {{-- Negative margins applied here to offset margins used in Isotope grid --}}
-    <div class="media-grid mt-10 -mb-16 -mx-2.5" x-init="initIsotope($el)">
+    <div class="media-grid opacity-0 mt-10 -mb-16 -mx-2.5" x-init="initIsotope($el)">
         @foreach ($media as $item)
-            <div @click="openPlayer({
+
+            {{-- TODO: deep-linking - is it actually needed here? Probably won't be linking directly to media... Could use replaceState() to update URL with querystring when opening / closing media. Check this on init / page load so that player can open the overlay already. Since the player needs several details, it's better to store this all in a data attribute and load the player based on the ID of the media / element that contains the data --}}
+
+            <div id="media_{{ $item['id'] }}"
+               @click="openPlayer({
                      player: {
                          type: '{{ $item['type'] }}', // video / audio
                          sources: [{
@@ -42,7 +46,7 @@
                  })"
                  class="media-item group cursor-pointer
                         {{-- Width is 25% minus the gutters (2 * 0.625rem that comes from mx-2.5) --}}
-                        float-left w-[calc(33%-1.25rem)] lg:w-[calc(25%-1.25rem)] mx-2.5 mb-16
+                        float-left w-[calc(100%/2-1.25rem)] md:w-[calc(100%/3-1.25rem)] lg:w-[calc(100%/4-1.25rem)] mx-2.5 mb-16
                         media-{{ $item['type'] }}
                         theme-{{ $item['theme']['id'] }}"
             >
index 089a0c9aa68a89672d40c36402d28dd0a723dbc3..ce4682b83d2a45ee6c1af89c24fa8ea1940bd8f1 100644 (file)
@@ -1,6 +1,6 @@
 {{-- Plyr custom icon SVG sprite --}}
 
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="hidden">
 
     <symbol id="plyr-airplay" viewBox="0 0 18 18"><path d="M16 1H2a1 1 0 00-1 1v10a1 1 0 001 1h3v-2H3V3h12v8h-2v2h3a1 1 0 001-1V2a1 1 0 00-1-1z"/><path d="M4 17h10l-5-6z"/></symbol>
 
index c085c5bbf9cc0cbf1825e55304951b6ed32dce39..74af5e5e218bbaccd0c9546e657d3b80fa5112a8 100644 (file)
 {{-- Embed SVG sprite directly --}}
 <x-media-library.player-icons />
 
-<div class="player-overlay
-            fixed top-0 left-0 w-screen h-screen
-            flex items-center
+<div class="overlay player-overlay
             bg-white text-black
-            px-30
-            z-20
-            transition ease-out-cubic duration-500"
+            z-20"
      x-show="playerOpen"
-     x-transition:enter-start="opacity-0"
-     x-transition:enter-end="opacity-100"
-     x-transition:leave-start="opacity-100"
-     x-transition:leave-end="opacity-0"
+     x-transition:enter.opacity.duration.500ms
+     x-transition:leave.opacity.duration.200ms
      x-cloak>
 
     <x-close @click.prevent="closePlayer()" />
index 0d7eafd5ef333c21e085c028796d9f3c06a41f58..4a5aab32bb0b914af82bd02a8bac20fddeb7f28b 100644 (file)
@@ -4,48 +4,75 @@
     <script>
         @php
             // TODO: Consider fetching this via JS only when search is used
+
+            // TODO: need to change Cubist/minisearch to output data differently but until then, can test it by copying the output manually into a .json file. Then use await / fetch to get data only when needed?
             echo file_get_contents(storage_path('search.js'));
+            //echo 'var documents = [];';
         @endphp
     </script>
 @endpush
 
-<div class="search-overlay
-            fixed top-0 left-0 w-screen h-screen
-            flex flex-col
+<div class="overlay search-overlay
             bg-white text-black
-            px-30
-            z-20
-            transition ease-out-cubic duration-500"
+            z-20"
      {{-- Pass search index [documents] to component for setup --}}
      {{-- See js/search.js --}}
      x-data="search(documents)"
      x-show="searchOpen"
-     x-transition:enter-start="opacity-0"
-     x-transition:enter-end="opacity-100"
-     x-transition:leave-start="opacity-100"
-     x-transition:leave-end="opacity-0"
+     x-transition:enter.opacity.duration.500ms
+     x-transition:leave.opacity.duration.200ms
      x-cloak>
 
-    <x-close @click.prevent="closeSearch()" />
+    <x-header>
+        <x-slot name="right">
+            <div x-show="searchOpen && !menuOpen" x-transition:enter.opacity.duration.500ms.delay.300ms x-transition:leave.opacity.duration.0ms.delay.0ms>
+                <x-close
+                    x-show="searchOpen && !menuOpen"
+                    @click.prevent="closeSearch()"
+                    class="flex"
+                />
+            </div>
+        </x-slot>
+    </x-header>
+
 
     {{-- Search Field --}}
     <div class="w-full pt-30 flex-grow-0">
-        <p class="font-medium text-2xl">Tapez ici le ou les mots-clé de votre recherche</p>
-
-        <input
-            id="searchField"
-            x-model="query"
-            @keyup.enter="$el.blur()" {{-- Remove focus on enter in order to hide virtual keyboard --}}
-            type="search"
-            placeholder="Recherche"
-            class="appearance-none outline-none
-                   mt-8 w-full
-                   font-semibold text-6xl uppercase
-                   border-b-2 border-black">
+        <div class="overflow-hidden font-medium text-2xl">
+            <p class="animate"
+               :class="{ 'animate-in-up': searchOpen }"
+               style="--animation-delay: 0.25s; --animation-duration: 0.7s;">
+                Tapez ici le ou les mots-clé de votre recherche
+            </p>
+        </div>
+
+        <div class="overflow-hidden">
+            <input
+                id="searchField"
+                type="search"
+                placeholder="Recherche"
+                @keyup.enter="$el.blur()" {{-- Remove focus on enter in order to hide virtual keyboard --}}
+                x-model="query"
+                class="appearance-none outline-none
+                       mt-8 w-full
+                       font-semibold text-6xl uppercase
+                       animate"
+                :class="{ 'animate-in-up': searchOpen }"
+                style="--animation-delay: 0.35s; --animation-duration: 0.7s;"
+            >
+        </div>
+        <div class="bg-black h-[2px]
+                    transform origin-left scale-x-0
+                    animate"
+             :class="{ 'animate-scale-x': searchOpen }"
+             style="--animation-delay: 0.5s; --animation-duration: 1.5s;"></div>
     </div>
 
     {{-- Search Results --}}
-    <div class="flex-1 max-h-full overflow-y-auto">
+    <div class="flex-1 max-h-full overflow-y-auto"
+         x-show="searchOpen"
+         x-transition:enter.opacity.duration.500ms.delay.1500ms
+         x-transition:leave.opacity.delay.0ms>
         <div x-show="query !== ''" class="font-secondary font-medium">
             <p x-text="resultCount" class="mt-15 opacity-50"></p>
 
index 0eb18134e6bb0d36782a2984b7f363a06e47db37..53c154c4be25f9d22b53e549281323ca4de30026 100644 (file)
@@ -31,7 +31,9 @@
         ];
     @endphp
 
-    <div class="flex flex-col flex-1 -mx-22 -mt-22">
+    <div x-data="{ ready: true }"
+         :class="{ 'opacity-0': !ready }" {{-- opacity-0 will be removed as soon as Alpine fires --}}
+         class="flex flex-col flex-1 -mx-22 -mt-22 opacity-0">
 
         {{-- Main home content --}}
         <div x-cloak x-data="{ shown: false }" x-intersect="shown = true"
             <div class="home-left flex flex-1 flex-col justify-center items-center p-6 max-w-[360px]">
                 @if($logo)
                     <img class="mb-10" src="{{ $logo }}" alt="Logo"
-                         x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.800ms.delay.200ms>
+                         x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.800ms>
                 @endif
                 @if($subtitle)
                     <p class="text-center mb-5 max-w-[280px]"
-                       x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.300ms>
+                       x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.100ms>
                         {{ $subtitle }}
                     </p>
                 @endif
                 @if($button)
                     <a class="bg-blue text-white py-4 px-10 rounded-full transition transform hover:scale-105"
                        href="{{ $button_link }}"
-                       x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.300ms>
+                       x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.100ms>
                         {{ $button }}
                     </a>
                 @endif
@@ -64,7 +66,7 @@
                             bg-contain bg-center bg-no-repeat
                             transform transition duration-300"
                      x-show="shown"
-                     x-transition.opacity.scale.50.duration.1000ms.delay.300ms
+                     x-transition.opacity.scale.50.duration.1000ms.delay.100ms
                      style="@if($illustration)background-image:url({{ $illustration }})@endif">
                 </div>
             </div>
index 608692dd3c7e0a9202ec776f198c4e9b683b5b68..fc9f8bec2f72ab74138d64006e39bba72bd64a2c 100644 (file)
@@ -6,6 +6,7 @@
         // TEMPORARY DATA MOCKUP
         $media = [
             [
+                'id' => 123,
                 'title' => "Qu’est ce que la gouvernance ?",
                 'type' => 'video',
                 'duration' => '78',
@@ -19,6 +20,7 @@
                 ],
             ],
             [
+                'id' => 545,
                 'title' => 'Les outils de communication',
                 'type' => 'video',
                 'duration' => '192',
@@ -32,6 +34,7 @@
                 ],
             ],
             [
+                'id' => 785,
                 'title' => "Une organisation Ã  plusieurs niveaux",
                 'type' => 'audio',
                 'duration' => '144',
@@ -45,6 +48,7 @@
                 ],
             ],
             [
+                'id' => 65,
                 'title' => 'Système d’information',
                 'type' => 'audio',
                 'duration' => '987',
@@ -58,6 +62,7 @@
                 ],
             ],
             [
+                'id' => 146,
                 'title' => "Qu’est ce que la gouvernance ?",
                 'type' => 'video',
                 'duration' => '614',
@@ -71,6 +76,7 @@
                 ],
             ],
             [
+                'id' => 370,
                 'title' => 'Les outils de communication',
                 'type' => 'audio',
                 'duration' => '45',
         ];
     @endphp
 
-    <h1 class="uppercase">Médiathèque</h1>
+    <div class="overflow-hidden py-2">{{-- vertical padding so accents don't get cropped --}}
+        <h1 class="uppercase animate animate-in-up"
+            style="--animation-duration: 0.7s;">
+            Médiathèque
+        </h1>
+    </div>
 
     {{-- Media library (components/media-library/index.blade.php) --}}
     <x-media-library :themes="$themes" :types="$media_types" :media="$media"></x-media-library>
index 8c15a4d04a82643d28347f18ceacf93601398833..d83a2ca7290d4d9575a7cf030c413257afe6bc9a 100644 (file)
@@ -1,26 +1,43 @@
 @extends('layouts.app')
 
-@section('content')
-
-    @php
+@push('extra_body_classes')
+    overflow-y-scroll {{-- We already know this will be a long page so this avoids the header jumping on load --}}
+@endpush
 
-    @endphp
+@section('content')
 
     {{-- RESOURCES --}}
     <div class="resources" x-data="{
-        PDFOpen: false, // Tracks if PDF viewer overlay is open
         viewerURL: '/tools/fluidbookpreview/pdfjs/web/viewer.html?file=', // Base URL for viewer
         shown: false,
 
-        openPDF(URL) {
-            this.overlayOpen = true; // Stops page scrolling in the background
+        init() {
+            // Open PDF viewer if querystring is already set
+            let querystring = new URLSearchParams(location.search);
+            if (querystring.get('file')) {
+                this.openPDF(querystring.get('file'), false);
+            }
+        },
+
+        openPDF(PDF_URL, updateQuerystring = true) {
+            if (updateQuerystring) {
+                const location = new URL(window.location.href);
+                location.searchParams.set('file', PDF_URL);
+                history.replaceState(null, document.title, location.toString());
+            }
+
             this.PDFOpen = true;
             $nextTick(() => { $refs.PDFViewer.setAttribute('src', this.viewerURL + URL) });
         },
 
         closePDF() {
             this.PDFOpen = false;
-            this.overlayOpen = false;
+            $refs.PDFViewer.setAttribute('src', '');
+
+            // Update the page URL to remove querystring
+            const location = new URL(window.location.href);
+            location.searchParams.delete('file');
+            history.replaceState(null, document.title, location.toString());
         },
     }" x-intersect="shown = true" x-cloak>
 
@@ -51,8 +68,9 @@
             {{-- DOCUMENTS --}}
             <div class="grid lg:grid-cols-2 gap-6">
                 @foreach($resources['documents'] as $doc)
-                    <a href="#" @click.prevent="openPDF('/storage/242/fluidbook.pdf')" class="group">
-                        <div class="bg-blue flex items-center p-10 text-white rounded-md">
+                    {{--<a href="{{ $doc['document_pdf'] }}" @click.prevent="openPDF($el.attributes.href.value)" class="group">--}}
+                    <a href="/storage/242/fluidbook.pdf" @click.prevent="openPDF($el.attributes.href.value)" class="group">
+                        <div class="bg-blue h-full flex items-center p-10 text-white rounded-md">
                             <img class="w-1/2 pr-6" src="/storage/107/groupe-133-at-2x.png" alt="{{ $doc['document_title'] }}">
 {{--                            <img class="w-1/2 pr-6" src="{{ $doc['document_image'] }}" alt="{{ $doc['document_title'] }}">--}}
                             <div class="-mr-5 space-y-4">
                         {{-- MEMOS --}}
                         <div class="grid grid-cols-2 gap-5 mt-8">
                             @foreach ($subchapter['subchapter_memos'] as $memo)
-                                <a href="#" @click.prevent="openPDF('/storage/242/fluidbook.pdf')" class="group">
+                                {{--<a href="{{ $memo['memo_pdf'] }}" @click.prevent="openPDF($el.attributes.href.value)" class="group">--}}
+                                <a href="/storage/242/fluidbook.pdf" @click.prevent="openPDF($el.attributes.href.value)" class="group">
                                     <div class="bg-grey-50 flex items-center p-8 rounded-md">
                                         <img class="w-1/4 pr-4"
 {{--                                             src="{{ $memo['memo_image'] }}"--}}
         </div>
 
         {{-- PDF Viewer Overlay --}}
-        <div class="pdf-overlay
-                    fixed top-0 left-0 w-screen h-screen
-                    flex flex-col
+        <div class="overlay pdf-overlay
                     bg-white text-black
-                    px-30 pt-20
-                    z-10
-                    transition ease-out-cubic duration-500"
+                    pt-20
+                    z-20"
              x-show="PDFOpen"
-             x-transition:enter-start="opacity-0"
-             x-transition:enter-end="opacity-100"
-             x-transition:leave-start="opacity-100"
-             x-transition:leave-end="opacity-0"
+             x-transition:enter.opacity.duration.500ms
+             x-transition:leave.opacity.duration.200ms
              x-cloak>
 
             {{-- PDF Viewer iframe --}}
             <iframe x-ref="PDFViewer" src="" frameborder="0"
                     class="absolute left-0 w-full top-[90px] h-[calc(100vh-90px)]"></iframe>
 
+            <x-header>
+                <x-slot name="right">
+                    <div x-show="PDFOpen && !menuOpen" x-transition:enter.opacity.duration.500ms.delay.300ms x-transition:leave.opacity.duration.0ms.delay.0ms>
+                        <x-close
+                            x-show="PDFOpen"
+                            @click.prevent="closePDF()"
+                            class="flex"
+                        />
+                    </div>
+                </x-slot>
+            </x-header>
         </div>
 
-        {{-- Close button sits outside overlay so it can sit above and cover header search button --}}
-        {{-- TODO: Rework this so it's less awkward when the main menu is opened on top of the PDF viewer --}}
-        {{-- TODO: instead of using the same main element, consider having a header widget that can be used to redisplay header inside overlays (with option to disable / replace items via slots --}}
-        <x-close @click.prevent="closePDF()"
-                 x-show="PDFOpen && !menuOpen" {{-- Don't show close button if menu gets opened --}}
-                 x-transition:enter="delay-300 duration-300"
-                 x-transition:enter-start="opacity-0"
-                 x-transition:enter-end="opacity-100"
-                 x-transition:leave-start="opacity-100"
-                 x-transition:leave-end="opacity-0"
-                 class="fixed top-8 right-8 flex z-30 bg-white pb-4 pl-4"
-                 x-cloak />
-
-
     </div>
 
 @endsection
index 41681d1c3fb655c45e914ffba33fd01e5797d71b..6c91bd45a7aca56a388db5640b720230cd019631 100644 (file)
@@ -1,5 +1,10 @@
 @extends('layouts.base')
 
+@section('body_tag')
+    {{-- Make sure no scrollbars are present because they affect the background scaling --}}
+    <body class="overflow-hidden">
+@endsection
+
 @section('main')
 
     {{-- Title + Illustration --}}
@@ -28,6 +33,7 @@
             <img x-show="shown"
                  x-transition.opacity.scale.95.origin.center.duration.1200ms.delay.800ms
                  class="max-w-[520px]"
+                 style="backface-visibility: hidden"
                  src="{{ asset('images/splash-illustration.png') }}">
         </x-link>
 
index 1fedc8581827015a26a013dfa66d28388451982f..bfe7eeb4f4d9a18456aafc4526d53325b1fe6b14 100644 (file)
@@ -1,99 +1,88 @@
 @extends('layouts.base')
 
+@push('before_scripts')
+    <script>
+        function app() {
+            return {
+                menuOpen: false,
+                searchOpen: false,
+                PDFOpen: false, // Resources PDF overlay
+                filtersOpen: false, // Media libary filters overlay
+                playerOpen: false, // Media player overlay
+
+                init() {
+                    setTimeout(this.measureWidth, 2000);
+
+                    // When an overlay opens, freeze the scrolling on the body
+                    this.$watch('overlayOpen', (isOpen) => {
+                        if (isOpen) {
+                            document.body.classList.add('overlay-open');
+                        } else {
+                            document.body.classList.remove('overlay-open');
+                        }
+                    });
+                },
+
+                get overlayOpen() {
+                    return this.menuOpen || this.searchOpen || this.PDFOpen || this.filtersOpen || this.playerOpen;
+                },
+
+                measureWidth() {
+                    // Measure available space for header after padding and scrollbars are accounted for
+                    let body = getComputedStyle(document.body);
+                    let width = parseFloat(body.width);
+                    width -= parseFloat(body.paddingLeft) + parseFloat(body.paddingRight);
+                    document.body.style.setProperty('--content-width', `${width}px`)
+                },
+
+                openSearch() {
+                    this.menuOpen = false;
+                    this.searchOpen = true;
+                    this.$nextTick(() => {
+                        // Delay focusing on the search field, otherwise it will interfere with animations
+                        setTimeout(() => document.getElementById('searchField').focus(), 1500);
+                    });
+                },
+
+                closeSearch() {
+                    this.searchOpen = false;
+                },
+            }
+        }
+    </script>
+@endpush
+
 @section('body_tag')
-    <body x-data="{
-        overlayOpen: false, // Tracks when a modal is open so background scroll can be frozen
-        menuOpen: false,
-        searchOpen: false,
-        openSearch() {
-            this.overlayOpen = true;
-            this.searchOpen = true;
-            $nextTick(() => {
-                document.getElementById('searchField').focus();
-            });
-        },
-        closeSearch() {
-            this.overlayOpen = false;
-            this.searchOpen = false;
-        },
-      }"
-      :class="{
-        'menu-open': menuOpen,
-        'overflow-hidden': overlayOpen
-      }"
-      class="font-primary p-8">
+    <body x-data="app()"
+          @resize.window.debounce="measureWidth()"
+          class="font-primary p-8 @stack('extra_body_classes')">
 @endsection
 
 @section('main')
-
-    <header class="site-header relative flex justify-between z-20 transition-colors duration-500">
-        {{-- Menu Toggle --}}
-        <a href="#" @click.prevent="menuOpen = !menuOpen">
-
-            {{-- Burger Menu Icon --}}
-            <svg x-show="!menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 33.494 26.495">
-                <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
-                    <path d="M1 1h31.494"/>
-                    <path d="M1 13.248h31.494"/>
-                    <path d="M1 25.495h31.494"/>
-                </g>
-            </svg>
-
-            {{-- Close Icon --}}
-            {{-- This icon is given the same width and height as the burger icon so it aligns properly (paths will be centred in the canvas) --}}
-            <svg x-cloak x-show="menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 25.098 25.098">
-                <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
-                    <path d="m1.414 23.683 22.27-22.27"/>
-                    <path d="m1.414 1.414 22.27 22.27"/>
-                </g>
-            </svg>
-
-            <span class="ml-14">Menu</span>
-        </a>
-
-        {{-- VEOLIA Logo --}}
-        <x-link class="header-logo text-red" href="/accueil">
-            <svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="130" height="31.878" viewBox="0 0 130 31.878"><path d="M13.704 31.795A16.13 16.13 0 0 1 .278 19.079a19.191 19.191 0 0 1-.189-5.063A16.014 16.014 0 0 1 13.598.139a19.613 19.613 0 0 1 5.536.127 16.063 16.063 0 0 1 11.993 10.7 15.252 15.252 0 0 1 .794 4.983 16.007 16.007 0 0 1-12.536 15.626 23.994 23.994 0 0 1-5.681.22Zm1.658-7.262a19.68 19.68 0 0 1-2.509-3.99 4.469 4.469 0 0 1-.4-2.247 3.587 3.587 0 0 1 .346-2.02 3.386 3.386 0 0 1 3.156-2.1c2.061-.012 3.576 1.758 3.567 4.166q-.009 2.367-2.468 5.525l-.947 1.215.914-.083a11.289 11.289 0 0 0 7.03-3.344 9.91 9.91 0 0 0 2.146-3.057 9.655 9.655 0 0 0 1.066-4.821 9.577 9.577 0 0 0-.262-2.748 11.391 11.391 0 0 0-12.229-8.536 11.425 11.425 0 0 0-9.762 8.536 13.065 13.065 0 0 0 .01 5.46 11.419 11.419 0 0 0 8.977 8.38 11.273 11.273 0 0 0 1.33.167l.468.011Zm63.629.811a12.141 12.141 0 0 1-3.307-1.111 7.234 7.234 0 0 1-3.274-5.455 16.528 16.528 0 0 1 .2-4.7 7.168 7.168 0 0 1 2.883-4.3 11.8 11.8 0 0 1 6.823-1.261c2.5.255 4.008.871 5.278 2.153a6.778 6.778 0 0 1 1.844 3.475 16.166 16.166 0 0 1 .051 5.776 6.754 6.754 0 0 1-5.2 5.254 16.019 16.019 0 0 1-5.298.17Zm4.092-3.055a3.074 3.074 0 0 0 1.737-1.57 7.571 7.571 0 0 0 .736-3.75c0-3.9-1.372-5.6-4.516-5.6a4.05 4.05 0 0 0-3.041.95c-1.035.93-1.478 2.323-1.478 4.647 0 3.219 1 5.013 3.1 5.541a7.933 7.933 0 0 0 3.461-.221Zm-39.092 2.622c-.062-.158-1.636-3.728-3.5-7.932s-3.387-7.689-3.387-7.744a6.066 6.066 0 0 1 1.548-.1c1.387 0 1.6.029 2.079.28a2.662 2.662 0 0 1 .841.753c.171.26 1.2 2.562 2.278 5.114s2.006 4.715 2.053 4.8a40.742 40.742 0 0 0 2.044-4.732c1.078-2.693 2.076-5.068 2.218-5.278a2.56 2.56 0 0 1 .789-.661c.484-.255.684-.28 2.246-.28h1.715l-3.31 7.323c-1.821 4.027-3.445 7.5-3.609 7.715-.506.666-1.126.9-2.6.966-1.278.068-1.298.062-1.406-.223Zm17.04-.007a6.749 6.749 0 0 1-4.9-5.126 12.989 12.989 0 0 1 0-5.393 7.238 7.238 0 0 1 3.253-4.507c1.366-.728 1.823-.787 6.226-.8l3.978-.008v2.893l-3.438.06-3.438.06-.794.391a2.765 2.765 0 0 0-1.13.877 4.562 4.562 0 0 0-.661 2.012v.274l4.55.032 4.55.032v2.772l-4.55.032-4.55.032v.266a5.392 5.392 0 0 0 .2.964 3.036 3.036 0 0 0 1.826 2.043c.647.257.9.276 4.094.318l3.405.045v2.873l-4.008-.013c-2.2-.007-4.279-.065-4.611-.128Zm35.54-.1a8.52 8.52 0 0 1-1.373-.486 5.251 5.251 0 0 1-1.992-2.091c-.544-1.188-.585-1.718-.59-7.609V9.073h1.266c1.094 0 1.323.036 1.688.264.806.505.777.3.844 5.943l.06 5.123.3.536a1.956 1.956 0 0 0 .9.844c.586.3.662.309 3.526.347l2.927.04v2.874l-3.345-.007a16.7 16.7 0 0 1-4.207-.233Zm9.869-7.2c.032-7.081.045-7.46.263-7.781.382-.563.851-.73 2.212-.785l1.235-.05v7.433c0 8.2.026 7.911-.759 8.39-.32.195-.609.235-1.685.236h-1.3Zm5.7 7.3c.034-.088 1.464-3.387 3.178-7.331 3.49-8.032 3.517-8.08 4.711-8.388a7.507 7.507 0 0 1 3.108-.019c.1.119 6.534 14.861 6.847 15.676.071.186-.066.2-1.505.2-2.346 0-2.719-.236-3.635-2.308l-.552-1.248-3.217-.032-3.217-.032-.124.325c-.068.179-.361.82-.652 1.426a3.114 3.114 0 0 1-1.039 1.455c-.5.345-.552.355-2.238.393-1.4.032-1.716.008-1.666-.121Zm11.231-6.518c-.92-2.207-2.181-5.116-2.249-5.19a38.91 38.91 0 0 0-2.253 4.949l-.164.392h2.365c1.844 0 2.35-.034 2.301-.151Z"/></svg>
-        </x-link>
-
-
-        {{-- Search Icon --}}
-        <a href="#" @click.prevent="openSearch()">
-            <svg class="stroke-current fixed top-8 right-8" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 29.414 29.414">
-                <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
-                    <path d="M1 12.25A11.25 11.25 0 1 0 12.25 1 11.25 11.25 0 0 0 1 12.25Z"/>
-                    <path d="m28 28-7.794-7.794"/>
-                </g>
-            </svg>
-            <span class="mr-14">Recherche</span>
-        </a>
-    </header>
-
-    {{-- Header spacer: same height as the header in the normal flow --}}
-    {{-- This is needed because the header switches to position:fixed when menu is open and therefore no longer --}}
-    {{-- takes any space in the document flow. This spacer is the replacement and prevents content from shifting. --}}
-    <div class="h-8" x-show="menuOpen" x-cloak></div>
-
-    <div class="min-h-content flex flex-col mx-22 pt-22">
-        @yield('content')
+    {{--
+        Content has constrained width so that nothing shifts when overlay is opened and scrolling is disabled.
+        The body width changes when scrollbars are removed, which causes alignment problems, especially with
+        fixed-position elements.
+    --}}
+    <div style="width: var(--content-width)">
+        <x-header main="true" />
+
+        <div class="min-h-content flex flex-col mx-22 pt-22">
+            @yield('content')
+        </div>
     </div>
 
     {{-- MENU OVERLAY --}}
-    <div class="menu-overlay
-                fixed top-0 left-0 w-screen h-screen
-                flex items-center
+    <div class="overlay menu-overlay
                 bg-blue text-white
-                px-30
-                z-10
-                transition ease-out-cubic duration-500"
+                z-30"
          x-show="menuOpen"
-         x-transition:enter-start="opacity-0"
-         x-transition:enter-end="opacity-100"
-         {{--x-transition:leave="delay-1000"--}}
-         x-transition:leave-start="opacity-100"
-         x-transition:leave-end="opacity-0"
+         x-transition.opacity.duration.500ms
+         x-transition:leave.opacity.duration.500ms.delay.500ms
          x-cloak>
 
+        <x-header logo-class="text-white" />
+
         <ul class="w-full font-medium text-6xl">
             @php
                 $menu_links = [
                 {{-- Extra padding and negative margin added so hover scale effect isn't clipped --}}
                 <li class="overflow-hidden pl-4 -ml-4">
                     <div x-show="menuOpen"
+                         @click.prevent="menuOpen = false; setTimeout(() => window.location = $event.target.href, 200);"
                          class="py-8
                                 transition transform ease-out-quint"
                          style="transition-delay: {{ 250 + (50 * $loop->index) }}ms"
                          x-transition:enter="duration-1000"
                          x-transition:enter-start="translate-y-[100px]"
                          x-transition:enter-end="translate-x-0"
-                         {{--x-transition:leave="duration-500"
+                         x-transition:leave="duration-500"
                          x-transition:leave-start="translate-x-0"
-                         x-transition:leave-end="translate-y-[100px]"--}}>
-                        <x-link class="inline-block text-current transition-transform transform duration-200 hover:scale-105"
-                                href="{{ $link }}">
+                         x-transition:leave-end="translate-y-[100px]">
+                        <x-link
+                            href="{{ $link }}"
+                            class="inline-block text-current
+                                   transform origin-bottom-left
+                                   transition-transform duration-200
+                                   hover:scale-105">
                             {{ $text }}
                         </x-link>
                     </div>
                     x-transition:enter="duration-2000"
                     x-transition:enter-start="scale-x-0"
                     x-transition:enter-end="scale-x-100"
-                    {{--x-transition:leave="duration-1000"
+                    x-transition:leave="duration-500"
                     x-transition:leave-start="scale-x-100"
-                    x-transition:leave-end="scale-x-0"--}}>
+                    x-transition:leave-end="scale-x-0">
                 </li>
             @endforeach
         </ul>
index 427888aec7df4347a00efb816d118320934eb4c9..de6567d5ac3e50c223f535a8e6c350fd8f985d74 100644 (file)
@@ -9,7 +9,7 @@
     <title>{{ config('app.name') }}</title>
 </head>
 @section('body_tag')
-<body>
+<body class="@stack('extra_body_classes')">
 @show
 
     @yield('main')
index 447d40068708b3612b866cfc1e5792310ac53110..60feaf4f8fdbb798bfec5feb4cf967c015b5ca1b 100644 (file)
@@ -36,9 +36,11 @@ module.exports = {
       },
       transitionTimingFunction: {
         'out-cubic': 'cubic-bezier(0.33, 1, 0.68, 1)',
+        'out-quart': 'cubic-bezier(0.25, 1, 0.5, 1)',
         'out-quint': 'cubic-bezier(0.22, 1, 0.36, 1)',
       },
       transitionDuration: {
+        '1500': '1500ms',
         '2000': '2000ms',
       },
       zIndex: {