]> _ Git - cubedesigners-v7.git/commitdiff
WIP #2033 @7
authorstephen@cubedesigners.com <stephen@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Thu, 14 Feb 2019 10:55:49 +0000 (10:55 +0000)
committerstephen@cubedesigners.com <stephen@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Thu, 14 Feb 2019 10:55:49 +0000 (10:55 +0000)
12 files changed:
framework/application/forms/CMS/Sub/Casestudies/Content/Bloc.php
framework/application/forms/CMS/Sub/Casestudies/Studies.php
framework/application/models/Casestudy.php
framework/application/views/helpers/CaseStudiesGrid.php [new file with mode: 0644]
framework/application/views/helpers/CasestudiesDetail.php
framework/application/views/helpers/CasestudiesList.php
framework/application/views/scripts/studies/index.phtml
framework/application/views/scripts/templates/home.phtml
js/admin.js
less/casestudies.less
less/casestudies_detail.less
less/casestudies_grid.less [new file with mode: 0644]

index 9475469b2fcfb098939c930bdb70e942e206a36c..96cfb123e3ab0ca739609e8aa56170aa386a60fe 100644 (file)
@@ -19,22 +19,22 @@ class Cubedesigners_Form_CMS_Sub_Casestudies_Content_Bloc extends CubeIT_Form_Su
         $text_color->setLabel('Text colour (leave blank for default)');
         $this->addElement($text_color);
 
-               $margin = new CubeIT_Form_Element_Int('margin');
-               $margin->setLabel("Décalage vertical du bloc (en pixels)");
-               $this->addElement($margin);
-
-               $zindex = new Zend_Form_Element_Select('zindex');
-               $options = array('default' => 'Par défaut');
-               for ($i = 1; $i <= 10; $i++) {
-                       $options[$i] = $i;
-               }
-               $zindex->setMultiOptions($options);
-               $zindex->setLabel('z-index du bloc');
-               $this->addElement($zindex);
-
-               $annee = new Zend_Form_Element_Text('annee');
-               $annee->setLabel('Année');
-               $this->addElement($annee);
+//             $margin = new CubeIT_Form_Element_Int('margin');
+//             $margin->setLabel("Décalage vertical du bloc (en pixels)");
+//             $this->addElement($margin);
+//
+//             $zindex = new Zend_Form_Element_Select('zindex');
+//             $options = array('default' => 'Par défaut');
+//             for ($i = 1; $i <= 10; $i++) {
+//                     $options[$i] = $i;
+//             }
+//             $zindex->setMultiOptions($options);
+//             $zindex->setLabel('z-index du bloc');
+//             $this->addElement($zindex);
+//
+//             $annee = new Zend_Form_Element_Text('annee');
+//             $annee->setLabel('Année');
+//             $this->addElement($annee);
 
                $titre = new Zend_Form_Element_Text('titre');
                $titre->setLabel('Titre');
@@ -49,10 +49,10 @@ class Cubedesigners_Form_CMS_Sub_Casestudies_Content_Bloc extends CubeIT_Form_Su
                $visuel->setMaxItems(1);
                $this->addElementLocalized($visuel, $isCompactTrad);
 
-               $visuelpadding = new CubeIT_Form_Element_Int('visuelpadding');
-               $visuelpadding->setLabel('Margin intérieur sous le visuel');
-               $visuelpadding->setValue(50);
-               $this->addElement($visuelpadding);
+//             $visuelpadding = new CubeIT_Form_Element_Int('visuelpadding');
+//             $visuelpadding->setLabel('Margin intérieur sous le visuel');
+//             $visuelpadding->setValue(50);
+//             $this->addElement($visuelpadding);
 
                $video = new CubeIT_Form_Element_WebVideo('video');
                $video->setLabel('URL de la vidéo');
index 6b98f3be4031c9b95b2259138d61cf40c9662318..4d200a9514e41424a74f851e3ec082e3159cbfd3 100644 (file)
@@ -36,6 +36,10 @@ class Cubedesigners_Form_CMS_Sub_Casestudies_Studies extends CubeIT_Form_List_Mo
                $visuel->setMaxItems(1);
                $this->addElementLocalized($visuel, $isCompactTrad, true);
 
+               $hover_color = new CubeIT_Form_Element_Color('hover_color');
+               $hover_color->setLabel('Hover overlay colour (for thumbnail)');
+               $this->addElement($hover_color);
+
                $visuel_detail = new CubeIT_Form_Element_File_Image('visuel_detail');
                $visuel_detail->setLabel('Visuel de la page de détail');
                $visuel_detail->setMaxItems(1);
index 40ab8697c917ec6db4b440eb3062143508739901..685a3f030d4a5c56dc2c29e605aeab66cd636eec 100644 (file)
@@ -8,6 +8,7 @@ class Cubedesigners_Model_Casestudy extends CubeIT_Model_Data_Table {
        protected $url;
        protected $visuel;
        protected $visuel_detail;
+       protected $hover_color;
        protected $legende;
        protected $header_style;
        protected $couleur;
@@ -28,6 +29,7 @@ class Cubedesigners_Model_Casestudy extends CubeIT_Model_Data_Table {
                $casestudies->addColumn('url', 'text');
                $casestudies->addColumn('visuel', 'string', array('length' => 128));
                $casestudies->addColumn('visuel_detail', 'string', array('length' => 128));
+               $casestudies->addColumn('hover_color', 'string', array('length' => 32));
                $casestudies->addColumn('legende', 'text');
                $casestudies->addColumn('header_style', 'text');
                $casestudies->addColumn('couleur', 'text');
diff --git a/framework/application/views/helpers/CaseStudiesGrid.php b/framework/application/views/helpers/CaseStudiesGrid.php
new file mode 100644 (file)
index 0000000..451728c
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+class Cubedesigners_View_Helper_CaseStudiesGrid extends CubeIT_View_Helper_Abstract {
+
+    protected $_tags;
+    protected $_page_data;
+    protected $_counter = 0;
+
+    public function caseStudiesGrid($limit = 3, $exclude_ID = null) {
+
+        $this->addScriptAndStyle('casestudies_grid');
+
+        // ToDo: see if there's a better way to do this
+        $this->_page_data = Bootstrap::getInstance()->getCMSDatasOfPage(4); // ID 4 = casestudies page
+
+        $db = Zend_Db_Table::getDefaultAdapter();
+        $select = $db->select()->from('casestudies')
+                               ->order('RAND()');
+
+        if ($exclude_ID) {
+            $select->where('id != ?', $exclude_ID);
+        }
+
+        $select->limit($limit);
+
+        $query = $select->query();
+
+        $res = '<div class="casestudies-grid">';
+
+
+        while ($study = $query->fetch()) {
+            $res .= $this->_study(CubeIT_Util_Cms::unserialize($study));
+        }
+
+        $res .= '</div>'; // .casestudies-grid
+
+        return $res;
+    }
+
+    protected function _study($study) {
+
+        // Max display size is 512 but we over-sample the size by 20% because there is a CSS scale effect on hover
+        $image = $this->view->imageProcess($study->visuel, $study->titre, 615, 615, ['class' => 'responsive casestudies-grid-item-image']);
+        $url = Cubedesigners_Util::generateAutoUri($study, $this->_page_data['seourl_stu']);
+        $tag_names = [];
+
+        foreach ($study->tags_secondaires as $categoryID) {
+            $tag_names[] = $this->_getTagName($categoryID);
+        }
+
+        $hover_style = empty($study->hover_color) ? '' : 'background-color:'. CubeIT_Util_Cms::hexToRGBA($study->hover_color, 0.75) .';';
+
+        $c = $image;
+        $c.= '<div class="casestudies-grid-item-label" style="'. $hover_style .'">';
+        $c.= '<h2 class="casestudies-grid-item-title">' . $study->titre . '</h2>';
+        $c.= '<h3 class="casestudies-grid-item-tags">' . implode(' / ', $tag_names) . '</h3>';
+        $c.= '</div>'; // .casestudies-grid-item-label
+
+
+
+        // Create a staggered delay for each row (3 items per row)
+        $delay = 300 + (($this->_counter % 3) * 200);
+
+        $this->_counter++;
+
+        return $this->link($c, $url, array(
+                'data-cat' => $study->categories,
+                'class' => 'casestudies-grid-item wow fadeInUp',
+                'data-wow-delay' => $delay . 'ms',
+            )) . ' '; // Space needed between elements for justified alignment
+    }
+
+
+    // Get secondary category names
+    protected function _getTagName($catID) {
+
+        // Cache the query data if it's not already done
+        if (!$this->_tags) {
+
+            $db = Zend_Db_Table::getDefaultAdapter();
+            $query = $db->select()->from('tags')->query();
+
+            while ($category = $query->fetch()) {
+                $category = CubeIT_Util_Cms::unserializeRow($category);
+                $this->_tags[$category->id] = CubeIT_Util_Object::toArray($category);
+            }
+        }
+
+        return $this->_tags[$catID]['name'];
+    }
+
+}
index 8e92b87293652a20e918d3e4a283390ef54f3981..8e1a790b2a73d21328a04ae6c24de3c417f70afd 100644 (file)
@@ -36,9 +36,29 @@ class Cubedesigners_View_Helper_CasestudiesDetail extends CubeIT_View_Helper_Abs
 
                $res .= '<div class="casestudies-detail-content" style="' . $style . ';">';
 
+               // To simplify spacing, blocks are separated by margins (top & bottom)
+        // which collapse into each other so we don't end up with unwanted double spacing.
+        // Blocks are grouped into sections that have the same background colour.
+        // This gives the needed spacing between colour changes because margins are contained within each section
+
                $blocs = $studie->blocs;
+        $current_bg = '';
+
+        foreach ($blocs as $bloc_index => $bloc) {
+
+            $section_style = empty($bloc->bg_color) ? '' : ' style="background-color:'. $bloc->bg_color .'"';
+
+            // Are we on the first block? If so, start the first section and record the current bg colour
+            if ($bloc_index == 0) {
+                $current_bg = $bloc->bg_color;
+                $res .= '<section'. $section_style .'>';
+            } elseif ($bloc->bg_color !== $current_bg) {
+                $current_bg = $bloc->bg_color;
+                       // Background change, so start a new section
+                $res .= '</section>';
+                $res .= '<section'. $section_style .'>';
+            }
 
-               foreach ($blocs as $bloc) {
 
                        // fb($bloc);
                        $margin = '';
@@ -56,17 +76,14 @@ class Cubedesigners_View_Helper_CasestudiesDetail extends CubeIT_View_Helper_Abs
 
                        $colours = '';
 
-                       if ($bloc->bg_color != '') {
-                           $colours .= 'background-color:'. $bloc->bg_color .';';
-            }
+//                     if ($bloc->bg_color != '') {
+//                         $colours .= 'background-color:'. $bloc->bg_color .';';
+//            }
                        if ($bloc->text_color != '') {
                            $colours .= 'color:'. $bloc->text_color .';';
             }
 
-
-                       // TODO: Manage padding better so it can be disabled for blocks that have the same colour where we don't want a double gap between them.
-
-            $res .= '<div class="detail-block" style="'. $colours .'">';
+            $res .= '<div class="detail-block wow fadeInUp" style="'. $colours .'">';
 
                        //--------------
 
@@ -83,10 +100,11 @@ class Cubedesigners_View_Helper_CasestudiesDetail extends CubeIT_View_Helper_Abs
                                $width = -1;
                                $height = -1;
                                CubeIT_Image::getDimensions($visuel, $width, $height);
-                               $padding = isset($bloc->visuelpadding) ? $bloc->visuelpadding : 50;
+                               //$padding = isset($bloc->visuelpadding) ? $bloc->visuelpadding : 50;
 
                 // Convert padding into a relative measure (%) for responsive images
-                $padding = $padding / 1200 * 100;
+                //$padding = $padding / 1200 * 100;
+                $padding = 0;
 
                                //$res .= '<div class="detail-visuel" style="background-image:url(\'' . $visuel . '\');height:' . $height . 'px;' . $margin . '' . $zindex . ';padding-bottom:' . $padding . 'px;"></div>';
                 $res .= '<div class="detail-visuel">';
@@ -123,8 +141,9 @@ class Cubedesigners_View_Helper_CasestudiesDetail extends CubeIT_View_Helper_Abs
 
                        $res .= '</div>'; // .detail-block
                }
-               $res .= '</div>';
-               $res .= '</div>';
+        $res .= '</section>'; // Always close the last section that was opened...
+               $res .= '</div>'; // .casestudies-detail-content
+               $res .= '</div>'; // .case
 
                return $res;
        }
index 8e2b051ec17fb7c38f64d41b50ce4d63509999db..233f43f7ef2c81ade1c55e24a4b4b93ba1dec1e0 100644 (file)
@@ -162,7 +162,10 @@ class Cubedesigners_View_Helper_CasestudiesList extends CubeIT_View_Helper_Abstr
         $image = $this->view->imageProcess($s->visuel, $s->titre, 512, 'auto');
         $url = Cubedesigners_Util::generateAutoUri($s, $this->_datas['seourl_stu']);
 
-        $c = '<div class="img-wrapper">';
+        $s->hover_color = empty($s->hover_color) ? '#000' : $s->hover_color;
+        $hover_style = 'color:'. CubeIT_Util_Cms::hexToRGBA($s->hover_color, 0.5) .';'; // Using color so CSS can pass currentColor to :before pseudo element
+
+        $c = '<div class="img-wrapper" style="'. $hover_style .'">';
         $c.= $image;
         $c.= '</div>';
                $c.= '<h2>' . $s->titre . '</h2>';
index decd9d0a9ea362b4d7edc2ddee30c607baca3daf..3c2a76d417497b563dccbeba95dc25bf900ad8e4 100644 (file)
@@ -6,10 +6,11 @@ $this->headScript()->addScriptAndStyle('casestudies_detail');
 
 echo $this->CasestudiesDetail($this->studie);
 ?>
-<div class="casestudies-list related content jgroup">
-    <div class="title">
-        <?php echo $this->markupDotclear($this->casestudies_titre); ?>
-    </div>
+<div class="content casestudies-related">
+    <?php echo $this->caseStudiesGrid(3, $this->studie->id); ?>
+
+    <p class="casestudies-archive-link">
+        <?php echo $this->linkInternal(__('Voir tous les projets') . $this->linkArrow(), 'casestudies', array('class' => 'arrow-link')); ?>
+    </p>
 
-    <?php echo $this->CasestudiesList($this->datas, $this->studie); ?>
 </div>
index fd1306ebfd30625871ba8649c9cc74fe7da645d2..514f8f97462e2cb110e83a763e911d494ae8569a 100644 (file)
@@ -24,8 +24,8 @@ if ($this->case_studies['button']['label']) {
 
 echo $this->markupDotclear($this->case_studies['texte'], array(), array('class' => 'intro'));
 
-echo '<div class="casestudies-list jgroup">';
-echo $this->CasestudiesList($case_studies_data, null, null, $this->case_studies['display_count']);
+echo '<div class="casestudies">';
+echo $this->caseStudiesGrid(6);
 echo '</div>';
 
 if ($this->case_studies['button']['label']) {
index e9ffcab089c0775a74f5c5437ab9d1d9bf3caab5..4a4e1ee84d6ac9e6271c4ae3a640712ded381afc 100644 (file)
@@ -6,45 +6,50 @@ function load_admin() {
 
 function initSelectType() {
        $("select[data-name='selected-type']").each(function () {
-               var cible = $(this).parent().parent();
+               var cible = $(this).closest('fieldset');
                displayFormTypeElements(cible, this.value);
        });
 
        $(document).on("change", "select[data-name='selected-type']", function () {
-               var cible = $(this).parent().parent();
+               var cible = $(this).closest('fieldset');
                displayFormTypeElements(cible, this.value);
        });
 }
 
 function displayFormTypeElements(cible, choice) {
 
+       cible.find('.legend').text('Edition du contenu (' + choice + ')');
+
        switch (choice) {
                case 'text' :
-                       cible.children("#titre-element").css("display", "block");
-                       cible.children("#texte-element").css("display", "block");
-                       cible.children("#visuel-element").css("display", "none");
-                       cible.children("#visuelpadding-element").css("display", "none");
-                       cible.children("#video-element").css("display", "none");
-                       cible.children(".elementwrap-video_background").css("display", "none");
+                       cible.find("#titre-element").show();
+                       cible.find("#texte-element").show();
+                       cible.find("#visuel-element").hide();
+                       cible.find("#visuelpadding-element").hide();
+                       cible.find("#video-element").hide();
+                       cible.find(".elementwrap-video_background").hide();
+                       cible.find(".elementwrap-text_color").show();
                        break;
 
                case 'visuel' :
-                       cible.children("#titre-element").css("display", "none");
-                       cible.children("#texte-element").css("display", "none");
-                       cible.children("#visuel-element").css("display", "block");
-                       cible.children("#visuelpadding-element").css("display", "block");
-                       cible.children("#video-element").css("display", "none");
-                       cible.children(".elementwrap-video_background").css("display", "none");
+                       cible.find("#titre-element").hide();
+                       cible.find("#texte-element").hide();
+                       cible.find("#visuel-element").show();
+                       cible.find("#visuelpadding-element").show();
+                       cible.find("#video-element").hide();
+                       cible.find(".elementwrap-video_background").hide();
+                       cible.find(".elementwrap-text_color").hide();
                        break;
 
                case 'video' :
-                       cible.children("#titre-element").css("display", "none");
-                       cible.children("#texte-element").css("display", "none");
-                       cible.children("#visuel-element").css("display", "none");
-                       cible.children("#visuelpadding-element").css("display", "none");
-                       cible.children("#video-element").css("display", "block");
-                       cible.children(".elementwrap-video_background").css("display", "block");
+                       cible.find("#titre-element").hide();
+                       cible.find("#texte-element").hide();
+                       cible.find("#visuel-element").hide();
+                       cible.find("#visuelpadding-element").hide();
+                       cible.find("#video-element").show();
+                       cible.find(".elementwrap-video_background").show();
+                       cible.find(".elementwrap-text_color").hide();
                        break;
        }
 
-}
\ No newline at end of file
+}
index 649ac1d97ee196c7360c11703d0e2afdc0366ce3..16372cb6e2be4963bea51f823916495265cacce2 100644 (file)
@@ -31,7 +31,7 @@
           display: block;
           width: 100%;
           height: 100%;
-          background-color: transparent;
+          background-color: currentColor; // Inherit from .img-wrapper
           background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAMAAADUivDaAAABp1BMVEUAAAD2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH2rgH5x1H967/84Z/////+9d/714D5zGH3sxH85q/++u/98M/4vTH4wkH3uCH60nH7249RxKUJAAAAfHRSTlMAIlmJsM/k8/LOrocCRJvjmEIeiOGGHCWc+ZkjDI77+lLlTgqdCB/QzBsy598mOO/qM/DtLejJBp6PU/5MDQ+NJIuCBd4BSj6R1Bdainmizcfg3ezx+NfGr6FRIRXSmkg76dx+GSDiTwnF2xYuKMsYn/aTHYUDQ5dBetmgCFBy9QAAArJJREFUeF6l1Plb01gYxfFTwKgD0gpYiaAFR8UqKhRwwKogBXVQlEVU3HBlAGdFcT3dV5Y/2oY+eabc5N4m8fN7v3lv3txCyldX37BP208e0A7+0ljngztNh5r9FPgDh5vgVEurRlttR4Jw4mi7Tin9WAdq6Tx+gkqh+k4odXWzpraTkPv1FB0JnIbEmR46dDYMW+fO07HeC7Bx8RJd6Ou3mSFCVwYGIQgO0SV/UNhFD127/BuqDdODEVS5Qi+iV2HCNY2eaNdhGqVHo2Zh7AQ9CvlQcYqeDWNXi04b8WQilUonMltU0YMw3KBVNp0yJZSRcZTF+ijK5VNVClnKTUwCuGktFFOGdCKfqERUjVsAminanSFfoiFbMBqblLoNQKMgXnlw9UjpHGV+BzooMt7ktnAs+VGiU7hDQcnYAqtslhNFSnXhLgUZy0ONMeQnmUaD3cvMCVHVC72H+xQYi+Qem8rEDGZrJ7LKxBzmKUiWE1ustqNMRKBTsF1OZCxbptQDLFCwZVyLnNDMUzHFPEX5vT8pFZTnoIZeWscwGuYccaOQoNxDcanm5KlCpkTm4pW7upMtUeYRHtMqmbLKUmIRjRSZV9xULCob06ijna2kGUlnmVTO8QRPKRHPlG2XzHPJGtFnQBtryRUVjecAXvCnGiMAlvhTjZcAYq/cNOJC4fUkylrpoiF+p29gCOrOG+JJ9LfYFaCC+p+8HRW+kLNGoiAWFt6hAvX06C5Mncv0ZPkaTPiDXkSXUCVAD+pRbeUsXVuNYY+gny6tvYdgcICu9I3Bor+PLgz9CRt/zdKxuTHYCv9Nh2b+gUTseJQORFtjkLuqsaaJJShdH9WppI+voBbfvyFK/bfeAic+fOymre7xKTi18enzHAVr61824E646+vi6rfuSET7tvp9uisMmR8A02/S/W9HBAAAAABJRU5ErkJggg==');
           background-position: center;
           background-repeat: no-repeat;
index ab1fc397bbb93ff46828c2865a49ee649d67a9a6..8d03bc18ec6c4ad0f1a867444c2a71d6656e2382 100644 (file)
        }
 
        .casestudies-detail-content {
-               background-color: #0194d5;
+
+        section {
+          overflow: hidden; // So margins are contained within each section, extending the section bg colour
+        }
 
                .detail-block {
-                       .constrain(padding-top, 7.5vw);
-                       .constrain(padding-bottom, 7.5vw);
+                       .constrain(margin-top, 5vw);
+                       .constrain(margin-bottom, 5vw);
                        .constrain(padding-left, 10vw);
                        .constrain(padding-right, 10vw);
+
+            //&:first-of-type {
+            //  margin-top: 0;
+            //  .constrain(padding-top, 5vw);
+            //}
                }
 
                .detail-text {
                        font-size: 60px;
                        line-height: 1.1;
                        margin-bottom: 0.5em;
+
+            @media screen and (max-width: 1000px) {
+              font-size: 5.85vw;
+            }
+            @media screen and (max-width: 685px) {
+              font-size: 40px;
+            }
                }
 
                .detail-text .dotclear {
 
                        img {
                                min-width: 462px; // 530 - 34*2
-                               vertical-align: bottom; // Stops gap under image when right at the bottom
+                display: block;
+                margin: 0 auto;
+                               //vertical-align: bottom; // Stops gap under image when right at the bottom
                                // (caused by images being inline elements and having space for character descenders).
                                // This is a better solution than setting it to display:block because images can still be aligned this way.
                        }
        }
 }
 
-.casestudies-list {
-       padding-top: 60px;
+.casestudies-related {
+  .constrain(padding-top, 2.5vw);
+  .constrain(padding-bottom, 5vw);
+}
+
+.casestudies-archive-link {
+  text-align: right;
 }
diff --git a/less/casestudies_grid.less b/less/casestudies_grid.less
new file mode 100644 (file)
index 0000000..0efc54e
--- /dev/null
@@ -0,0 +1,66 @@
+@import "00-constants";
+
+.casestudies-grid {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  .constrain(grid-gap, 5vw);
+  .constrain(margin-top, 2.5vw);
+  .constrain(margin-bottom, 2.5vw);
+
+  @media screen and (max-width: 1400px) {
+    grid-gap: 2.5vw
+  }
+
+  &-item {
+    position: relative;
+    &:hover {
+      .casestudies-grid-item-label {
+        opacity: 1;
+      }
+    }
+
+    &-image {
+      display: block;
+    }
+
+    &-label {
+      overflow: hidden;
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      text-align: center;
+      background-color: rgba(0, 0, 0, 0.6);
+      color: #fff;
+      padding: 1em;
+      opacity: 0;
+      transition: opacity 0.3s ease-out;
+    }
+
+    &-title, h2 {
+      font-size: 50px;
+      line-height: 1;
+
+      @media screen and (max-width: 1400px) {
+        font-size: 3.5vw;
+      }
+      @media screen and (max-width: 1400px) {
+        font-size: 24px;
+      }
+    }
+
+    &-tags, h3 {
+      font-size: 18px;
+      margin-top: 0.5em;
+
+      @media screen and (max-width: 1000px) {
+        font-size: 15px;
+      }
+    }
+  }
+}