tiles.html
<section class="tiles">
    <div class="container">
        <div class="tiles__grid -featured">
            <!-- 
                Thirds grid: 3 columns at desktop
                - (no modifier) = 1×1 small square
                - -featured = 2×2 large square  
                - -wide = 2×1 horizontal
                - -tall = 1×2 vertical
            -->
            <div class="tiles__set -thirds">
                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1557683316-973673baf926?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -featured -dark -has-content">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1478760329108-5c3ed9d495a0?w=800&h=800&fit=crop" alt="">
                    </div>
                    <div class="tiles__overlay"></div>
                    <div class="tiles__content">
                        <h3 class="tiles__heading">Featured tile</h3>
                        <p class="tiles__description">This tile uses -featured to span 2×2</p>
                        <div class="tiles__actions">
                            <a href="#" class="button -outline">Learn More</a>
                        </div>
                    </div>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1579546929518-9e396f3cc809?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>
            </div>

            <!-- 
                Halves grid: 4 columns at desktop
                Add any number of tiles, any combination of modifiers
            -->
            <div class="tiles__set -halves">
                <div class="tiles__tile -featured">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1557682250-33bd709cbe85?w=800&h=800&fit=crop" alt="">
                    </div>
                    <button class="tiles__more">
                        <svg class="icon" aria-hidden="true" role="presentation">
                            <use href="/main-icons-sprite.svg#images" />
                        </svg>
                        View 10 More Images
                    </button>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1614850523296-d8c1af93d400?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -wide">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1557682224-5b8590cd9ec5?w=600&h=300&fit=crop" alt="">
                    </div>
                </div>
            </div>

            <!-- 
                3-column grid with mixed layout:
                Row 1: Wide (2 cols) + Tall (1 col, spans 2 rows)
                Row 2: Small + Small + Tall continues
            -->
            <div class="tiles__set -thirds">
                <div class="tiles__tile -wide -dark -has-content">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&h=400&fit=crop" alt="">
                    </div>
                    <div class="tiles__overlay"></div>
                    <div class="tiles__content">
                        <h3 class="tiles__heading">Wide tile</h3>
                        <p class="tiles__description">Spans 2 columns horizontally</p>
                    </div>
                </div>

                <div class="tiles__tile -tall -dark">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?w=400&h=800&fit=crop" alt="">
                    </div>
                    <div class="tiles__overlay"></div>
                    <div class="tiles__content">
                        <h3 class="tiles__heading">Tall tile</h3>
                        <p class="tiles__description">Spans 2 rows</p>
                    </div>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>
            </div>

            <!-- 
                4-column grid: Small + Wide + Small (1+2+1 = 4 cols)
                Visually continues from the section above (no extra gap)
            -->
            <div class="tiles__set -halves">
                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1501854140801-50d01698950b?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -wide">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1433086966358-54859d0ed716?w=800&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>
            </div>

            <!-- 
                3-column staggered: DOM order below is for md+ placement.
                Mobile-first CSS order: fill|fill / short|short / fill|short; at md+ order resets so DOM matches desktop stagger.

                Grid rows (md+):    Col 1      Col 2      Col 3
                Row 1:      [fill-top] [short]  [fill-top]
                Row 2:      [fill-bot] [fill-top] [fill-bot]
                Row 3:      [short] [fill-bot] [short]
            -->
            <div class="tiles__set -thirds -staggered">
                <!-- Tile order (desktop DOM): fill, short, fill, fill, short, short -->
                <div class="tiles__tile -fill">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1557683316-973673baf926?w=400&h=600&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -short">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1478760329108-5c3ed9d495a0?w=800&h=450&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -fill">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1579546929518-9e396f3cc809?w=400&h=600&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -fill">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1557682250-33bd709cbe85?w=800&h=450&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -short">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=400&h=600&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -short">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1614850523296-d8c1af93d400?w=800&h=450&fit=crop" alt="">
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>
index.scss
@use '@/core' as *;

.tiles {
  $b: &;

  padding: 2rem 0;

  &__grid {
    --columns: 1;
    --gap: var(--root-gap, 1rem);

    display: grid;
    gap: var(--gap);
    grid-template-columns: repeat(var(--columns), 1fr);
    grid-auto-flow: dense;

    @include at(md) {
      --columns: 2;
    }

    @include at(lg) {
      --columns: 4;
    }
  }

  &__tile {
    background-color: var(--tile-bg, var(--color-gray-100, #f5f5f5));
    display: grid;
    overflow: hidden;
    position: relative;

    // Position for content tiles
    &.-has-content {
      align-items: end;
    }
  }

  &__media {
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    width: 100%;

    img,
    video {
      height: 100%;
      object-fit: cover;
      width: 100%;
    }
  }

  &__content {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    max-width: 32rem;
    padding: 2rem;
    position: relative;
    z-index: 1;
  }

  &__heading {
    color: inherit;
    font-family: var(--root-font-family-heading);
    font-size: clamp(2rem, 5vw, 3.5rem);
    font-weight: 700;
    line-height: 1.1;
    margin: 0;
  }

  &__description {
    line-height: 1.5;
    margin: 0;
  }

  &__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-block-start: 0.5rem;
  }

  // Dark theme for tiles with dark backgrounds
  &__tile.-dark {
    color: #ffffff;

    #{$b}__heading,
    #{$b}__description {
      color: inherit;
    }

    .button.-outline {
      border-color: currentcolor;
      color: inherit;

      &:hover {
        background-color: #ffffff;
        border-color: #ffffff;
        color: #000000;
      }
    }
  }

  // Light theme for tiles with light backgrounds
  &__tile.-light {
    color: var(--root-color, #333333);
  }

  // Featured layout container
  &__grid.-featured {
    display: flex;
    flex-direction: column;
    gap: var(--gap);
  }

  /**
   * Tile Sets - Flexible grid layouts
   *
   * Set modifiers define the column structure:
   *   -thirds  : 3-column grid at desktop (for 1/3 + 2/3 style layouts)
   *   -halves  : 4-column grid at desktop (for 50/50 style layouts)
   *
   * Tile modifiers control spanning (work in any set):
   *   -featured : Spans 2 columns × 2 rows (large square)
   *   -wide     : Spans 2 columns × 1 row (horizontal banner)
   *   -tall     : Spans 1 column × 2 rows (vertical banner)
   *   (default) : 1 column × 1 row (small square)
   *
   * Tiles flow in DOM order with dense packing to fill gaps.
   * Add as many tiles as needed - the grid adapts automatically.
   */
  &__set {
    container-type: inline-size;
    display: grid;
    gap: var(--gap);
    grid-auto-flow: dense;

    // Thirds: 3-column grid
    &.-thirds {
      --_tile-size: 100cqi; // Mobile: full width

      grid-template-columns: 1fr;

      @include at(sm) {
        --_tile-size: calc((100cqi - var(--gap)) / 2);

        grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
      }

      @include at(md) {
        --_tile-size: calc((100cqi - var(--gap) * 2) / 3);

        grid-template-columns: repeat(3, 1fr);
      }

      // Staggered layout: uses -short (1 row) and -fill (2 rows) tiles
      // No explicit row heights - tiles define their own heights via aspect-ratio/min-height
      &.-staggered {
        // Row-major flow: dense backfills holes and can strand late tiles in implicit/zero-sized tracks
        // with rowspan + mixed spans (last items “missing” in some engines).
        grid-auto-columns: minmax(0, 1fr);
        grid-auto-flow: row;

        // Short height = tile-width × 9/16
        --_short-height: calc(var(--_tile-size) * 0.5625);

        #{$b}__tile.-fill {
          // Fill = 2 shorts + 1 gap
          min-height: calc(var(--_short-height) * 2 + var(--gap));
          aspect-ratio: auto;
        }

        // Absolute .tiles__media gives no in-flow height — tile needs intrinsic block size.
        // aspect-ratio:auto + height:100% below md was cyclic → 0× height in some rows.
        #{$b}__tile.-short {
          place-self: stretch stretch;
          aspect-ratio: 16 / 9;
          height: auto;
          min-height: 0;
          width: 100%;

          @include at(md) {
            align-self: start;
          }
        }

        // Sandwich last two -short tiles between fills (demo = 6 tiles). md+ uses DOM order.
        > .tiles__tile:nth-child(1) {
          order: 1;
        }

        > .tiles__tile:nth-child(2) {
          order: 6;
        }

        > .tiles__tile:nth-child(3) {
          order: 2;
        }

        > .tiles__tile:nth-child(4) {
          order: 5;
        }

        > .tiles__tile:nth-child(5) {
          order: 3;
        }

        > .tiles__tile:nth-child(6) {
          order: 4;
        }

        @include at(md) {
          > .tiles__tile:nth-child(1),
          > .tiles__tile:nth-child(2),
          > .tiles__tile:nth-child(3),
          > .tiles__tile:nth-child(4),
          > .tiles__tile:nth-child(5),
          > .tiles__tile:nth-child(6) {
            order: 0;
          }
        }
      }
    }

    // Halves: 4-column grid (good for 50/50 layouts with sub-divisions)
    &.-halves {
      --_tile-size: 100cqi; // Mobile: full width

      grid-template-columns: 1fr;

      @include at(sm) {
        --_tile-size: calc((100cqi - var(--gap)) / 2);

        // Allow columns to shrink more before dropping
        grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
      }

      @include at(md) {
        --_tile-size: calc((100cqi - var(--gap) * 3) / 4);

        grid-template-columns: repeat(4, 1fr);
      }
    }

    // =======================================================================
    // Tile modifiers - control spanning behavior (mobile-first)
    // Default: 1×1 square tile on all screens
    // Modifiers add spanning at md breakpoint and above
    // =======================================================================

    // Default: 1×1 square tile
    #{$b}__tile {
      aspect-ratio: 1;
    }

    // Featured: Large 2×2 tile at md+ (square on mobile)
    #{$b}__tile.-featured {
      @include at(sm) {
        // Span all available columns at sm to avoid implicit tracks.
        grid-column: 1 / -1;
        grid-row: span 2;
      }

      @include at(md) {
        grid-column: span 2;
        grid-row: span 2;
      }
    }

    // Wide: Horizontal 2×1 tile at md+ (square on mobile)
    #{$b}__tile.-wide {
      @include at(sm) {
        // Span all available columns at md to avoid implicit tracks.
        grid-column: 1 / -1;
        aspect-ratio: auto;
        align-self: stretch;
        min-height: var(--_tile-size, 200px);
      }

      @include at(md) {
        grid-column: span 2;
      }
    }

    // Tall: Vertical 1×2 tile at md+ (square on mobile)
    #{$b}__tile.-tall {
      @include at(sm) {
        grid-row: span 2;
        aspect-ratio: auto;
        align-self: stretch;
        min-height: calc(var(--_tile-size, 200px) * 2 + var(--gap));
      }
    }

    // Short: default compact landscape from sm (square below sm via &__tile); staggered overrides below md
    #{$b}__tile.-short {
      @include at(sm) {
        aspect-ratio: 16 / 9;
      }
    }

    // Fill: Spans 2 grid rows at md+, pairs with -short tiles (square on mobile)
    #{$b}__tile.-fill {
      @include at(md) {
        grid-row: span 2;
        aspect-ratio: auto;
        align-self: stretch;
      }
    }
  }

  // "View more" button - positioned within tile content
  &__more {
    align-items: center;
    align-self: flex-end;
    background: #ffffff;
    border-radius: var(--root-border-radius);
    border: none;
    box-shadow: 0 2px 4px rgb(0 0 0 / 0.15);
    color: var(--link-color, var(--color-blue));
    cursor: pointer;
    display: inline-flex;
    font-size: 0.875rem;
    font-weight: 500;
    gap: 0.5rem;
    margin: auto 2rem 2rem auto; // Push to bottom-right with tile padding
    padding: 0.5rem 1rem;
    position: relative;
    transition: box-shadow var(--root-ease-out-fast);
    white-space: nowrap;
    z-index: 2;

    &:hover {
      box-shadow: 0 4px 8px rgb(0 0 0 / 0.2);
    }

    svg,
    .icon {
      fill: currentcolor;
      height: 1em;
      width: 1em;
    }
  }

  // Gradient overlay for better text readability
  &__overlay {
    background: linear-gradient(to top, rgb(0 0 0 / 0.7) 0%, rgb(0 0 0 / 0.3) 50%, rgb(0 0 0 / 0) 100%);
    inset: 0;
    position: absolute;
    z-index: 0;
  }
}