├── README.md ├── alternatingTextAndImage.css ├── alternatingTextAndImage.html ├── htmlmocker.js ├── index.html ├── multipleCards.css ├── multipleCards.html └── stylesheet.css /README.md: -------------------------------------------------------------------------------- 1 | This is an idea for mocking HTML data that lets you test whether your layout breaks on any screen size for any 2 | unexpected dynamic data. 3 | 4 | This script scans the document for class names with a specific pattern and periodically randomizes the content 5 | of those elements while meeting the constraints specified in the class name. This can let you quickly test 6 | whether your layout breaks for any dynamic content that you may not have thought about. This should ideally be used 7 | with a responsive design testing tool such as DevTools or Sizzy/Bizzy. 8 | 9 | For eg, if your HTML is this: 10 | ``` 11 |

article headline

12 | ``` 13 | Then, this script will find this tag, and periodically replace the innerText of h1 with random strings which are 14 | anywhere between the minimum length 30 and maximum length 90. Similiarily, constraints on image sizes, 15 | video sizes, # of children elements in a list etc. For a list, mock children elements will be generated 16 | 17 | Some examples of the constraints are: 18 | - size-30__50 (from 30 to 50 chars) 19 | - size-\_\_50 (up to 50 chars) 20 | - size-50__ (from 50 to anysize) 21 | - size-45 (exactly 45 chars) 22 | - size-"50x50__500x500" (50px x 50px to 600px x 600px) 23 | - size-3__7 (list size from minimum 3 to maximum 7) 24 | 25 | Ideas for more constraints: 26 | - Allowed characters (such as whitespace or emojis) 27 | - Image's aspect ratio 28 | - Video sizes, formats 29 | -------------------------------------------------------------------------------- /alternatingTextAndImage.css: -------------------------------------------------------------------------------- 1 | /* layout relevant styles */ 2 | html, body { 3 | width: 100%; 4 | height: 100%; 5 | margin: 0; 6 | } 7 | 8 | body { 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | } 13 | 14 | main { 15 | display: flex; 16 | width: 50%; 17 | flex-direction: column; 18 | } 19 | 20 | article { 21 | display: flex; 22 | flex-direction: row; 23 | align-items: center; 24 | } 25 | article:nth-child(odd) { 26 | flex-direction: row-reverse; 27 | text-align: right; 28 | } 29 | article .img { 30 | flex-basis: 50%; 31 | } 32 | article .img img { 33 | width: 100%; 34 | } 35 | article .content { 36 | flex-basis: 50%; 37 | display: flex; 38 | flex-direction: column; 39 | } 40 | article .content .cta { 41 | align-self: center; 42 | } 43 | 44 | /* other styles */ 45 | article { 46 | font-family: Roboto, sans-serif; 47 | } 48 | article .content { 49 | padding: 50px; 50 | box-sizing: border-box; 51 | } 52 | article h1 { 53 | font-size: 1.5rem; 54 | } 55 | article p { 56 | font-size: 0.9rem; 57 | } 58 | 59 | .cta { 60 | width: 180px; 61 | height: 40px; 62 | border-radius: 20px; 63 | border: 2px solid #000; 64 | line-height: 40px; 65 | text-align: center; 66 | font-size: 1.1rem; 67 | color: #000; 68 | text-decoration: none; 69 | margin-top: 10px; 70 | position: relative; 71 | box-sizing: border-box; 72 | } 73 | .cta:hover { 74 | background: #000; 75 | color: #fff; 76 | } 77 | .cta:hover::after { 78 | right: 10px; 79 | } 80 | .cta::after { 81 | content: ">"; 82 | font-family: "Jua"; 83 | position: absolute; 84 | right: 15px; 85 | transition: right 1s ease; 86 | } 87 | 88 | a, a:hover { 89 | transition: all 0.3s ease; 90 | } 91 | 92 | -------------------------------------------------------------------------------- /alternatingTextAndImage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HTML MOCKER DEMO 5 | 6 | 7 | 8 |
9 |
10 |
11 | Mobile phone near a laptop 16 |
17 |
18 |

Headline 1

19 |

20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam 21 | vulputate est non neque faucibus elementum. 22 |

23 | button 1 24 |
25 |
26 |
27 |
28 | Curved computer display on a table 33 |
34 |
35 |

Headline 2

36 |

37 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam 38 | vulputate est non neque faucibus elementum. 39 |

40 | button 2 41 |
42 |
43 |
44 |
45 | Mobile phone with black screen 50 |
51 |
52 |

Headline 3

53 |

54 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum 55 | rhoncus urna sed mauris facilisis convallis. Nam blandit, ante ut 56 | ultricies iaculis, sem ligula molestie ipsum, mollis iaculis diam 57 | urna eget massa. 58 |

59 | button 3 60 |
61 |
62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /htmlmocker.js: -------------------------------------------------------------------------------- 1 | console.log("script loaded"); 2 | const INTERVAL_TIME = 6000; 3 | 4 | let classList = ["hm-text", "hm-img", "hm-list"]; 5 | 6 | function generateRandomString(constraints) { 7 | // Generate random string of given length from a 8 | // set of predefined charachters 9 | let min = Math.ceil(Number(constraints["size"].split("__")[0])); 10 | let max = Math.floor(Number(constraints["size"].split("__")[1])); 11 | 12 | // random number between range Max and Min inclusive 13 | let length = Math.floor(Math.random() * (max + 1 - min)) + min; 14 | 15 | let result = ""; 16 | let characters = 17 | "ABCD\nE FGHIJKL MNOPQRST UVWXY\n\tZabcdefg hijklmnopq rstuvwxyz0\t123456789"; 18 | let charactersLength = characters.length; 19 | 20 | for (let i = 0; i < length; i++) { 21 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 22 | } 23 | 24 | return result; 25 | } 26 | 27 | function getRandomImage(constraints) { 28 | // use lorem picsum api to get random images respecting given constarints 29 | let minWidth = Math.ceil( 30 | Number(constraints["size"].split("__")[0].split("x")[0]) 31 | ); 32 | let minHeight = Math.ceil( 33 | Number(constraints["size"].split("__")[0].split("x")[1]) 34 | ); 35 | let maxWidth = Math.ceil( 36 | Number(constraints["size"].split("__")[1].split("x")[0]) 37 | ); 38 | let maxHeight = Math.ceil( 39 | Number(constraints["size"].split("__")[1].split("x")[1]) 40 | ); 41 | let randomWidth = 42 | Math.floor(Math.random() * (maxWidth + 1 - minWidth)) + minWidth; 43 | let randomHeight = 44 | Math.floor(Math.random() * (maxHeight + 1 - minHeight)) + minHeight; 45 | return "https://picsum.photos/" + randomWidth + "/" + randomHeight; 46 | } 47 | 48 | function getConstraints(className, kind) { 49 | return className 50 | .split(" ") 51 | .filter((x) => x.startsWith("hm-") && x != kind) 52 | .reduce((acc, c) => { 53 | acc[c.split("-")[1]] = c.split("-")[2]; 54 | return acc; 55 | }, {}); 56 | } 57 | 58 | function generateRandomList(min, max, list, childType, childClassName, childInnerHTML) { 59 | 60 | // get a random number of childs respecting the given constraints 61 | let length = Math.floor(Math.random() * (max + 1 - min)) + min; 62 | console.log(length); 63 | 64 | //clear all childrens 65 | list.innerText = ""; 66 | 67 | // append childs to the parent list 68 | for (let i = 0; i < length; i++) { 69 | child = document.createElement(childType); 70 | child.className = childClassName; 71 | child.innerHTML = childInnerHTML; 72 | list.appendChild(child); 73 | } 74 | } 75 | 76 | classList.forEach(async function (kind) { 77 | document.querySelectorAll("." + kind).forEach(async function (el) { 78 | let constraints = getConstraints(el.className, kind); 79 | 80 | // Generate a random text / image source / list that respects the constraints but explores the 81 | // boundary conditions 82 | switch (kind) { 83 | case "hm-text": 84 | // Generate random text 85 | setInterval(function () { 86 | el.innerText = generateRandomString(constraints); 87 | }, INTERVAL_TIME); 88 | break; 89 | 90 | case "hm-img": 91 | // replace image with random images respecting the constraints 92 | setInterval(function () { 93 | el.src = getRandomImage(constraints); 94 | }, INTERVAL_TIME); 95 | break; 96 | 97 | case "hm-list": 98 | // get size constraints of the list 99 | let min = Math.ceil(Number(constraints["size"].split("__")[0])); 100 | let max = Math.floor(Number(constraints["size"].split("__")[1])); 101 | 102 | // const { list, listClone } = await clonenode(".hm-list"); 103 | const list = document.querySelector(".hm-list"); 104 | const listClone = list.cloneNode(true); 105 | const childInnerHTML = listClone.children[0].innerHTML; 106 | const childType = listClone.children[0].nodeName.toLowerCase(); 107 | const childClassName = listClone.children[0].className; 108 | 109 | //generate random list 110 | setInterval(() => { 111 | generateRandomList(min, max, list, childType, childClassName, childInnerHTML); 112 | }, INTERVAL_TIME); 113 | 114 | break; 115 | 116 | default: 117 | console.log("Unknown HTML Mocker kind: " + kind); 118 | } 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML-Mocker Demo 6 | 7 | 8 | 9 |
10 |

HTML Mocker Demo

11 |

This script periodically randomizes the contents of your HTML tags to make it easy to test if your layouts will remain responsive 12 | with unexpected dynamic data. It can randomize text length, # of words in a text node, width and height of images via Unsplash, 13 | number of children elements (with recursion) and more. 14 |

15 |

The idea is to detect bugs during development and without having to do manual testing for various boundary conditions of data. 16 | This is a work in progress. You can file issues and send PRs on the GitHub repo.

17 | 18 |

random heading 😀

19 | 20 |
21 |

random heading 😀

22 |
23 |
24 |
25 |

random heading 😀

26 |
27 | 28 |
29 | random image 30 |
31 | 32 | 38 | 39 |
40 |
    41 |
  1. random image
  2. 42 |
  3. random image
  4. 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /multipleCards.css: -------------------------------------------------------------------------------- 1 | /* layout relevant styles */ 2 | html, body { 3 | width: 100%; 4 | height: 100%; 5 | margin: 0; 6 | } 7 | 8 | body { 9 | display: flex; 10 | flex-direction: row; 11 | align-items: center; 12 | } 13 | body main { 14 | display: flex; 15 | flex-direction: column; 16 | flex-grow: 1; 17 | align-items: center; 18 | } 19 | body .row { 20 | display: flex; 21 | flex-direction: row; 22 | flex-wrap: wrap; 23 | margin: 0 25%; 24 | } 25 | 26 | .card { 27 | display: flex; 28 | flex-basis: 20%; 29 | flex-grow: 1; 30 | } 31 | .card p { 32 | flex-grow: 1; 33 | } 34 | .card .cta { 35 | align-self: center; 36 | } 37 | 38 | /* other styles */ 39 | .card { 40 | flex-direction: column; 41 | padding: 10px; 42 | color: #fff; 43 | border-radius: 20px; 44 | box-sizing: border-box; 45 | margin: 10px; 46 | font-family: Roboto, sans-serif; 47 | } 48 | .card.orange { 49 | background: #fbb040; 50 | background: linear-gradient(135deg, #fbb040 20%, #ef4136 100%); 51 | } 52 | .card.blue { 53 | background: #00aeef; 54 | background: linear-gradient(135deg, #00aeef 20%, #2d388a 100%); 55 | } 56 | .card h1 { 57 | margin: 0; 58 | font-size: 1.2rem; 59 | font-weight: lighter; 60 | text-align: center; 61 | } 62 | .card p { 63 | font-size: 0.8rem; 64 | } 65 | .card .cta { 66 | width: 100px; 67 | height: 25px; 68 | border-radius: 13px; 69 | border: 1px solid #fff; 70 | line-height: 25px; 71 | text-align: center; 72 | font-size: 0.8rem; 73 | color: #fff; 74 | text-decoration: none; 75 | margin-top: 10px; 76 | position: relative; 77 | box-sizing: border-box; 78 | } 79 | .card .cta:hover { 80 | background: #ffffff; 81 | color: #000; 82 | } 83 | .card .cta:hover::after { 84 | right: 5px; 85 | } 86 | .card .cta::after { 87 | content: ">"; 88 | font-family: "Jua"; 89 | position: absolute; 90 | right: 8px; 91 | transition: right 1s ease; 92 | } 93 | 94 | a, a:hover { 95 | transition: all 0.3s ease; 96 | } 97 | -------------------------------------------------------------------------------- /multipleCards.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HTML MOCKER DEMO 5 | 6 | 7 | 8 | 9 |
10 |
11 |
12 |

Headline #1

13 |

14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam 15 | vulputate est non neque faucibus elementum. 16 |

17 | CTA #1 18 |
19 |
20 |

Headline #2

21 |

22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis 23 | sollicitudin sagittis luctus. Duis id lacinia massa. Vestibulum diam 24 | purus, tempor. 25 |

26 | CTA #2 27 |
28 |
29 |

Headline #3

30 |

31 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis 32 | sollicitudin sagittis luctus. Aliquam erat volutpat. Vestibulum diam 33 | purus, tempor. 34 |

35 | CTA #3 36 |
37 |
38 |

Headline #4

39 |

40 | Lorem ipsum dolor sit amet. Duis sollicitudin sagittis luctus. 41 | Aliquam erat volutpat. Duis id lacinia massa. Vestibulum diam purus, 42 | tempor. 43 |

44 | CTA #4 45 |
46 |
47 |

Headline #5

48 |

49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam 50 | vulputate est non neque faucibus elementum. 51 |

52 | CTA #5 53 |
54 |
55 |

Headline #6

56 |

57 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis 58 | sollicitudin sagittis luctus. Vestibulum diam purus, tempor. 59 |

60 | CTA #6 61 |
62 |
63 |

Headline #7

64 |

65 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis 66 | sollicitudin sagittis luctus. Aliquam erat volutpat. 67 |

68 | CTA #7 69 |
70 |
71 |

Headline #8

72 |

73 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis 74 | sollicitudin sagittis luctus. Aliquam erat volutpat. Duis id lacinia 75 | massa. Vestibulum diam purus, tempor. Lorem ipsum dolor sit amet, 76 | consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur 77 | adipiscing elit. 78 |

79 | CTA #8 80 |
81 |
82 |
83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /stylesheet.css: -------------------------------------------------------------------------------- 1 | .hm-text { 2 | color: darkred; 3 | } 4 | 5 | --------------------------------------------------------------------------------