mixin project(data)
   a.card.item(class='flex flex-col justify-start items-start ', href='', title=data.title)
      figure(class='shadow-md hover:shadow-lg hover:shadow-sm bg-white mb-4 rounded-sm')
         picture.relative.mb-4.overflow-hidden.lozad(style='display: block; object-fit: cover; min-height: 1rem;' height="284" data-iesrc=data.image+'/400x400' data-alt=data.title)
           source(srcset=data.image+'?w=600&h=600&fit=crop&crop=edges&auto=compress' media='(min-width: 980px)')
           source(srcset=data.image+'?w=400&h=400&fit=crop&crop=edges&auto=compress' media='(min-width: 240px)')
           span.absolute.right-0.bottom-0.m-2.text-sm.font-bold.text-gray-500
             = '#'+data.category
           .flag.absolute.bg-red-500.w-24.h-24.right-0.top-0
           noscript
             img(src=data.image+'?w=400&h=400&fit=crop&crop=edges&auto=compress' alt=data.title)
         figcaption(class='p-6 pt-0')
            h3.headline-4.text-base.font-bold.leading-tight.mb-1=data.title
            p.caption.text-xs.leading-regular=data.desc
            
-
   var projects = [
     {title: "Project Four", category: "UX", title: "Project Title", desc: "A project description", image: "https://www.datocms-assets.com/10166/1692051814-trout-creek-stack.webp"},
     {title: "Project One", category: "Design", title: "Project Title", desc: "A project description which is longer; consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", image: "https://www.datocms-assets.com/10166/1692051761-mt-adams-perseids-23-2475.jpg"},
     {title: "Project Two", category: "Motion", title: "Project Title, which might wrap or be longish", desc: "A project description", image: "https://www.datocms-assets.com/10166/1685323438-columbia-gorge-wildflowers-03.jpg"},
     {title: "Project Four", category: "UX", title: "Project Title", desc: "A project description", image: "https://www.datocms-assets.com/10166/1685555719-nootka-lupine-with-mt-hood-gresham-butte.jpeg"},
     {title: "Project Three", category: "Video", title: "Foxglove in early summer", desc: "Wilczek: one of my notions about the human concept of beauty is that beautiful things are things that evolution has primed us to enjoy and want to come back to and feel pleasure in experiencing.", image: "https://www.datocms-assets.com/10166/1611000738-foxglove-with-sun-glare.jpeg"},
     {title: "Project Four", category: "UX", title: "Project Title", desc: "A project description", image: "https://www.datocms-assets.com/10166/1604713099-tamron-85mm-tests-2020-9171.jpeg"},
   ]
nav.flex.items-center.justify-between.flex-wrap.bg-gray-200.p-6(role='navigation')
  .flex.items-center.flex-shrink-0.text-gray-500.mr-6
    a.w-36(class='sm:48' href='/' title='Home')
      img.logo.h-16.m-2(src='https://allanwhite.design/images/easel-logo.svg')
  .block(class='md:hidden')
    button#toggle.toggle-me.flex.items-center.px-3.py-2.rounded.text-blue-400(aria-controls='menu' class='hover:border hover:text-black')
      svg.icon.icon-default.fill-current.h-6.w-6.outline-none(viewbox='0 0 20 20' xmlns='http://www.w3.org/2000/svg')
        title Menu
        path(d='M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z')
      svg.icon.icon-active.fill-current.h-6.w-6.hidden.outline-none(xmlns='http://www.w3.org/2000/svg' viewbox='0 0 32 32')
        title Close Menu
        path(d='M16 13.7L4.7 2.4 2.4 4.7 13.7 16 2.4 27.3l2.3 2.3L16 18.3l11.3 11.3 2.3-2.3L18.3 16 29.6 4.7l-2.3-2.3L16 13.7z')
  .toggle-target.w-full.block.flex-shrink.hidden(class='md:flex md:items-center md:w-auto')
    .nav-items.text-sm(class='md:flex-grow md:mr-4')
      a.nav-item.text-center.block.p-2.mt-2.text-blue-800(href='#' class='md:mt-0 md:inline-block hover:text-gray-500') Docs
      a.nav-item.text-center.block.p-2.mt-2.text-blue-800(href='#' class='md:mt-0 md:inline-block hover:text-gray-500') Examples
    .text-center.mx-auto.block
      a.nav-item.text-sm.inline-block.mt-4.px-4.py-2.leading-none.border.rounded.text-blue-500.border-blue-500.mt-4(href='#' class='md:mt-0 hover:border-black hover:text-black md:mt-0') Call to action

section.p-8.max-w-3xl.mx-auto
  h1.text-2xl.font-bold Horizontal scrolling cards demo
     span.ml-3.font-bold.uppercase.text-sm.tracking-widest.text-gray-600 CSS-only
  p.mb-3.text-sm In this exploration, I wanted to see how mobile-portrait-width cards might be more effectively organized using a simple design pattern of a scrolling horizontal list. It's implemented with CSS, and doesn't require any scripting for the layout or interactions. <a href="https://github.com/ApoorvSaxena/lozad.js">Lozad</a> is used for lazy-loading.
  p.mb-3.text-sm There's some nice hover effects in place, and I use a simple CSS mask gradient on the edge to indicate there are more cards. <em>To-do:</em> Enable tabindex for accessibilty.
  p.mb-3.text-sm You can read a more detailed write-up <a class="font-bold" at my href="https://dev.to/allanwhite/css-only-horizontally-scrolling-cards-with-snapping-pl0" title="Dev.To article">Dev.to blog</a>.

section.project-list.opacity-0.no-scrollbar.mask-right(data-toggle-class="toggled" class="lozad")
   for data in projects
     +project(data)

section.p-8.max-w-2xl.mx-auto
  h3.text-xl Responsive Navigation 
  p This responsive navigation requires a small bit of JS, but is simpler, more accessible and more flexible than a pure CSS solution. Classes are toggled on the targeted div as well as the button, allowing for some effects.
  p In a production scenario, we&apos;d take some of the tailwind classes and combine them with the 
    code @apply
    |  directive.
View Compiled
// @import url('https://rsms.me/inter/inter.css');
html { font-family: 'Inter', sans-serif; background-color: var(--bg-color) }
@supports (font-variation-settings: normal) {
  html { font-family: 'Inter var', sans-serif; }
}
:root {
   --gutter: 1rem;
   --bg-color: #edf2f7; // bg-gray-200
   --speed: 0.3s;
}

.project-list { // inspired by https://uxdesign.cc/creating-horizontal-scrolling-containers-the-right-way-css-grid-c256f64fc585
   display: grid;
   grid-gap: calc(var(--gutter) * 1.5 + 1vw);
   grid-template-columns: 1rem;
   grid-template-rows: minmax(10rem, 1fr);
   grid-auto-flow: column;
   grid-auto-columns: 380px; // small viewports
   @media (min-width: 800px) {
      // grid-auto-columns: calc(var(--gutter) * 12);
   }
   overflow-x: scroll;
   scroll-snap-type: x proximity;
   scroll-snap-points-x: repeat(100%);
   scroll-snap-type: mandatory;
   scroll-snap-destination: 100% 0%;
   // padding-bottom: calc(.75 * var(--gutter));
   // margin-bottom: calc(-.25 * var(--gutter));
   -webkit-overflow-scrolling: touch;
   &::-webkit-scrollbar {
      display: none;
   }
   @media (min-width: 800px) { // yeah make this large-viewport only
      &:hover {
         .item {
            opacity: 0.6;
            filter: blur(2px);
            // transition-delay: 0.3s;
         }
      }
   }
}
.mask-right {
   @media screen and (min-width: 640px) {
      mask: linear-gradient(-90deg, rgba(0,0,0,0) 3%, rgba(0,0,0,1) 20%);
      -webkit-mask: linear-gradient(-90deg, rgba(0,0,0,0) 3%, rgba(0,0,0,1) 20%);
   }
}
.flag {
   transition: all var(--speed) ease;
   transform: translate3d(6rem, -6rem, 0) rotate(45deg);
}
.project-list:before,
.project-list:after {
   content: '';
   width: 10px;
}
.item {
   scroll-snap-align: center;
   padding: calc(var(--gutter) / 2 * 1.5);
   transition: all var(--speed);
   &:hover {
      opacity: 1 !important;
      filter: blur(0) !important;
      .flag {
         transform: translate3d(4rem, -4rem, 0) rotate(45deg); // greater the offset value here, smaller the 'tag' is
      }
   }
}
.item:nth-last-child {
   margin-right: 5rem;
}
.lozad {
   transition: opacity var(--speed);
   opacity: 0;
}
.lozad.loaded {
   opacity: 1;
}
.toggled {
   transition: opacity var(--speed);
   transition-delay: var(--speed);
   opacity: 1;
}
.no-scrollbar {
   scrollbar-width: none;
   margin-bottom: 0;
   padding-bottom: 0;
}
.no-scrollbar::-webkit-scrollbar {
   display: none;
}


.is-active { // when toggle is triggered, the target gets .is-active
   display: block;
}
$speed-default: 150ms;
.icon, .toggle-me, .nav-item {
   transition: all var(--speed) ease; // for production: just apply to [color | position | opacity]
}
.is-toggled {
   // border-color: gold;
   color: black;
   .icon-default {
      display: none;
      // opacity: 0;
   }
   .icon-active {
      display: block;
      // opacity: 1;
   }
}

// == working on some transitions for menu elements. 

// a.nav-item {
//    transition: opacity 0.2s, transform 0.2s ease;
//    transform: translate3D(0, 3px, 0);
//    opacity: 0;
// }
.nav-items {
   @for $i from 1 through 6 {
      // add a small bit of delay for n children. Estimating 8
      $t: 0.05s * $i + 0.2s;
      &:nth-child(#{$i}) a {
         transition-delay: $t;
      }
   }
}
.toggle-target.is-active .nav-items a.nav-item {
   transform: translate3D(0, 0, 0);
   opacity: 1;
}

%prefade {
    transition: opacity 0.2s, transform 0.2s ease;
    transform: translate3D(-4px, 0, 0);
    transition-delay: 0.8s;
    opacity: 0;
}
.fade-1 { // might need a coherent timing function, so delays are coordinated
    @extend %prefade;
    transition-delay: 0.8s;
}
View Compiled
(function() {
    var toggler = document.querySelector('.toggle-me');
    var toggleTarget = document.querySelector('.toggle-target');

    toggler.addEventListener('click', function() {
         toggler.toggleAttribute('aria-expanded');
         toggleTarget.classList.toggle('is-active');
         toggler.classList.toggle('is-toggled');
    });
})();
const observer = lozad('.lozad', {
    rootMargin: '100px 0px', // like css margin
    // threshold: 0.1 // ratio of element convergence
    loaded: function(el) {
        // Custom implementation on a loaded element
        el.classList.add('loaded');
        // el.classList.remove('loading-blur');
    }
});
observer.observe();

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/1.1.2/tailwind.min.css
  2. https://rsms.me/inter/inter.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.9.0/lozad.min.js