<div class="container">

<h1>Dynamic Image grid</h1>

  
  <label for="photos">Photos</label>
  <input type="number" id="photos" value="5" min="1" max="10" onchange="calculateGrid()" />
  <select id="layout" onchange="calculateGrid(true)">
    <option>single</option>
    <optgroup label="2 Photos" id="group-2">
      <option selected>p-p</option>
      <option>p-l</option>
      <option>l-l</option>
      <option>l-p</option>
    </optgroup>
    <optgroup label="3 Photos" id="group-3">
      <option>p-p-p</option>
      <option>p-p-l</option>
      <option>p-l-l</option>
      <option>p-l-p</option>
      <option>l-l-l</option>
      <option>l-l-p</option>
      <option>l-p-p</option>
      <option>l-p-l</option>
    </optgroup>
    <optgroup label="4 or more photos" id="group-4">
      <option>p-p-p-p</option>
      <option>p-p-p-l</option>
      <option>p-p-l-p</option>
      <option>p-l-p-p</option>
      <option>p-p-l-l</option>
      <option>p-l-l-p</option>
      <option>p-l-p-l</option>
      <option>l-l-l-l</option>
      <option>l-l-l-p</option>
      <option>l-l-p-l</option>
      <option>l-p-l-l</option>
      <option>l-l-p-p</option>
      <option>l-p-p-l</option>
      <option>l-p-l-p</option>
    </optgroup>
  </select>
  <ul id="imageGrid" class="h-h">
  </ul>

<aside>

  <h2>About</h2>

  <p>A dynamically generated image grid that adjusts to show a thumbnail at the correct size depending on the orentation of the photo.</p>

  <h2>What could be improved?</h2>

  <p>Only the first image is given a special treatment, but in theory any of the images shown could be given a different treatment</p>
  
  </aside>
  
</div>

@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;600&display=swap');

body {
  background: linear-gradient(-45deg, #70e1f5, #ffd194);
  font-family: 'Open Sans', sans-serif;
}

h1,
aside,
.container {
  margin: 0 auto 20px;
  max-width: 500px;
}

.container {
  background: white;
  padding: 20px;
}

ul {
  display: grid;
  grid-gap: 5px 5px;
  height: 300px;
  list-style-type: none;
  margin: 0;
  overflow: hidden;
  padding: 0;
  width: 300px;
}
li {
  background-image: url(https://images.unsplash.com/photo-1590943101617-6642f6f1e909?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60);
  background-position: center;
  background-size: cover;
  display: block;
}

.single {
	display: block;
}
#additionalImages {
	color: #FFF;
	font-size: 2em;
	font-weight: bold;
	text-shadow: 0 0 5px #000;
}
li {
	height: 100%;
	width: 100%;
}

.p-p,
.p-l {
	grid-template-columns: 50% 50%;
}

.p-p li,
.p-l li {
	grid-row-start: 0;
	grid-row-end: 2;
}

.l-l,
.l-p {
	grid-template-rows: 50% 50%;
}

.l-l li,
.l-p li {
	grid-column-start: 0;
	grid-column-end: 2;
}


.p-p-p,
.p-p-l,
.p-l-l,
.p-l-p,
.l-l-l,
.l-l-p,
.l-p-p,
.l-p-l {
	grid-template-columns: 50% 50%;
	grid-template-rows: 50% 50%;
}
.p-p-p li:first-child,
.p-p-l li:first-child,
.p-l-l li:first-child,
.p-l-p li:first-child {
	grid-row-start: 1;
	grid-row-end: 3;
}

.l-l-l li:first-child,
.l-l-p li:first-child,
.l-p-p li:first-child,
.l-p-l li:first-child {
	grid-column-start: 1;
	grid-column-end: 3;
}

.p-p-p-p,
.p-p-p-l,
.p-p-l-p,
.p-l-p-p,
.p-p-l-l,
.p-l-l-p,
.p-l-p-l {
	grid-template-columns: 50% 50%;
	grid-template-rows: calc(100%/3) calc(100%/3) calc(100%/3);
}

.l-l-l-l,
.l-l-l-p,
.l-l-p-l,
.l-p-l-l,
.l-l-p-p,
.l-p-p-l,
.l-p-l-p {
	grid-template-columns: calc(100%/3) calc(100%/3) calc(100%/3);
	grid-template-rows: 50% 50%;
}

.p-p-p-p li:first-child,
.p-p-p-l li:first-child,
.p-p-l-p li:first-child,
.p-l-p-p li:first-child,
.p-p-l-l li:first-child,
.p-l-l-p li:first-child,
.p-l-p-l li:first-child {
	grid-column-start: 1;
	grid-column-end: 1;
	grid-row-start: 1;
	grid-row-end: 4;
}

.l-l-l-l li:first-child,
.l-l-l-p li:first-child,
.l-l-p-l li:first-child,
.l-p-l-l li:first-child,
.l-l-p-p li:first-child,
.l-p-p-l li:first-child,
.l-p-l-p li:first-child {
	grid-column-start: 1;
	grid-column-end: 4;
	grid-row-start: 1;
	grid-row-end: 1;
}
function $(id) {
  return document.getElementById(id);
}

function calculateGrid(ignoreAdjustingReconfiguration) {
  var i = 0,
	count = $('photos').value;
	if(!ignoreAdjustingReconfiguration) {
		allowedConfigurations(count);
	}
  $('imageGrid').className = $('layout').value;
  $('imageGrid').innerHTML = '';
  while(i < count) {
    if(i < 4) {
		if(i == 3 && count > 4) {
      		$('imageGrid').innerHTML += '<li class="hasMore"><span id="additionalImages">+1</span></li>';
		} else {
      		$('imageGrid').innerHTML += '<li></li>';
		}
    } else {
      $('additionalImages').innerHTML = '+'+(i-2);
    }
    i++;
  }
}

function allowedConfigurations(count) {
	var matchfound = false;
	if(count == 2) {
		$('group-2').disabled = false;
		$('group-3').disabled = true;
		$('group-4').disabled = true;
		for(var i = 0; i < $('group-2').children.length; i++) {
			if(layout.options[layout.options.selectedIndex].value == $('group-2').children[i].value) {
				matchfound = true;
			}
		}
		if(!matchfound) {
			$('group-2').children[0].selected = true;
		}
	} else if(count == 3) {
		$('group-2').disabled = true;
		$('group-3').disabled = false;
		$('group-4').disabled = true;
		for(var i = 0; i < $('group-3').children.length; i++) {
			if(layout.options[layout.options.selectedIndex].value == $('group-3').children[i].value) {
				matchfound = true;
			}
		}
		if(!matchfound) {
			$('group-3').children[0].selected = true;
		}
	} else if(count >= 4) {
		$('group-2').disabled = true;
		$('group-3').disabled = true;
		$('group-4').disabled = false;
		for(var i = 0; i < $('group-4').children.length; i++) {
			if(layout.options[layout.options.selectedIndex].value == $('group-4').children[i].value) {
				matchfound = true;
			}
		}
		if(!matchfound) {
			$('group-4').children[0].selected = true;
		}
	} else {
		$('group-2').disabled = true;
		$('group-3').disabled = true;
		$('group-4').disabled = true;
		$('layout').children[0].selected = true;
	}
}

calculateGrid();
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.