├── img ├── screenshot.png └── grid-to-flex.jpg ├── .gitignore ├── README.md ├── js ├── app-new-min.js ├── app.js └── app-new.js ├── styles ├── app.scss ├── reset.scss └── app.css └── index.html /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadeed/grid-to-flex/HEAD/img/screenshot.png -------------------------------------------------------------------------------- /img/grid-to-flex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadeed/grid-to-flex/HEAD/img/grid-to-flex.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.codekit3 2 | README.html 3 | js/app-min.js 4 | js/app-new-min.js 5 | .idea/ 6 | js/untitled.xml 7 | build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grid to flex 2 | 3 | ![""](img/screenshot.png) 4 | 5 | CSS Grid minmax function is used to dynamically make a responsive layout with a specificed item width. On the left side, you can control the minimum width and spacing between grid items. 6 | 7 | Check it out: [shadeed.github.io/grid-to-flex/](http://shadeed.github.io/grid-to-flex/) 8 | 9 | [Codepen Demo](https://codepen.io/shadeed/pen/XWrLmYe) 10 | 11 | Then, you can edit or add Flexbox breakpoints. Once that is done, click on Generate SCSS to get the ready to use Sass code. 12 | 13 | ## Generated Code 14 | The code generated is a Sass mixin that can be reused in your project. 15 | 16 | ## Suggestions 17 | Do you have a comment or suggestion? Please open an issue. -------------------------------------------------------------------------------- /js/app-new-min.js: -------------------------------------------------------------------------------- 1 | class gridToFlex{constructor(){this.init()}init(){this.initValues(),this.cacheDOM(),this.assignDefaults(),this.bindEvents(),this.addDefaultBreakpoints(),this.generateGridItems()}cacheDOM(){this.minCol=document.querySelector("#itemWidth"),this.gridColGap=document.querySelector("#gridColGap"),this.gridRowGap=document.querySelector("#gridRowGap"),this.unifyGap=document.querySelector("#unifyGap"),this.gridWrapper=document.querySelector(".js-grid"),this.breakPointsList=document.querySelector(".flex-breakpoints-list"),this.addBreakpointBtn=document.querySelector("#addBreakpoint"),this.generateCSS=document.querySelector("#generateCSS"),this.resultModal=document.querySelector("#resultModal"),this.modalBody=document.querySelector("#modalBody"),this.resultCode=document.querySelector("#resultCode"),this.copyCSS=document.querySelector("#copyCSS"),this.closeModal=document.querySelector("#close")}initValues(){this.minColWidth=200,this.gridColGapValue=16,this.gridRowGapValue=16,this.isUnify=!1,this.flexBreakpoints=[],this.flexBreakpointsInfo=[]}assignDefaults(){this.minCol.value=this.minColWidth,this.gridColGap.value=this.gridColGapValue,this.gridRowGap.value=this.gridRowGapValue}bindEvents(){this.minCol.addEventListener("input",this.colChange.bind(this)),this.gridColGap.addEventListener("input",this.colGapChange.bind(this)),this.gridRowGap.addEventListener("input",this.rowGapChange.bind(this)),this.addBreakpointBtn.addEventListener("click",this.addBreakpointEvent.bind(this)),this.generateCSS.addEventListener("click",this.generateResult.bind(this)),this.copyCSS.addEventListener("click",this.copyResult.bind(this)),this.closeModal.addEventListener("click",this.closeResult.bind(this))}colChange(){this.minCol.value<0||(this.minColWidth=this.minCol.value,this.generateGridItems())}colGapChange(){this.gridColGapValue=this.gridColGap.value,this.generateGridItems()}rowGapChange(){this.gridRowGapValue=this.gridRowGap.value,this.generateGridItems()}generateGridItems(){if(this.unifyGap.checked?this.isUnify=!0:this.isUnify=!1,this.isUnify){let e=document.activeElement;"gridColGap"==e.getAttribute("id")&&(this.gridRowGapValue=this.gridColGapValue),"gridRowGap"==e.getAttribute("id")&&(this.gridColGapValue=this.gridRowGapValue),this.gridColGap.value=this.gridColGapValue,this.gridRowGap.value=this.gridRowGapValue}let e="display: grid; \ngrid-template-columns: "+`repeat(auto-fit, minmax(${this.minColWidth}px, 1fr));`+"\n"+`grid-gap: ${this.gridRowGapValue}px ${this.gridColGapValue}px;`;document.querySelector("#gridCodePreview").innerHTML=e,this.gridWrapper.style.gridTemplateColumns=`repeat(auto-fit, minmax(${this.minColWidth}px, 1fr))`,this.gridWrapper.style.gridColumnGap=`${this.gridColGapValue}px`,this.gridWrapper.style.gridRowGap=`${this.gridRowGapValue}px`}addBreakpointEvent(e){e.preventDefault();let t=this.addBreakpoint();this.breakPointsList.appendChild(t)}addBreakpoint(e=0){let t=e>0?e:this.breakPointsList.children.length,i=document.createElement("div");i.classList.add("flex-breakpoints-item");let r=document.createElement("button");r.innerHTML="remove",r.setAttribute("aria-label","Remove Breakpoint"),r.addEventListener("click",this.deleteBreakpoint.bind(this));let n=document.createElement("h3");n.innerHTML=`Breakpoint ${t+1}`;let a=document.createElement("div");a.classList.add("o-grid--2");let s=document.createElement("div"),l=document.createElement("label");l.classList.add("c-label"),l.setAttribute("for",`fromWidth-${t+1}`),l.innerHTML="Min Width";let o=document.createElement("input");o.classList.add("c-input"),o.setAttribute("type","number"),o.setAttribute("id",`fromWidth-${t+1}`),o.setAttribute("placeholder","e.g: 500px"),o.setAttribute("required","");let d=document.createElement("div"),p=document.createElement("label");p.classList.add("c-label"),p.setAttribute("for",`itemsToShow-${t+1}`),p.innerHTML="Items";let u=document.createElement("input");return u.classList.add("c-input"),u.setAttribute("type","number"),u.setAttribute("id",`itemsToShow-${t+1}`),u.setAttribute("placeholder","e.g: 3"),u.setAttribute("required",""),s.appendChild(l),s.appendChild(o),d.appendChild(p),d.appendChild(u),a.appendChild(s),a.appendChild(d),i.appendChild(r),i.appendChild(n),i.appendChild(a),this.flexBreakpointsInfo.push({firstInput:o.getAttribute("id"),secondInput:u.getAttribute("id")}),this.flexBreakpointsInfo.length>0&&this.generateCSS.removeAttribute("disabled"),i}renameFlexbreakpointsInfo(e){this.flexBreakpointsInfo=[],console.log(e);for(let t=0;t0?this.generateCSS.removeAttribute("disabled"):this.generateCSS.setAttribute("disabled","");for(let i=0;i div:first-child label"),r=t[i].querySelector(".o-grid--2 > div:first-child input");e.setAttribute("for",`fromWidth-${i+1}`),r.setAttribute("id",`fromWidth-${i+1}`);let n=t[i].querySelector(".o-grid--2 > div:last-child label"),a=t[i].querySelector(".o-grid--2 > div:last-child input");n.setAttribute("for",`itemsToShow-${i+1}`),a.setAttribute("id",`itemsToShow-${i+1}`)}}getBreakpoints(){this.flexBreakpoints=[];for(var e=0;e * {\n margin-bottom: ${this.gridRowGapValue}px;\n margin-left: ${this.gridColGapValue}px;\n }\n `;for(var r=0;r * {\n width: calc((99%/ #{${this.flexBreakpoints[r].numOfItems}}) - ${this.gridColGapValue}px);\n flex: 0 0 calc((99% / #{${this.flexBreakpoints[r].numOfItems}}) - ${this.gridColGapValue}px);\n }\n }\n `;t.push(n)}var a=`\n @supports(grid-area: auto) {\n grid-template-columns: repeat(auto-fit, minmax(${this.minColWidth}px, 1fr));\n margin-left: 0;\n\n > * {\n width: auto;\n margin-left: 0;\n margin-bottom: 0;\n }\n }\n\n }`;let s=i+"\n"+t.join("\n")+"\n"+a;this.resultCode.innerHTML=s,this.resultModal.classList.add("is-active");let l=this.resultModal.querySelectorAll("a[href]:not([disabled]), button:not([disabled])"),o=l[0],d=l[l.length-1];this.resultModal.addEventListener("keydown",function(e){"Tab"!==e.key&&9!==e.keyCode||(e.shiftKey?document.activeElement===o&&(d.focus(),e.preventDefault()):document.activeElement===d&&(o.focus(),e.preventDefault()))})}copyResult(e){e.preventDefault(),this.resultCode.select(),document.execCommand("copy")}closeResult(e){e.preventDefault(),this.resultModal.classList.remove("is-active")}}new gridToFlex; -------------------------------------------------------------------------------- /styles/app.scss: -------------------------------------------------------------------------------- 1 | @import "reset.scss"; 2 | 3 | $color-primary: #3863D9; 4 | 5 | html { 6 | height: 100%; 7 | box-sizing: border-box; 8 | } 9 | 10 | *, *:before, *:after { 11 | box-sizing: inherit; 12 | } 13 | 14 | body { 15 | display: flex; 16 | flex-direction: column; 17 | height: 100%; 18 | font-family: 'IBM Plex Sans', sans-serif; 19 | } 20 | 21 | h2 { 22 | font-size: 1.5rem; 23 | font-weight: bold; 24 | color: $color-primary; 25 | margin-bottom: 2rem; 26 | } 27 | 28 | h3 { 29 | font-size: 1.25rem; 30 | margin-bottom: 0.5rem; 31 | } 32 | 33 | button:focus, 34 | input[type="checkbox"]:focus { 35 | outline: dashed 3px grey; 36 | outline-offset: 4px; 37 | } 38 | 39 | .c-form__wrapper { 40 | display: grid; 41 | grid-template-columns: 1fr 1fr; 42 | grid-gap: 8px; 43 | margin-bottom: 8px; 44 | 45 | @media (min-width: 600px) { 46 | grid-template-columns: 2fr 1fr 1fr; 47 | } 48 | } 49 | 50 | .c-input:not([type="checkbox"]) { 51 | display: block; 52 | width: 100%; 53 | font-size: 16px; 54 | color: $color-primary; 55 | padding: 0.65rem 0.5rem; 56 | border: 1px solid #afafaf; 57 | border-radius: 5px; 58 | transition: 0.2s ease-out; 59 | 60 | &:focus { 61 | border-color: $color-primary; 62 | box-shadow: 0 3px 10px 0 rgba($color-primary, 0.25); 63 | } 64 | } 65 | 66 | .c-label { 67 | display: block; 68 | margin-bottom: 0.5rem; 69 | } 70 | 71 | form { 72 | > p { 73 | margin-bottom: 1.5rem; 74 | } 75 | } 76 | 77 | .c-button { 78 | appearance: none; 79 | display: block; 80 | width: 100%; 81 | font-size: 16px; 82 | background: $color-primary; 83 | color: #fff; 84 | border: 0; 85 | padding: 0.65rem 0.5rem 0.8rem; 86 | border-radius: 4px; 87 | transition: 0.2s ease-out; 88 | 89 | &:hover { 90 | background: darken($color-primary, 5%); 91 | transform: translateY(-2px); 92 | box-shadow: 0 5px 10px 0 rgba(#000, 0.4); 93 | } 94 | } 95 | 96 | .c-button--with-icon { 97 | svg { 98 | display: inline-block; 99 | vertical-align: middle; 100 | width: 18px; 101 | height: 18px; 102 | fill: #fff; 103 | margin-right: 4px; 104 | } 105 | } 106 | 107 | .c-button[disabled] { 108 | opacity: 0.5; 109 | cursor: not-allowed; 110 | } 111 | 112 | .c-button--secondary { 113 | background: transparent; 114 | color: #000; 115 | border: 2px solid $color-primary; 116 | 117 | &:hover { 118 | color: #fff; 119 | border-color: darken($color-primary, 10%); 120 | } 121 | } 122 | 123 | .c-header { 124 | position: relative; 125 | display: grid; 126 | grid-gap: 1rem; 127 | color: #fff; 128 | background: $color-primary; 129 | padding: 1rem; 130 | box-shadow: 0 3px 30px 0 rgba(#000, 0.4); 131 | 132 | a { 133 | color: inherit; 134 | text-decoration: none; 135 | } 136 | 137 | a:last-child { 138 | margin-left: 1rem; 139 | } 140 | 141 | a:hover { 142 | text-decoration: underline; 143 | } 144 | 145 | @media (min-width: 1000px) { 146 | display: flex; 147 | justify-content: space-between; 148 | } 149 | } 150 | 151 | .c-page-title { 152 | font-size: 1.5rem; 153 | font-weight: bold; 154 | //margin-bottom: 0.5rem; 155 | } 156 | 157 | .wrapper { 158 | flex-grow: 1; 159 | 160 | 161 | @media (min-width: 1000px) { 162 | display: grid; 163 | grid-template-columns: 320px 1fr; 164 | } 165 | } 166 | 167 | .o-grid--2 { 168 | display: grid; 169 | grid-template-columns: 1fr 1fr; 170 | grid-gap: 1rem; 171 | } 172 | 173 | .c-sidebar { 174 | position: relative; 175 | z-index: 1; 176 | background: #fff; 177 | padding: 1rem; 178 | box-shadow: 3px 0 30px 0 rgba(#000, 0.1); 179 | } 180 | 181 | .c-main { 182 | padding: 1rem; 183 | //color: #fff; 184 | background: #eaeaea; 185 | } 186 | 187 | .js-grid { 188 | width: 95%; 189 | margin: 1rem auto; 190 | display: grid; 191 | grid-template-columns: 1fr 1fr 1fr; 192 | //grid-gap: 1rem; 193 | 194 | @media (min-width: 1700px) { 195 | max-width: 1100px; 196 | } 197 | } 198 | 199 | .js-grid-item { 200 | background: rgba(#000, 0.15); 201 | border: 2px dashed #707070; 202 | border-radius: 5px; 203 | min-height: 160px; 204 | } 205 | 206 | .s-content { 207 | max-width: 95%; 208 | margin: 0 auto 1rem; 209 | 210 | h3 { 211 | font-weight: bold; 212 | } 213 | 214 | p { 215 | max-width: 800px; 216 | line-height: 1.45; 217 | } 218 | 219 | svg { 220 | //fill: #fff; 221 | display: inline-block; 222 | vertical-align: middle; 223 | width: 24px; 224 | height: 24px; 225 | } 226 | } 227 | 228 | #gridCodePreview { 229 | max-width: 95%; 230 | margin: 0 auto; 231 | line-height: 1.6; 232 | color: #fff; 233 | background: #292929; 234 | border-radius: 5px; 235 | padding: 0.65rem; 236 | overflow-x: scroll; 237 | 238 | span { 239 | background: $color-primary; 240 | color: #fff; 241 | opacity: 0; 242 | padding: 3px 5px; 243 | border-radius: 5px; 244 | animation: showFull 0.5s linear forwards; 245 | } 246 | } 247 | 248 | @keyframes showFull { 249 | to { 250 | opacity: 1; 251 | } 252 | } 253 | 254 | /* Flexbox Breapoints */ 255 | .flex-breakpoints-item { 256 | position: relative; 257 | margin-bottom: 1.5rem; 258 | 259 | h3 { 260 | padding-left: 28px; 261 | } 262 | 263 | button { 264 | appearance: none; 265 | position: absolute; 266 | left: 0; 267 | top: 0; 268 | width: 20px; 269 | height: 20px; 270 | background: #bf3636 url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='100pt' height='100pt' version='1.1' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill ='%23fff' d='m32.699 66.699c0.54688 0.57422 1.3086 0.90234 2.1016 0.90234 0.77344-0.003906 1.5195-0.28906 2.0977-0.80078l13-12.602 12.602 13c0.49219 0.66016 1.2773 1.0352 2.1016 1 0.79297 0 1.5508-0.32422 2.0977-0.89844 1.1719-1.1406 1.2188-3.0078 0.10156-4.1992l-12.602-13 13-12.602c0.58984-0.53516 0.93359-1.2852 0.94922-2.0781 0.019531-0.79297-0.28516-1.5625-0.84766-2.1211-1.1406-1.1719-3.0078-1.2188-4.1992-0.10156l-13 12.602-12.602-13c-0.53516-0.58984-1.2852-0.93359-2.0781-0.94922-0.79297-0.019531-1.5625 0.28516-2.1211 0.84766-1.1719 1.1406-1.2188 3.0078-0.10156 4.1992l12.602 13-13 12.602c-0.58984 0.53516-0.93359 1.2852-0.94922 2.0781-0.019531 0.79297 0.28516 1.5625 0.84766 2.1211z'/%3E%3C/svg%3E%0A"); 271 | background-size: 20px; 272 | background-position: center; 273 | background-repeat: no-repeat; 274 | color: #fff; 275 | border: 0; 276 | font-size: 0; 277 | border-radius: 50%; 278 | cursor: pointer; 279 | } 280 | } 281 | 282 | .add-breakpoint { 283 | appearance: none; 284 | position: absolute; 285 | right: 0; 286 | top: 0; 287 | width: 30px; 288 | height: 30px; 289 | background: $color-primary url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='100pt' height='100pt' version='1.1' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='m75 43.75h-18.75v-18.75c0-3.75-2.5-6.25-6.25-6.25s-6.25 2.5-6.25 6.25v18.75h-18.75c-3.75 0-6.25 2.5-6.25 6.25s2.5 6.25 6.25 6.25h18.75v18.75c0 3.75 2.5 6.25 6.25 6.25s6.25-2.5 6.25-6.25v-18.75h18.75c3.75 0 6.25-2.5 6.25-6.25s-2.5-6.25-6.25-6.25z'/%3E%3C/svg%3E%0A"); 290 | background-size: 28px; 291 | background-position: center; 292 | background-repeat: no-repeat; 293 | color: #fff; 294 | border: 0; 295 | font-size: 0; 296 | border-radius: 50%; 297 | cursor: pointer; 298 | transition: 0.2s ease-out; 299 | 300 | &:hover { 301 | background-color: darken($color-primary, 10%); 302 | transform: scale(1.1); 303 | box-shadow: 0 3px 10px 0 rgba(#000, 0.25); 304 | } 305 | } 306 | 307 | // Modal 308 | .c-modal { 309 | position: fixed; 310 | left: 0; 311 | top: 0; 312 | right: 0; 313 | bottom: 0; 314 | z-index: 10; 315 | background: rgba(#000, 0.5); 316 | opacity: 0; 317 | animation: opacity 0.5s ease-out forwards; 318 | display: none; 319 | 320 | &.is-active { 321 | display: block; 322 | } 323 | } 324 | 325 | .c-modal__content { 326 | position: absolute; 327 | top: 4rem; 328 | left: 50%; 329 | transform: translateX(-50%) translateY(30px); 330 | width: 550px; 331 | height: 400px; 332 | padding: 1rem; 333 | background: #fff; 334 | border-radius: 5px; 335 | animation: transform 0.3s 0.025s ease-out forwards; 336 | 337 | @media (max-width: 700px) { 338 | max-width: 85%; 339 | } 340 | 341 | textarea { 342 | appearance: none; 343 | user-select: none; 344 | border: 0; 345 | background: transparent; 346 | width: 100%; 347 | height: 90%; 348 | margin-top: 10px; 349 | } 350 | } 351 | 352 | .c-modal__close { 353 | appearance: none; 354 | position: absolute; 355 | left: -20px; 356 | top: -20px; 357 | width: 40px; 358 | height: 40px; 359 | font-size: 0; 360 | border: 0; 361 | background: #fff; 362 | border-radius: 50%; 363 | box-shadow: 0 3px 10px 0 rgba(#000, 0.3); 364 | 365 | svg { 366 | width: 24px; 367 | height: 24px; 368 | } 369 | } 370 | 371 | @keyframes opacity { 372 | to { 373 | opacity: 1; 374 | } 375 | } 376 | 377 | @keyframes transform { 378 | to { 379 | transform: translateX(-50%) translateY(0); 380 | } 381 | } -------------------------------------------------------------------------------- /styles/reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0-modified | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | 27 | /* make sure to set some focus styles for accessibility */ 28 | :focus { 29 | outline: 0; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | body { 39 | line-height: 1; 40 | } 41 | 42 | ol, ul { 43 | list-style: none; 44 | } 45 | 46 | blockquote, q { 47 | quotes: none; 48 | } 49 | 50 | blockquote:before, blockquote:after, 51 | q:before, q:after { 52 | content: ''; 53 | content: none; 54 | } 55 | 56 | table { 57 | border-collapse: collapse; 58 | border-spacing: 0; 59 | } 60 | 61 | input[type=search]::-webkit-search-cancel-button, 62 | input[type=search]::-webkit-search-decoration, 63 | input[type=search]::-webkit-search-results-button, 64 | input[type=search]::-webkit-search-results-decoration { 65 | -webkit-appearance: none; 66 | -moz-appearance: none; 67 | } 68 | 69 | input[type=search] { 70 | -webkit-appearance: none; 71 | -moz-appearance: none; 72 | -webkit-box-sizing: content-box; 73 | -moz-box-sizing: content-box; 74 | box-sizing: content-box; 75 | } 76 | 77 | input::-webkit-outer-spin-button, 78 | input::-webkit-inner-spin-button { 79 | /* display: none; <- Crashes Chrome on hover */ 80 | -webkit-appearance: none; 81 | margin: 0; /* <-- Apparently some margin are still there even though it's hidden */ 82 | } 83 | 84 | input[type=number] { 85 | -moz-appearance:textfield; /* Firefox */ 86 | } 87 | 88 | textarea { 89 | overflow: auto; 90 | vertical-align: top; 91 | resize: vertical; 92 | } 93 | 94 | /** 95 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 96 | */ 97 | 98 | audio, 99 | canvas, 100 | video { 101 | display: inline-block; 102 | *display: inline; 103 | *zoom: 1; 104 | max-width: 100%; 105 | } 106 | 107 | /** 108 | * Prevent modern browsers from displaying `audio` without controls. 109 | * Remove excess height in iOS 5 devices. 110 | */ 111 | 112 | audio:not([controls]) { 113 | display: none; 114 | height: 0; 115 | } 116 | 117 | /** 118 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 119 | * Known issue: no IE 6 support. 120 | */ 121 | 122 | [hidden] { 123 | display: none; 124 | } 125 | 126 | /** 127 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 128 | * `em` units. 129 | * 2. Prevent iOS text size adjust after orientation change, without disabling 130 | * user zoom. 131 | */ 132 | 133 | html { 134 | font-size: 100%; /* 1 */ 135 | -webkit-text-size-adjust: 100%; /* 2 */ 136 | -ms-text-size-adjust: 100%; /* 2 */ 137 | } 138 | 139 | /** 140 | * Address `outline` inconsistency between Chrome and other browsers. 141 | */ 142 | 143 | a:focus { 144 | outline: thin dotted; 145 | } 146 | 147 | /** 148 | * Improve readability when focused and also mouse hovered in all browsers. 149 | */ 150 | 151 | a:active, 152 | a:hover { 153 | outline: 0; 154 | } 155 | 156 | /** 157 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 158 | * 2. Improve image quality when scaled in IE 7. 159 | */ 160 | 161 | img { 162 | border: 0; /* 1 */ 163 | -ms-interpolation-mode: bicubic; /* 2 */ 164 | } 165 | 166 | /** 167 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 168 | */ 169 | 170 | figure { 171 | margin: 0; 172 | } 173 | 174 | /** 175 | * Correct margin displayed oddly in IE 6/7. 176 | */ 177 | 178 | form { 179 | margin: 0; 180 | } 181 | 182 | /** 183 | * Define consistent border, margin, and padding. 184 | */ 185 | 186 | fieldset { 187 | border: 1px solid #c0c0c0; 188 | margin: 0 2px; 189 | padding: 0.35em 0.625em 0.75em; 190 | } 191 | 192 | /** 193 | * 1. Correct color not being inherited in IE 6/7/8/9. 194 | * 2. Correct text not wrapping in Firefox 3. 195 | * 3. Correct alignment displayed oddly in IE 6/7. 196 | */ 197 | 198 | legend { 199 | border: 0; /* 1 */ 200 | padding: 0; 201 | white-space: normal; /* 2 */ 202 | *margin-left: -7px; /* 3 */ 203 | } 204 | 205 | /** 206 | * 1. Correct font size not being inherited in all browsers. 207 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 208 | * and Chrome. 209 | * 3. Improve appearance and consistency in all browsers. 210 | */ 211 | 212 | button, 213 | input, 214 | select, 215 | textarea { 216 | font-size: 100%; /* 1 */ 217 | margin: 0; /* 2 */ 218 | vertical-align: baseline; /* 3 */ 219 | *vertical-align: middle; /* 3 */ 220 | } 221 | 222 | /** 223 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 224 | * the UA stylesheet. 225 | */ 226 | 227 | button, 228 | input { 229 | line-height: normal; 230 | } 231 | 232 | /** 233 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 234 | * All other form control elements do not inherit `text-transform` values. 235 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 236 | * Correct `select` style inheritance in Firefox 4+ and Opera. 237 | */ 238 | 239 | button, 240 | select { 241 | text-transform: none; 242 | } 243 | 244 | /** 245 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 246 | * and `video` controls. 247 | * 2. Correct inability to style clickable `input` types in iOS. 248 | * 3. Improve usability and consistency of cursor style between image-type 249 | * `input` and others. 250 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 251 | * Known issue: inner spacing remains in IE 6. 252 | */ 253 | 254 | button, 255 | html input[type="button"], /* 1 */ 256 | input[type="reset"], 257 | input[type="submit"] { 258 | -webkit-appearance: button; /* 2 */ 259 | cursor: pointer; /* 3 */ 260 | *overflow: visible; /* 4 */ 261 | } 262 | 263 | /** 264 | * Re-set default cursor for disabled elements. 265 | */ 266 | 267 | button[disabled], 268 | html input[disabled] { 269 | cursor: default; 270 | } 271 | 272 | /** 273 | * 1. Address box sizing set to content-box in IE 8/9. 274 | * 2. Remove excess padding in IE 8/9. 275 | * 3. Remove excess padding in IE 7. 276 | * Known issue: excess padding remains in IE 6. 277 | */ 278 | 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | box-sizing: border-box; /* 1 */ 282 | padding: 0; /* 2 */ 283 | *height: 13px; /* 3 */ 284 | *width: 13px; /* 3 */ 285 | } 286 | 287 | /** 288 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 289 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 290 | * (include `-moz` to future-proof). 291 | */ 292 | 293 | input[type="search"] { 294 | -webkit-appearance: textfield; /* 1 */ 295 | -moz-box-sizing: content-box; 296 | -webkit-box-sizing: content-box; /* 2 */ 297 | box-sizing: content-box; 298 | } 299 | 300 | /** 301 | * Remove inner padding and search cancel button in Safari 5 and Chrome 302 | * on OS X. 303 | */ 304 | 305 | input[type="search"]::-webkit-search-cancel-button, 306 | input[type="search"]::-webkit-search-decoration { 307 | -webkit-appearance: none; 308 | } 309 | 310 | /** 311 | * Remove inner padding and border in Firefox 3+. 312 | */ 313 | 314 | button::-moz-focus-inner, 315 | input::-moz-focus-inner { 316 | border: 0; 317 | padding: 0; 318 | } 319 | 320 | /** 321 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 322 | * 2. Improve readability and alignment in all browsers. 323 | */ 324 | 325 | textarea { 326 | overflow: auto; /* 1 */ 327 | vertical-align: top; /* 2 */ 328 | } 329 | 330 | /** 331 | * Remove most spacing between table cells. 332 | */ 333 | 334 | table { 335 | border-collapse: collapse; 336 | border-spacing: 0; 337 | } 338 | 339 | html, 340 | button, 341 | input, 342 | select, 343 | textarea { 344 | color: #222; 345 | } 346 | 347 | 348 | ::-moz-selection { 349 | background: #b3d4fc; 350 | text-shadow: none; 351 | } 352 | 353 | ::selection { 354 | background: #b3d4fc; 355 | text-shadow: none; 356 | } 357 | 358 | img { 359 | vertical-align: middle; 360 | } 361 | 362 | fieldset { 363 | border: 0; 364 | margin: 0; 365 | padding: 0; 366 | } 367 | 368 | textarea { 369 | resize: vertical; 370 | } 371 | 372 | .chromeframe { 373 | margin: 0.2em 0; 374 | background: #ccc; 375 | color: #000; 376 | padding: 0.2em 0; 377 | } 378 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | • Grid to Flexbox 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |

Grid to Flex

31 |
32 | 33 |
34 |

By @shadeed9Github

35 |
36 |
37 | 38 |
39 | 98 |
99 |
100 |

101 | 102 | 103 | 104 | How it works 105 |

106 |

CSS Grid minmax function is used to dynamically make a responsive layout with a specificed item width. On the left side, you can control the minimum width and spacing between grid items.
Then, you can edit or add Flexbox breakpoints. Once that is done, click on Generate SCSS to get the ready to use Sass code. See usage Example

107 |
108 | 109 |
110 |                 display: grid;
111 |                 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
112 |                 grid-gap: 16px;
113 |             
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | 131 |
132 |
133 | 138 | 144 |

145 |             
146 |         
147 |
148 | 149 | 150 | 151 | 152 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | var minCol = document.querySelector('#itemWidth'); 2 | var gridColGap = document.querySelector('#gridColGap'); 3 | var gridRowGap = document.querySelector('#gridRowGap'); 4 | var unifyGap = document.querySelector('#unifyGap'); 5 | var gridWrapper = document.querySelector('.js-grid'); 6 | 7 | var breakPointsList = document.querySelector('.flex-breakpoints-list'); 8 | var addBreakpointBtn = document.querySelector('#addBreakpoint'); 9 | var generateCSS = document.querySelector('#generateCSS'); 10 | 11 | var minColWidth = 200; 12 | var gridColGapValue = 16; 13 | var gridRowGapValue = 16; 14 | 15 | var isUnify = false; 16 | 17 | // Flexbox breakpoints 18 | var flexBreakpoints = []; 19 | 20 | var flexBreakpointsInfo = []; 21 | 22 | // Add three breakpoints by default 23 | for(var i = 0; i < 3; i++) { 24 | var breakpoint = addBreakpoint(i); 25 | //console.log(breakpoint); 26 | var breakpointTitle = breakpoint.querySelector('h3'); 27 | var inputs = breakpoint.querySelectorAll('input'); 28 | 29 | breakPointsList.appendChild(breakpoint); 30 | 31 | if(i === 0) { 32 | breakpointTitle.innerHTML = "Small"; 33 | inputs[0].value = "320"; 34 | inputs[1].value = "2"; 35 | } 36 | if(i === 1) { 37 | breakpointTitle.innerHTML = "Medium"; 38 | inputs[0].value = "768"; 39 | inputs[1].value = "3"; 40 | } 41 | if(i === 2) { 42 | breakpointTitle.innerHTML = "Large"; 43 | inputs[0].value = "1024"; 44 | inputs[1].value = "4"; 45 | } 46 | } 47 | 48 | // Asign default value for inputs 49 | minCol.value = minColWidth; 50 | gridColGap.value = gridColGapValue; 51 | gridRowGap.value = gridRowGapValue; 52 | 53 | minCol.addEventListener('input', function () { 54 | if(this.value < 0) { 55 | return; 56 | } 57 | minColWidth = this.value; 58 | generateGridItems(); 59 | }); 60 | 61 | gridColGap.addEventListener('input', function () { 62 | gridColGapValue = this.value; 63 | generateGridItems(); 64 | }); 65 | 66 | gridRowGap.addEventListener('input', function () { 67 | gridRowGapValue = this.value; 68 | generateGridItems(); 69 | }); 70 | 71 | unifyGap.addEventListener('input', function () { 72 | if (this.checked) { 73 | gridColGapValue = 16; 74 | gridRowGapValue = 16; 75 | 76 | gridColGap.value = gridColGapValue; 77 | gridRowGap.value = gridRowGapValue; 78 | 79 | isUnify = true; 80 | 81 | generateGridItems(); 82 | } else { 83 | isUnify = false; 84 | } 85 | }); 86 | 87 | addBreakpointBtn.addEventListener('click', function (e) { 88 | e.preventDefault(); 89 | var breakpoint = addBreakpoint(); 90 | breakPointsList.appendChild(breakpoint); 91 | }); 92 | 93 | generateCSS.addEventListener('click', function (e) { 94 | e.preventDefault(); 95 | getBreakpoints(); 96 | 97 | //console.log("Flex Breakpoints" + flexBreakpoints); 98 | 99 | //console.log("Flex Breakpoints IDs" + flexBreakpointsInfo); 100 | 101 | var breakPointsList = []; 102 | 103 | var result1 = `@mixin grid() { 104 | display: flex; 105 | flex-wrap: wrap; 106 | 107 | @supports(grid-area: auto) { 108 | display: grid; 109 | grid-gap: ${gridColGapValue}px ${gridRowGapValue}px; 110 | } 111 | } 112 | @mixin gridAuto() { 113 | margin-left: -${gridColGapValue}px; 114 | 115 | > * { 116 | margin-bottom: ${gridRowGapValue}px; 117 | margin-left: ${gridColGapValue}px; 118 | } 119 | `; 120 | 121 | 122 | for(var i = 0; i < flexBreakpoints.length; i++) { 123 | if(flexBreakpoints[i].breakpointFrom != "" && flexBreakpoints[i].numOfItems != "") { 124 | var result2 = ` 125 | @media (min-width: ${flexBreakpoints[i].breakpointFrom}px) { 126 | > * { 127 | width: calc((99%/ #{${flexBreakpoints[i].numOfItems}}) - ${gridColGapValue}px); 128 | flex: 0 0 calc((99% / #{${flexBreakpoints[i].numOfItems}}) - ${gridColGapValue}px); 129 | } 130 | } 131 | `; 132 | 133 | breakPointsList.push(result2); 134 | } 135 | } 136 | 137 | var grid = ` 138 | @supports(grid-area: auto) { 139 | grid-template-columns: repeat(auto-fit, minmax(${minColWidth}px, 1fr)); 140 | margin-left: 0; 141 | 142 | > * { 143 | width: auto; 144 | margin-left: 0; 145 | margin-bottom: 0; 146 | } 147 | } 148 | 149 | }`; 150 | 151 | var resultModal = document.querySelector('#resultModal'); 152 | var modalBody = document.querySelector('#modalBody'); 153 | var resultCode = document.querySelector('#resultCode'); 154 | var copyCSS = document.querySelector('#copyCSS'); 155 | var closeModal = document.querySelector('#close'); 156 | 157 | var code = result1 + "\n" + breakPointsList.join("\n") + "\n" + grid; 158 | 159 | resultCode.innerHTML = code; 160 | 161 | resultModal.classList.add('is-active'); 162 | 163 | copyCSS.addEventListener('click', function(e){ 164 | e.preventDefault(); 165 | resultCode.select(); 166 | document.execCommand("copy"); 167 | }); 168 | 169 | closeModal.addEventListener('click', function(e){ 170 | e.preventDefault(); 171 | resultModal.classList.remove('is-active'); 172 | }); 173 | 174 | //console.log(result1 + "\n" + breakPointsList + "\n" + grid); 175 | }); 176 | 177 | generateGridItems(); 178 | 179 | /********************** Functions ***********************/ 180 | function generateGridItems() { 181 | if(unifyGap.checked) { 182 | isUnify = true; 183 | } else { 184 | isUnify = false; 185 | } 186 | 187 | if(isUnify) { 188 | // Get the current focused element 189 | var currentActiveElem = document.activeElement; 190 | 191 | if(currentActiveElem.getAttribute('id') == 'gridColGap') { 192 | gridRowGapValue = gridColGapValue; 193 | } 194 | 195 | if(currentActiveElem.getAttribute('id') == 'gridRowGap') { 196 | gridColGapValue = gridRowGapValue; 197 | } 198 | 199 | gridColGap.value = gridColGapValue; 200 | gridRowGap.value = gridRowGapValue; 201 | } 202 | 203 | var prevCode = "display: grid; \n" + "grid-template-columns: "+ 204 | `repeat(auto-fit, minmax(${minColWidth}px, 1fr));` + "\n" + `grid-gap: ${gridRowGapValue}px ${gridColGapValue}px;`; 205 | 206 | document.querySelector('#gridCodePreview').innerHTML = prevCode; 207 | 208 | gridWrapper.style.gridTemplateColumns = `repeat(auto-fit, minmax(${minColWidth}px, 1fr))`; 209 | gridWrapper.style.gridColumnGap = `${gridColGapValue}px`; 210 | gridWrapper.style.gridRowGap = `${gridRowGapValue}px`; 211 | } 212 | 213 | function addBreakpoint(defaultLength = 0) { 214 | var listLength = (defaultLength > 0) ? defaultLength : breakPointsList.children.length; 215 | 216 | var mainDiv = document.createElement('div'); 217 | mainDiv.classList.add('flex-breakpoints-item'); 218 | 219 | var deleteBtn = document.createElement('button'); 220 | deleteBtn.innerHTML = "remove"; 221 | deleteBtn.setAttribute('aria-label', 'Remove Breakpoint'); 222 | deleteBtn.addEventListener('click', function(e){ 223 | e.preventDefault(); 224 | e.target.parentNode.parentNode.removeChild(e.target.parentNode); 225 | 226 | // Update the number, IDs of the breakpoints 227 | updateBreakpoints(); 228 | 229 | var parentElm = e.target.parentNode; 230 | var parentLabel = parentElm.querySelector('.c-label'); 231 | var labelFor = parentLabel.getAttribute('for'); 232 | var index = parseInt(labelFor.replace(/[^0-9]/g, ''), 10); 233 | 234 | flexBreakpointsInfo.splice(index-1, 1); 235 | 236 | // Rename the array IDs to match the inputs 237 | renameFlexbreakpointsInfo(flexBreakpointsInfo.length); 238 | 239 | console.log(flexBreakpointsInfo); 240 | }); 241 | 242 | function renameFlexbreakpointsInfo(arrayLength) { 243 | flexBreakpointsInfo = []; 244 | 245 | console.log(arrayLength); 246 | 247 | for (var i = 0; i < arrayLength; i++) { 248 | flexBreakpointsInfo.push({ 249 | firstInput: `fromWidth-${i+1}`, 250 | secondInput: `itemsToShow-${i+1}` 251 | }); 252 | } 253 | } 254 | 255 | var bpTitle = document.createElement('h3'); 256 | bpTitle.innerHTML = `Breakpoint ${listLength+1}`; 257 | 258 | var gridDiv = document.createElement('div'); 259 | gridDiv.classList.add('o-grid--2'); 260 | 261 | var firstInputDiv = document.createElement('div'); 262 | 263 | var firstInputLabel = document.createElement('label'); 264 | firstInputLabel.classList.add('c-label'); 265 | firstInputLabel.setAttribute('for', `fromWidth-${listLength+1}`); 266 | firstInputLabel.innerHTML = "Min Width"; 267 | 268 | var firstInput = document.createElement('input'); 269 | firstInput.classList.add('c-input'); 270 | firstInput.setAttribute('type', 'number'); 271 | firstInput.setAttribute('id', `fromWidth-${listLength+1}`); 272 | firstInput.setAttribute('placeholder', 'e.g: 500px'); 273 | firstInput.setAttribute('required', ''); 274 | 275 | var secondInputDiv = document.createElement('div'); 276 | 277 | var secondInputLabel = document.createElement('label'); 278 | secondInputLabel.classList.add('c-label'); 279 | secondInputLabel.setAttribute('for', `itemsToShow-${listLength+1}`); 280 | secondInputLabel.innerHTML = "Items"; 281 | 282 | var secondInput = document.createElement('input'); 283 | secondInput.classList.add('c-input'); 284 | secondInput.setAttribute('type', 'number'); 285 | secondInput.setAttribute('id', `itemsToShow-${listLength+1}`); 286 | secondInput.setAttribute('placeholder', 'e.g: 3'); 287 | secondInput.setAttribute('required', ''); 288 | 289 | firstInputDiv.appendChild(firstInputLabel); 290 | firstInputDiv.appendChild(firstInput); 291 | 292 | secondInputDiv.appendChild(secondInputLabel); 293 | secondInputDiv.appendChild(secondInput); 294 | 295 | gridDiv.appendChild(firstInputDiv); 296 | gridDiv.appendChild(secondInputDiv); 297 | 298 | mainDiv.appendChild(deleteBtn); 299 | mainDiv.appendChild(bpTitle); 300 | mainDiv.appendChild(gridDiv); 301 | 302 | flexBreakpointsInfo.push({ 303 | firstInput: firstInput.getAttribute('id'), 304 | secondInput: secondInput.getAttribute('id') 305 | }); 306 | 307 | if(flexBreakpointsInfo.length > 0) { 308 | generateCSS.removeAttribute('disabled'); 309 | } 310 | 311 | return mainDiv; 312 | } 313 | 314 | function updateBreakpoints() { 315 | var listLength = breakPointsList.children.length; 316 | var breakpointsItem = document.querySelectorAll('.flex-breakpoints-item'); 317 | 318 | if(listLength > 0) { 319 | generateCSS.removeAttribute('disabled'); 320 | } else { 321 | generateCSS.setAttribute('disabled', ''); 322 | } 323 | 324 | for(var i = 0; i < listLength; i++) { 325 | var breakpointsTitle = breakpointsItem[i].querySelector('h3'); 326 | breakpointsTitle.innerHTML = `Breakpoint ${i+1}`; 327 | 328 | var breakpointsFirstLabel = breakpointsItem[i].querySelector('.o-grid--2 > div:first-child label'); 329 | var breakpointsFirstInput = breakpointsItem[i].querySelector('.o-grid--2 > div:first-child input'); 330 | 331 | breakpointsFirstLabel.setAttribute("for", `fromWidth-${i+1}`); 332 | breakpointsFirstInput.setAttribute("id", `fromWidth-${i+1}`); 333 | 334 | var breakpointsSecondLabel = breakpointsItem[i].querySelector('.o-grid--2 > div:last-child label'); 335 | var breakpointsSecondInput = breakpointsItem[i].querySelector('.o-grid--2 > div:last-child input'); 336 | 337 | breakpointsSecondLabel.setAttribute("for", `itemsToShow-${i+1}`); 338 | breakpointsSecondInput.setAttribute("id", `itemsToShow-${i+1}`); 339 | } 340 | } 341 | 342 | function getBreakpoints() { 343 | // Emptying the array to avoid making it larger in case generateCSS clicked multiple times. 344 | flexBreakpoints = []; 345 | 346 | for(var i = 0; i < flexBreakpointsInfo.length; i++) { 347 | var fromWidthID = flexBreakpointsInfo[i].firstInput; 348 | var numOfItemsID = flexBreakpointsInfo[i].secondInput; 349 | 350 | //console.log(fromWidthID); 351 | //console.log(numOfItemsID); 352 | 353 | var fromWidthValue = document.querySelector("#"+fromWidthID).value; 354 | var numOfItemsValue = document.querySelector("#"+numOfItemsID).value; 355 | 356 | flexBreakpoints.push({ 357 | breakpointFrom: fromWidthValue, 358 | numOfItems: numOfItemsValue 359 | }); 360 | } 361 | } -------------------------------------------------------------------------------- /js/app-new.js: -------------------------------------------------------------------------------- 1 | class gridToFlex { 2 | constructor() { 3 | this.init(); 4 | } 5 | 6 | init() { 7 | this.initValues(); 8 | this.cacheDOM(); 9 | this.assignDefaults(); 10 | this.bindEvents(); 11 | this.addDefaultBreakpoints(); 12 | this.generateGridItems(); 13 | } 14 | 15 | cacheDOM() { 16 | this.minCol = document.querySelector('#itemWidth'); 17 | this.gridColGap = document.querySelector('#gridColGap'); 18 | this.gridRowGap = document.querySelector('#gridRowGap'); 19 | this.unifyGap = document.querySelector('#unifyGap'); 20 | this.gridWrapper = document.querySelector('.js-grid'); 21 | this.breakPointsList = document.querySelector('.flex-breakpoints-list'); 22 | this.addBreakpointBtn = document.querySelector('#addBreakpoint'); 23 | this.generateCSS = document.querySelector('#generateCSS'); 24 | this.resultModal = document.querySelector('#resultModal'); 25 | this.modalBody = document.querySelector('#modalBody'); 26 | this.resultCode = document.querySelector('#resultCode'); 27 | this.copyCSS = document.querySelector('#copyCSS'); 28 | this.closeModal = document.querySelector('#close'); 29 | } 30 | 31 | initValues() { 32 | this.minColWidth = 200; 33 | this.gridColGapValue = 16; 34 | this.gridRowGapValue = 16; 35 | this.isUnify = false; 36 | this.flexBreakpoints = []; 37 | this.flexBreakpointsInfo = []; 38 | } 39 | 40 | assignDefaults() { 41 | this.minCol.value = this.minColWidth; 42 | this.gridColGap.value = this.gridColGapValue; 43 | this.gridRowGap.value = this.gridRowGapValue; 44 | } 45 | 46 | bindEvents() { 47 | this.minCol.addEventListener('input', this.colChange.bind(this)); 48 | this.gridColGap.addEventListener('input', this.colGapChange.bind(this)); 49 | this.gridRowGap.addEventListener('input', this.rowGapChange.bind(this)); 50 | this.addBreakpointBtn.addEventListener('click', this.addBreakpointEvent.bind(this)); 51 | this.generateCSS.addEventListener('click', this.generateResult.bind(this)); 52 | this.copyCSS.addEventListener('click', this.copyResult.bind(this)); 53 | this.closeModal.addEventListener('click', this.closeResult.bind(this)); 54 | } 55 | 56 | colChange() { 57 | if(this.minCol.value < 0) { 58 | return; 59 | } 60 | this.minColWidth = this.minCol.value; 61 | this.generateGridItems(); 62 | } 63 | 64 | colGapChange() { 65 | this.gridColGapValue = this.gridColGap.value; 66 | this.generateGridItems(); 67 | } 68 | 69 | rowGapChange() { 70 | this.gridRowGapValue = this.gridRowGap.value; 71 | this.generateGridItems(); 72 | } 73 | 74 | generateGridItems() { 75 | if(this.unifyGap.checked) { 76 | this.isUnify = true; 77 | } else { 78 | this.isUnify = false; 79 | } 80 | 81 | if(this.isUnify) { 82 | let currentActiveElem = document.activeElement; 83 | 84 | if(currentActiveElem.getAttribute('id') == 'gridColGap') { 85 | this.gridRowGapValue = this.gridColGapValue; 86 | } 87 | 88 | if(currentActiveElem.getAttribute('id') == 'gridRowGap') { 89 | this.gridColGapValue = this.gridRowGapValue; 90 | } 91 | 92 | this.gridColGap.value = this.gridColGapValue; 93 | this.gridRowGap.value = this.gridRowGapValue; 94 | } 95 | 96 | let prevCode = "display: grid; \n" + "grid-template-columns: "+ 97 | `repeat(auto-fit, minmax(${this.minColWidth}px, 1fr));` + "\n" + `grid-gap: ${this.gridRowGapValue}px ${this.gridColGapValue}px;`; 98 | 99 | document.querySelector('#gridCodePreview').innerHTML = prevCode; 100 | 101 | this.gridWrapper.style.gridTemplateColumns = `repeat(auto-fit, minmax(${this.minColWidth}px, 1fr))`; 102 | this.gridWrapper.style.gridColumnGap = `${this.gridColGapValue}px`; 103 | this.gridWrapper.style.gridRowGap = `${this.gridRowGapValue}px`; 104 | } 105 | 106 | addBreakpointEvent(e) { 107 | e.preventDefault(); 108 | let breakpoint = this.addBreakpoint(); 109 | this.breakPointsList.appendChild(breakpoint); 110 | } 111 | 112 | addBreakpoint(defaultLength = 0) { 113 | let listLength = (defaultLength > 0) ? defaultLength : this.breakPointsList.children.length; 114 | 115 | let mainDiv = document.createElement('div'); 116 | mainDiv.classList.add('flex-breakpoints-item'); 117 | 118 | let deleteBtn = document.createElement('button'); 119 | deleteBtn.innerHTML = "remove"; 120 | deleteBtn.setAttribute('aria-label', 'Remove Breakpoint'); 121 | deleteBtn.addEventListener('click', this.deleteBreakpoint.bind(this)); 122 | 123 | let bpTitle = document.createElement('h3'); 124 | bpTitle.innerHTML = `Breakpoint ${listLength+1}`; 125 | 126 | let gridDiv = document.createElement('div'); 127 | gridDiv.classList.add('o-grid--2'); 128 | 129 | let firstInputDiv = document.createElement('div'); 130 | 131 | let firstInputLabel = document.createElement('label'); 132 | firstInputLabel.classList.add('c-label'); 133 | firstInputLabel.setAttribute('for', `fromWidth-${listLength+1}`); 134 | firstInputLabel.innerHTML = "Min Width"; 135 | 136 | let firstInput = document.createElement('input'); 137 | firstInput.classList.add('c-input'); 138 | firstInput.setAttribute('type', 'number'); 139 | firstInput.setAttribute('id', `fromWidth-${listLength+1}`); 140 | firstInput.setAttribute('placeholder', 'e.g: 500px'); 141 | firstInput.setAttribute('required', ''); 142 | 143 | let secondInputDiv = document.createElement('div'); 144 | 145 | let secondInputLabel = document.createElement('label'); 146 | secondInputLabel.classList.add('c-label'); 147 | secondInputLabel.setAttribute('for', `itemsToShow-${listLength+1}`); 148 | secondInputLabel.innerHTML = "Items"; 149 | 150 | let secondInput = document.createElement('input'); 151 | secondInput.classList.add('c-input'); 152 | secondInput.setAttribute('type', 'number'); 153 | secondInput.setAttribute('id', `itemsToShow-${listLength+1}`); 154 | secondInput.setAttribute('placeholder', 'e.g: 3'); 155 | secondInput.setAttribute('required', ''); 156 | 157 | firstInputDiv.appendChild(firstInputLabel); 158 | firstInputDiv.appendChild(firstInput); 159 | 160 | secondInputDiv.appendChild(secondInputLabel); 161 | secondInputDiv.appendChild(secondInput); 162 | 163 | gridDiv.appendChild(firstInputDiv); 164 | gridDiv.appendChild(secondInputDiv); 165 | 166 | mainDiv.appendChild(deleteBtn); 167 | mainDiv.appendChild(bpTitle); 168 | mainDiv.appendChild(gridDiv); 169 | 170 | this.flexBreakpointsInfo.push({ 171 | firstInput: firstInput.getAttribute('id'), 172 | secondInput: secondInput.getAttribute('id') 173 | }); 174 | 175 | if(this.flexBreakpointsInfo.length > 0) { 176 | this.generateCSS.removeAttribute('disabled'); 177 | } 178 | 179 | return mainDiv; 180 | } 181 | 182 | renameFlexbreakpointsInfo(arrayLength) { 183 | this.flexBreakpointsInfo = []; 184 | 185 | console.log(arrayLength); 186 | 187 | for (let i = 0; i < arrayLength; i++) { 188 | this.flexBreakpointsInfo.push({ 189 | firstInput: `fromWidth-${i+1}`, 190 | secondInput: `itemsToShow-${i+1}` 191 | }); 192 | } 193 | } 194 | 195 | updateBreakpoints() { 196 | let listLength = this.breakPointsList.children.length; 197 | let breakpointsItem = document.querySelectorAll('.flex-breakpoints-item'); 198 | 199 | if(listLength > 0) { 200 | this.generateCSS.removeAttribute('disabled'); 201 | } else { 202 | this.generateCSS.setAttribute('disabled', ''); 203 | } 204 | 205 | for(let i = 0; i < listLength; i++) { 206 | let breakpointsTitle = breakpointsItem[i].querySelector('h3'); 207 | breakpointsTitle.innerHTML = `Breakpoint ${i+1}`; 208 | 209 | let breakpointsFirstLabel = breakpointsItem[i].querySelector('.o-grid--2 > div:first-child label'); 210 | let breakpointsFirstInput = breakpointsItem[i].querySelector('.o-grid--2 > div:first-child input'); 211 | 212 | breakpointsFirstLabel.setAttribute("for", `fromWidth-${i+1}`); 213 | breakpointsFirstInput.setAttribute("id", `fromWidth-${i+1}`); 214 | 215 | let breakpointsSecondLabel = breakpointsItem[i].querySelector('.o-grid--2 > div:last-child label'); 216 | let breakpointsSecondInput = breakpointsItem[i].querySelector('.o-grid--2 > div:last-child input'); 217 | 218 | breakpointsSecondLabel.setAttribute("for", `itemsToShow-${i+1}`); 219 | breakpointsSecondInput.setAttribute("id", `itemsToShow-${i+1}`); 220 | } 221 | } 222 | 223 | getBreakpoints() { 224 | // Emptying the array to avoid making it larger in case generateCSS clicked multiple times. 225 | this.flexBreakpoints = []; 226 | 227 | for(var i = 0; i < this.flexBreakpointsInfo.length; i++) { 228 | var fromWidthID = this.flexBreakpointsInfo[i].firstInput; 229 | var numOfItemsID = this.flexBreakpointsInfo[i].secondInput; 230 | 231 | //console.log(fromWidthID); 232 | //console.log(numOfItemsID); 233 | 234 | var fromWidthValue = document.querySelector("#"+fromWidthID).value; 235 | var numOfItemsValue = document.querySelector("#"+numOfItemsID).value; 236 | 237 | this.flexBreakpoints.push({ 238 | breakpointFrom: fromWidthValue, 239 | numOfItems: numOfItemsValue 240 | }); 241 | } 242 | } 243 | 244 | deleteBreakpoint(e) { 245 | e.preventDefault(); 246 | e.target.parentNode.parentNode.removeChild(e.target.parentNode); 247 | 248 | // Update the number, IDs of the breakpoints 249 | this.updateBreakpoints(); 250 | 251 | let parentElm = e.target.parentNode; 252 | let parentLabel = parentElm.querySelector('.c-label'); 253 | let labelFor = parentLabel.getAttribute('for'); 254 | let index = parseInt(labelFor.replace(/[^0-9]/g, ''), 10); 255 | 256 | this.flexBreakpointsInfo.splice(index-1, 1); 257 | 258 | // Rename the array IDs to match the inputs 259 | this.renameFlexbreakpointsInfo(this.flexBreakpointsInfo.length); 260 | 261 | console.log(this.flexBreakpointsInfo); 262 | } 263 | 264 | addDefaultBreakpoints() { 265 | // Add three breakpoints by default 266 | for(var i = 0; i < 3; i++) { 267 | var breakpoint = this.addBreakpoint(i); 268 | //console.log(breakpoint); 269 | var breakpointTitle = breakpoint.querySelector('h3'); 270 | var inputs = breakpoint.querySelectorAll('input'); 271 | 272 | this.breakPointsList.appendChild(breakpoint); 273 | 274 | if(i === 0) { 275 | breakpointTitle.innerHTML = "Small"; 276 | inputs[0].value = "320"; 277 | inputs[1].value = "2"; 278 | } 279 | if(i === 1) { 280 | breakpointTitle.innerHTML = "Medium"; 281 | inputs[0].value = "768"; 282 | inputs[1].value = "3"; 283 | } 284 | if(i === 2) { 285 | breakpointTitle.innerHTML = "Large"; 286 | inputs[0].value = "1024"; 287 | inputs[1].value = "4"; 288 | } 289 | } 290 | } 291 | 292 | generateResult(e) { 293 | e.preventDefault(); 294 | this.getBreakpoints(); 295 | 296 | let breakPointsList = []; 297 | 298 | let result1 = `@mixin grid() { 299 | display: flex; 300 | flex-wrap: wrap; 301 | 302 | @supports(grid-area: auto) { 303 | display: grid; 304 | grid-gap: ${this.gridColGapValue}px ${this.gridRowGapValue}px; 305 | } 306 | } 307 | @mixin gridAuto() { 308 | margin-left: -${this.gridColGapValue}px; 309 | 310 | > * { 311 | margin-bottom: ${this.gridRowGapValue}px; 312 | margin-left: ${this.gridColGapValue}px; 313 | } 314 | `; 315 | 316 | 317 | for(var i = 0; i < this.flexBreakpoints.length; i++) { 318 | if(this.flexBreakpoints[i].breakpointFrom != "" && this.flexBreakpoints[i].numOfItems != "") { 319 | var result2 = ` 320 | @media (min-width: ${this.flexBreakpoints[i].breakpointFrom}px) { 321 | > * { 322 | width: calc((99%/ #{${this.flexBreakpoints[i].numOfItems}}) - ${this.gridColGapValue}px); 323 | flex: 0 0 calc((99% / #{${this.flexBreakpoints[i].numOfItems}}) - ${this.gridColGapValue}px); 324 | } 325 | } 326 | `; 327 | 328 | breakPointsList.push(result2); 329 | } 330 | } 331 | 332 | var grid = ` 333 | @supports(grid-area: auto) { 334 | grid-template-columns: repeat(auto-fit, minmax(${this.minColWidth}px, 1fr)); 335 | margin-left: 0; 336 | 337 | > * { 338 | width: auto; 339 | margin-left: 0; 340 | margin-bottom: 0; 341 | } 342 | } 343 | 344 | }`; 345 | 346 | let code = result1 + "\n" + breakPointsList.join("\n") + "\n" + grid; 347 | 348 | this.resultCode.innerHTML = code; 349 | 350 | this.resultModal.classList.add('is-active'); 351 | 352 | let focusableElms = this.resultModal.querySelectorAll('a[href]:not([disabled]), button:not([disabled])'); 353 | 354 | let firstActiveElm = focusableElms[0]; 355 | let lastActiveElm = focusableElms[focusableElms.length - 1]; 356 | 357 | this.resultModal.addEventListener('keydown', function(e) { 358 | if (e.key === 'Tab' || e.keyCode === 9) { 359 | if ( e.shiftKey ) { 360 | if (document.activeElement === firstActiveElm) { 361 | lastActiveElm.focus(); 362 | e.preventDefault(); 363 | } 364 | } else { 365 | if (document.activeElement === lastActiveElm) { 366 | firstActiveElm.focus(); 367 | e.preventDefault(); 368 | } 369 | } 370 | } 371 | }); 372 | } 373 | 374 | copyResult(e) { 375 | e.preventDefault(); 376 | this.resultCode.select(); 377 | document.execCommand("copy"); 378 | } 379 | 380 | closeResult(e) { 381 | e.preventDefault(); 382 | this.resultModal.classList.remove('is-active'); 383 | } 384 | } 385 | 386 | new gridToFlex(); -------------------------------------------------------------------------------- /styles/app.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0-modified | 20110126 3 | License: none (public domain) 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, hgroup, 16 | menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video { 18 | margin: 0; 19 | padding: 0; 20 | border: 0; 21 | font-size: 100%; 22 | font: inherit; 23 | vertical-align: baseline; } 24 | 25 | /* make sure to set some focus styles for accessibility */ 26 | :focus { 27 | outline: 0; } 28 | 29 | /* HTML5 display-role reset for older browsers */ 30 | article, aside, details, figcaption, figure, 31 | footer, header, hgroup, menu, nav, section { 32 | display: block; } 33 | 34 | body { 35 | line-height: 1; } 36 | 37 | ol, ul { 38 | list-style: none; } 39 | 40 | blockquote, q { 41 | quotes: none; } 42 | 43 | blockquote:before, blockquote:after, 44 | q:before, q:after { 45 | content: ''; 46 | content: none; } 47 | 48 | table { 49 | border-collapse: collapse; 50 | border-spacing: 0; } 51 | 52 | input[type=search]::-webkit-search-cancel-button, 53 | input[type=search]::-webkit-search-decoration, 54 | input[type=search]::-webkit-search-results-button, 55 | input[type=search]::-webkit-search-results-decoration { 56 | -webkit-appearance: none; 57 | -moz-appearance: none; } 58 | 59 | input[type=search] { 60 | -webkit-appearance: none; 61 | -moz-appearance: none; 62 | -webkit-box-sizing: content-box; 63 | -moz-box-sizing: content-box; 64 | box-sizing: content-box; } 65 | 66 | input::-webkit-outer-spin-button, 67 | input::-webkit-inner-spin-button { 68 | /* display: none; <- Crashes Chrome on hover */ 69 | -webkit-appearance: none; 70 | margin: 0; 71 | /* <-- Apparently some margin are still there even though it's hidden */ } 72 | 73 | input[type=number] { 74 | -moz-appearance: textfield; 75 | /* Firefox */ } 76 | 77 | textarea { 78 | overflow: auto; 79 | vertical-align: top; 80 | resize: vertical; } 81 | 82 | /** 83 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 84 | */ 85 | audio, 86 | canvas, 87 | video { 88 | display: inline-block; 89 | *display: inline; 90 | *zoom: 1; 91 | max-width: 100%; } 92 | 93 | /** 94 | * Prevent modern browsers from displaying `audio` without controls. 95 | * Remove excess height in iOS 5 devices. 96 | */ 97 | audio:not([controls]) { 98 | display: none; 99 | height: 0; } 100 | 101 | /** 102 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 103 | * Known issue: no IE 6 support. 104 | */ 105 | [hidden] { 106 | display: none; } 107 | 108 | /** 109 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 110 | * `em` units. 111 | * 2. Prevent iOS text size adjust after orientation change, without disabling 112 | * user zoom. 113 | */ 114 | html { 115 | font-size: 100%; 116 | /* 1 */ 117 | -webkit-text-size-adjust: 100%; 118 | /* 2 */ 119 | -ms-text-size-adjust: 100%; 120 | /* 2 */ } 121 | 122 | /** 123 | * Address `outline` inconsistency between Chrome and other browsers. 124 | */ 125 | a:focus { 126 | outline: thin dotted; } 127 | 128 | /** 129 | * Improve readability when focused and also mouse hovered in all browsers. 130 | */ 131 | a:active, 132 | a:hover { 133 | outline: 0; } 134 | 135 | /** 136 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 137 | * 2. Improve image quality when scaled in IE 7. 138 | */ 139 | img { 140 | border: 0; 141 | /* 1 */ 142 | -ms-interpolation-mode: bicubic; 143 | /* 2 */ } 144 | 145 | /** 146 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 147 | */ 148 | figure { 149 | margin: 0; } 150 | 151 | /** 152 | * Correct margin displayed oddly in IE 6/7. 153 | */ 154 | form { 155 | margin: 0; } 156 | 157 | /** 158 | * Define consistent border, margin, and padding. 159 | */ 160 | fieldset { 161 | border: 1px solid #c0c0c0; 162 | margin: 0 2px; 163 | padding: 0.35em 0.625em 0.75em; } 164 | 165 | /** 166 | * 1. Correct color not being inherited in IE 6/7/8/9. 167 | * 2. Correct text not wrapping in Firefox 3. 168 | * 3. Correct alignment displayed oddly in IE 6/7. 169 | */ 170 | legend { 171 | border: 0; 172 | /* 1 */ 173 | padding: 0; 174 | white-space: normal; 175 | /* 2 */ 176 | *margin-left: -7px; 177 | /* 3 */ } 178 | 179 | /** 180 | * 1. Correct font size not being inherited in all browsers. 181 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 182 | * and Chrome. 183 | * 3. Improve appearance and consistency in all browsers. 184 | */ 185 | button, 186 | input, 187 | select, 188 | textarea { 189 | font-size: 100%; 190 | /* 1 */ 191 | margin: 0; 192 | /* 2 */ 193 | vertical-align: baseline; 194 | /* 3 */ 195 | *vertical-align: middle; 196 | /* 3 */ } 197 | 198 | /** 199 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 200 | * the UA stylesheet. 201 | */ 202 | button, 203 | input { 204 | line-height: normal; } 205 | 206 | /** 207 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 208 | * All other form control elements do not inherit `text-transform` values. 209 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 210 | * Correct `select` style inheritance in Firefox 4+ and Opera. 211 | */ 212 | button, 213 | select { 214 | text-transform: none; } 215 | 216 | /** 217 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 218 | * and `video` controls. 219 | * 2. Correct inability to style clickable `input` types in iOS. 220 | * 3. Improve usability and consistency of cursor style between image-type 221 | * `input` and others. 222 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 223 | * Known issue: inner spacing remains in IE 6. 224 | */ 225 | button, 226 | html input[type="button"], 227 | input[type="reset"], 228 | input[type="submit"] { 229 | -webkit-appearance: button; 230 | /* 2 */ 231 | cursor: pointer; 232 | /* 3 */ 233 | *overflow: visible; 234 | /* 4 */ } 235 | 236 | /** 237 | * Re-set default cursor for disabled elements. 238 | */ 239 | button[disabled], 240 | html input[disabled] { 241 | cursor: default; } 242 | 243 | /** 244 | * 1. Address box sizing set to content-box in IE 8/9. 245 | * 2. Remove excess padding in IE 8/9. 246 | * 3. Remove excess padding in IE 7. 247 | * Known issue: excess padding remains in IE 6. 248 | */ 249 | input[type="checkbox"], 250 | input[type="radio"] { 251 | box-sizing: border-box; 252 | /* 1 */ 253 | padding: 0; 254 | /* 2 */ 255 | *height: 13px; 256 | /* 3 */ 257 | *width: 13px; 258 | /* 3 */ } 259 | 260 | /** 261 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 262 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 263 | * (include `-moz` to future-proof). 264 | */ 265 | input[type="search"] { 266 | -webkit-appearance: textfield; 267 | /* 1 */ 268 | -moz-box-sizing: content-box; 269 | -webkit-box-sizing: content-box; 270 | /* 2 */ 271 | box-sizing: content-box; } 272 | 273 | /** 274 | * Remove inner padding and search cancel button in Safari 5 and Chrome 275 | * on OS X. 276 | */ 277 | input[type="search"]::-webkit-search-cancel-button, 278 | input[type="search"]::-webkit-search-decoration { 279 | -webkit-appearance: none; } 280 | 281 | /** 282 | * Remove inner padding and border in Firefox 3+. 283 | */ 284 | button::-moz-focus-inner, 285 | input::-moz-focus-inner { 286 | border: 0; 287 | padding: 0; } 288 | 289 | /** 290 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 291 | * 2. Improve readability and alignment in all browsers. 292 | */ 293 | textarea { 294 | overflow: auto; 295 | /* 1 */ 296 | vertical-align: top; 297 | /* 2 */ } 298 | 299 | /** 300 | * Remove most spacing between table cells. 301 | */ 302 | table { 303 | border-collapse: collapse; 304 | border-spacing: 0; } 305 | 306 | html, 307 | button, 308 | input, 309 | select, 310 | textarea { 311 | color: #222; } 312 | 313 | ::-moz-selection { 314 | background: #b3d4fc; 315 | text-shadow: none; } 316 | 317 | ::selection { 318 | background: #b3d4fc; 319 | text-shadow: none; } 320 | 321 | img { 322 | vertical-align: middle; } 323 | 324 | fieldset { 325 | border: 0; 326 | margin: 0; 327 | padding: 0; } 328 | 329 | textarea { 330 | resize: vertical; } 331 | 332 | .chromeframe { 333 | margin: 0.2em 0; 334 | background: #ccc; 335 | color: #000; 336 | padding: 0.2em 0; } 337 | 338 | html { 339 | height: 100%; 340 | box-sizing: border-box; } 341 | 342 | *, *:before, *:after { 343 | box-sizing: inherit; } 344 | 345 | body { 346 | display: flex; 347 | flex-direction: column; 348 | height: 100%; 349 | font-family: 'IBM Plex Sans', sans-serif; } 350 | 351 | h2 { 352 | font-size: 1.5rem; 353 | font-weight: bold; 354 | color: #3863D9; 355 | margin-bottom: 2rem; } 356 | 357 | h3 { 358 | font-size: 1.25rem; 359 | margin-bottom: 0.5rem; } 360 | 361 | button:focus, 362 | input[type="checkbox"]:focus { 363 | outline: dashed 3px grey; 364 | outline-offset: 4px; } 365 | 366 | .c-form__wrapper { 367 | display: grid; 368 | grid-template-columns: 1fr 1fr; 369 | grid-gap: 8px; 370 | margin-bottom: 8px; } 371 | @media (min-width: 600px) { 372 | .c-form__wrapper { 373 | grid-template-columns: 2fr 1fr 1fr; } } 374 | 375 | .c-input:not([type="checkbox"]) { 376 | display: block; 377 | width: 100%; 378 | font-size: 16px; 379 | color: #3863D9; 380 | padding: 0.65rem 0.5rem; 381 | border: 1px solid #afafaf; 382 | border-radius: 5px; 383 | transition: 0.2s ease-out; } 384 | .c-input:not([type="checkbox"]):focus { 385 | border-color: #3863D9; 386 | box-shadow: 0 3px 10px 0 rgba(56, 99, 217, 0.25); } 387 | 388 | .c-label { 389 | display: block; 390 | margin-bottom: 0.5rem; } 391 | 392 | form > p { 393 | margin-bottom: 1.5rem; } 394 | 395 | .c-button { 396 | appearance: none; 397 | display: block; 398 | width: 100%; 399 | font-size: 16px; 400 | background: #3863D9; 401 | color: #fff; 402 | border: 0; 403 | padding: 0.65rem 0.5rem 0.8rem; 404 | border-radius: 4px; 405 | transition: 0.2s ease-out; } 406 | .c-button:hover { 407 | background: #2855d0; 408 | transform: translateY(-2px); 409 | box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.4); } 410 | 411 | .c-button--with-icon svg { 412 | display: inline-block; 413 | vertical-align: middle; 414 | width: 18px; 415 | height: 18px; 416 | fill: #fff; 417 | margin-right: 4px; } 418 | 419 | .c-button[disabled] { 420 | opacity: 0.5; 421 | cursor: not-allowed; } 422 | 423 | .c-button--secondary { 424 | background: transparent; 425 | color: #000; 426 | border: 2px solid #3863D9; } 427 | .c-button--secondary:hover { 428 | color: #fff; 429 | border-color: #244cba; } 430 | 431 | .c-header { 432 | position: relative; 433 | display: grid; 434 | grid-gap: 1rem; 435 | color: #fff; 436 | background: #3863D9; 437 | padding: 1rem; 438 | box-shadow: 0 3px 30px 0 rgba(0, 0, 0, 0.4); } 439 | .c-header a { 440 | color: inherit; 441 | text-decoration: none; } 442 | .c-header a:last-child { 443 | margin-left: 1rem; } 444 | .c-header a:hover { 445 | text-decoration: underline; } 446 | @media (min-width: 1000px) { 447 | .c-header { 448 | display: flex; 449 | justify-content: space-between; } } 450 | 451 | .c-page-title { 452 | font-size: 1.5rem; 453 | font-weight: bold; } 454 | 455 | .wrapper { 456 | flex-grow: 1; } 457 | @media (min-width: 1000px) { 458 | .wrapper { 459 | display: grid; 460 | grid-template-columns: 320px 1fr; } } 461 | 462 | .o-grid--2 { 463 | display: grid; 464 | grid-template-columns: 1fr 1fr; 465 | grid-gap: 1rem; } 466 | 467 | .c-sidebar { 468 | position: relative; 469 | z-index: 1; 470 | background: #fff; 471 | padding: 1rem; 472 | box-shadow: 3px 0 30px 0 rgba(0, 0, 0, 0.1); } 473 | 474 | .c-main { 475 | padding: 1rem; 476 | background: #eaeaea; } 477 | 478 | .js-grid { 479 | width: 95%; 480 | margin: 1rem auto; 481 | display: grid; 482 | grid-template-columns: 1fr 1fr 1fr; } 483 | @media (min-width: 1700px) { 484 | .js-grid { 485 | max-width: 1100px; } } 486 | 487 | .js-grid-item { 488 | background: rgba(0, 0, 0, 0.15); 489 | border: 2px dashed #707070; 490 | border-radius: 5px; 491 | min-height: 160px; } 492 | 493 | .s-content { 494 | max-width: 95%; 495 | margin: 0 auto 1rem; } 496 | .s-content h3 { 497 | font-weight: bold; } 498 | .s-content p { 499 | max-width: 800px; 500 | line-height: 1.45; } 501 | .s-content svg { 502 | display: inline-block; 503 | vertical-align: middle; 504 | width: 24px; 505 | height: 24px; } 506 | 507 | #gridCodePreview { 508 | max-width: 95%; 509 | margin: 0 auto; 510 | line-height: 1.6; 511 | color: #fff; 512 | background: #292929; 513 | border-radius: 5px; 514 | padding: 0.65rem; 515 | overflow-x: scroll; } 516 | #gridCodePreview span { 517 | background: #3863D9; 518 | color: #fff; 519 | opacity: 0; 520 | padding: 3px 5px; 521 | border-radius: 5px; 522 | animation: showFull 0.5s linear forwards; } 523 | 524 | @keyframes showFull { 525 | to { 526 | opacity: 1; } } 527 | 528 | /* Flexbox Breapoints */ 529 | .flex-breakpoints-item { 530 | position: relative; 531 | margin-bottom: 1.5rem; } 532 | .flex-breakpoints-item h3 { 533 | padding-left: 28px; } 534 | .flex-breakpoints-item button { 535 | appearance: none; 536 | position: absolute; 537 | left: 0; 538 | top: 0; 539 | width: 20px; 540 | height: 20px; 541 | background: #bf3636 url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='100pt' height='100pt' version='1.1' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill ='%23fff' d='m32.699 66.699c0.54688 0.57422 1.3086 0.90234 2.1016 0.90234 0.77344-0.003906 1.5195-0.28906 2.0977-0.80078l13-12.602 12.602 13c0.49219 0.66016 1.2773 1.0352 2.1016 1 0.79297 0 1.5508-0.32422 2.0977-0.89844 1.1719-1.1406 1.2188-3.0078 0.10156-4.1992l-12.602-13 13-12.602c0.58984-0.53516 0.93359-1.2852 0.94922-2.0781 0.019531-0.79297-0.28516-1.5625-0.84766-2.1211-1.1406-1.1719-3.0078-1.2188-4.1992-0.10156l-13 12.602-12.602-13c-0.53516-0.58984-1.2852-0.93359-2.0781-0.94922-0.79297-0.019531-1.5625 0.28516-2.1211 0.84766-1.1719 1.1406-1.2188 3.0078-0.10156 4.1992l12.602 13-13 12.602c-0.58984 0.53516-0.93359 1.2852-0.94922 2.0781-0.019531 0.79297 0.28516 1.5625 0.84766 2.1211z'/%3E%3C/svg%3E%0A"); 542 | background-size: 20px; 543 | background-position: center; 544 | background-repeat: no-repeat; 545 | color: #fff; 546 | border: 0; 547 | font-size: 0; 548 | border-radius: 50%; 549 | cursor: pointer; } 550 | 551 | .add-breakpoint { 552 | appearance: none; 553 | position: absolute; 554 | right: 0; 555 | top: 0; 556 | width: 30px; 557 | height: 30px; 558 | background: #3863D9 url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='100pt' height='100pt' version='1.1' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='m75 43.75h-18.75v-18.75c0-3.75-2.5-6.25-6.25-6.25s-6.25 2.5-6.25 6.25v18.75h-18.75c-3.75 0-6.25 2.5-6.25 6.25s2.5 6.25 6.25 6.25h18.75v18.75c0 3.75 2.5 6.25 6.25 6.25s6.25-2.5 6.25-6.25v-18.75h18.75c3.75 0 6.25-2.5 6.25-6.25s-2.5-6.25-6.25-6.25z'/%3E%3C/svg%3E%0A"); 559 | background-size: 28px; 560 | background-position: center; 561 | background-repeat: no-repeat; 562 | color: #fff; 563 | border: 0; 564 | font-size: 0; 565 | border-radius: 50%; 566 | cursor: pointer; 567 | transition: 0.2s ease-out; } 568 | .add-breakpoint:hover { 569 | background-color: #244cba; 570 | transform: scale(1.1); 571 | box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.25); } 572 | 573 | .c-modal { 574 | position: fixed; 575 | left: 0; 576 | top: 0; 577 | right: 0; 578 | bottom: 0; 579 | z-index: 10; 580 | background: rgba(0, 0, 0, 0.5); 581 | opacity: 0; 582 | animation: opacity 0.5s ease-out forwards; 583 | display: none; } 584 | .c-modal.is-active { 585 | display: block; } 586 | 587 | .c-modal__content { 588 | position: absolute; 589 | top: 4rem; 590 | left: 50%; 591 | transform: translateX(-50%) translateY(30px); 592 | width: 550px; 593 | height: 400px; 594 | padding: 1rem; 595 | background: #fff; 596 | border-radius: 5px; 597 | animation: transform 0.3s 0.025s ease-out forwards; } 598 | @media (max-width: 700px) { 599 | .c-modal__content { 600 | max-width: 85%; } } 601 | .c-modal__content textarea { 602 | appearance: none; 603 | user-select: none; 604 | border: 0; 605 | background: transparent; 606 | width: 100%; 607 | height: 90%; 608 | margin-top: 10px; } 609 | 610 | .c-modal__close { 611 | appearance: none; 612 | position: absolute; 613 | left: -20px; 614 | top: -20px; 615 | width: 40px; 616 | height: 40px; 617 | font-size: 0; 618 | border: 0; 619 | background: #fff; 620 | border-radius: 50%; 621 | box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.3); } 622 | .c-modal__close svg { 623 | width: 24px; 624 | height: 24px; } 625 | 626 | @keyframes opacity { 627 | to { 628 | opacity: 1; } } 629 | 630 | @keyframes transform { 631 | to { 632 | transform: translateX(-50%) translateY(0); } } 633 | --------------------------------------------------------------------------------