Created
September 1, 2026 17:24
-
-
Save sandro-pasquali/e4826381f84026f6eb71984572978648 to your computer and use it in GitHub Desktop.
Responsive CSS layouts derived from an ascii representation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Title</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 10px; | |
| } | |
| .bulldog-layout { | |
| display: grid; | |
| grid-template-rows: var(--grid-rows); | |
| grid-template-columns: var(--grid-cols); | |
| grid-row-gap: 5px; | |
| grid-column-gap: 5px; | |
| max-width: 1000px; | |
| height: 100vh; | |
| margin: 0 auto; | |
| } | |
| .bulldog-layout div { | |
| grid-area: var(--row) / var(--col) / span var(--height) / span var(--width); | |
| background-color: var(--bg-color); | |
| text-align: center; | |
| line-height: calc(var(--height) / var(--totalRows) * 100vh); | |
| color: #860ef5; | |
| border: 1px black solid; | |
| } | |
| @media (max-width: 600px) { | |
| .bulldog-layout { | |
| grid-template-columns: 1fr; | |
| } | |
| .bulldog-layout div { | |
| grid-area: auto / auto / auto / auto; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="testblock" class="bulldog-layout"></div> | |
| <script> | |
| // Inspired by: https://github.com/jlogix/text2css-grid | |
| // | |
| const design =` | |
| +---+---+ | |
| | | | | |
| +---+---+ | |
| | | | | |
| +-+-+-+-+ | |
| | | | | | | |
| +-+-+-+-+ | |
| | | | |
| +---+---+ | |
| `; | |
| const lines = design.split(/[\r\n]/) // Split layout into individual lines. | |
| .map(line => line.match(/\+/) ? // '+' means one or more boxes is closing. | |
| line.split(/[\+\|]/) | |
| .filter(entry => entry) // Filter out zero-length lines. | |
| .map(box => box.match(/^\-+$/) ? // All '-'s mean box is closing here. | |
| -(box.length + 1) : // Indicate close with negative number. | |
| box.length + 1) : // Box still open, use positive length. | |
| line.split(/\|/) // If '+' not found split at '|'. | |
| .filter(entry => entry) // Filter out zero-length lines. | |
| .map(box => box.length + 1)) // Add box width to the array. | |
| .filter(entry => entry.length); // Filter out empty arrays. | |
| const backgroundColors = ["#f7a7a7", "#d0f7a7", "#f7d6a7", "#a7e2f7", "#e7bafb", "#fbbadc", "#def5d1", "#f7f1a7", "#b5fdd6", "#fb9387"]; | |
| let currentRow = 0; | |
| const boxes = []; | |
| // 1. Calculate the start column of each box. | |
| // 2. For each row in the array and for each number in the row: | |
| // If the number is positive: | |
| // If an open box already exists at this column offset: | |
| // Increment the row count of the open box at the column offset. | |
| // Else (i.e. no open box found at the column offset): | |
| // Create a new box at this row and column offsets. | |
| // Else (i.e. number is negative): | |
| // Increment the row count of the box at the current column offset and close the box. | |
| // | |
| lines.filter(entry => entry) // Filter out zero length lines | |
| .forEach(line => { | |
| // Starting columns of boxes. | |
| // | |
| const startColumns = line.map((value, index, array) => array.filter((_, filterIndex) => filterIndex < index).reduce((sum, num) => sum + Math.abs(num), 1)); | |
| line.forEach((value, index) => { | |
| console.log(value, index); | |
| // Any open boxes at this column offset? | |
| // | |
| const openBoxes = boxes.filter(box => box.col === startColumns[index] && box.isOpen); | |
| if (value > 0) { // A positive number means... | |
| if (openBoxes.length) { | |
| openBoxes.forEach(box => box.height++); // ...add one more row to open boxes at this column offset | |
| } else { // No open boxes exist at this column offset. start a new box. | |
| const newBox = { | |
| row: currentRow, | |
| col: startColumns[index], | |
| width: value, | |
| height: 1, | |
| color: backgroundColors[boxes.length % backgroundColors.length], | |
| isOpen: true | |
| }; | |
| boxes.push(newBox); | |
| } | |
| } else { | |
| // Negative number means close open boxes at this column offset. | |
| // | |
| openBoxes.forEach(box => { | |
| box.height++; | |
| box.isOpen = false; | |
| }); | |
| } | |
| }); | |
| currentRow++; | |
| }); | |
| const totalRows = lines.length - 1; | |
| const totalCols = lines[0].reduce((sum, element) => sum + Math.abs(element), 0); | |
| const layoutContainer = document.getElementById('testblock'); | |
| layoutContainer.style.setProperty('--grid-rows', `repeat(${totalRows}, 1fr)`); | |
| layoutContainer.style.setProperty('--grid-cols', `repeat(${totalCols}, 1fr)`); | |
| layoutContainer.style.setProperty('--totalRows', totalRows); | |
| boxes.forEach((box, index) => { | |
| const boxDiv = document.createElement('div'); | |
| boxDiv.textContent = String(index + 1); | |
| boxDiv.style.setProperty('--row', box.row); | |
| boxDiv.style.setProperty('--col', box.col); | |
| boxDiv.style.setProperty('--width', box.width); | |
| boxDiv.style.setProperty('--height', box.height); | |
| boxDiv.style.setProperty('--bg-color', box.color); | |
| layoutContainer.appendChild(boxDiv); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment