{ "$schema": "../schemas/code-crispies-module-schema.json", "id": "layouts", "title": "Layouts", "description": "Master modern CSS layout techniques with Flexbox and Grid for responsive, powerful designs.", "difficulty": "intermediate", "lessons": [ { "id": "layouts-1", "title": "Flex Basics", "description": "Learn the core properties of Flexbox to align, distribute space, and order items in a container.", "task": "Set .flex to display: flex, and center its children both horizontally and vertically.", "previewHTML": "
1
2
3
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .flex > div { background: #eceff1; margin: 0.5rem; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Enable and center Flexbox */\n.flex {", "initialCode": "", "codeSuffix": "}", "previewContainer": "preview-area", "concept": { "explanation": "Flexbox is a one-dimensional layout system designed for distributing space and aligning items along a single axis (row or column). When you set display: flex on a container, it establishes two perpendicular axes: the main axis (horizontal by default, left to right) and the cross axis (vertical, top to bottom). The justify-content property controls alignment along the main axis (horizontal centering in this case), while align-items controls alignment along the cross axis (vertical centering). This separation of concerns makes Flexbox perfect for one-dimensional layouts like navigation bars, card rows, or button groups where you need precise control over spacing and alignment.", "diagram": "Flexbox: One-Dimensional Layout System\n\nFlexbox Container with Main & Cross Axes:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n justify-content: center\n (main axis alignment)\n ↓\n ┌─────────────────────────┐\n │ ┌───┐ │ ← align-items: center\n │ ┌───┤ 2 ├───┐ │ (cross axis alignment)\n │ │ 1 └───┘ 3 │ │\n │ └───────────┘ │\n └─────────────────────────┘\n ← main axis →\n\nDefault Flexbox Behavior:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nWithout centering:\ndisplay: flex\n ┌─────────────────────────┐\n │┌───┬───┬───┐ │\n ││ 1 │ 2 │ 3 │ │\n │└───┴───┴───┘ │\n └─────────────────────────┘\n ↑ Items flow left-to-right\n along main axis by default\n\nWith justify-content: center:\n ┌─────────────────────────┐\n │ ┌───┬───┬───┐ │\n │ │ 1 │ 2 │ 3 │ │\n │ └───┴───┴───┘ │\n └─────────────────────────┘\n ↑ Centered horizontally\n\nWith align-items: center:\n ┌─────────────────────────┐\n │ ┌───┬───┬───┐ │\n │ │ 1 │ 2 │ 3 │ │\n │ └───┴───┴───┘ │\n └─────────────────────────┘\n ↑ Centered vertically\n\nMain Axis vs Cross Axis:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nRow direction (default):\n main axis: horizontal →\n cross axis: vertical ↓\n\nColumn direction:\n main axis: vertical ↓\n cross axis: horizontal →", "containerVsItem": "display: flex, justify-content, and align-items are CONTAINER properties set on the parent element. They control the layout and alignment of all child items collectively. Individual items can override cross-axis alignment with align-self." }, "validations": [ { "type": "contains", "value": "display", "message": "Use display: flex", "options": { "caseSensitive": false } }, { "type": "contains", "value": "justify-content", "message": "Use justify-content: center", "options": { "caseSensitive": false } }, { "type": "contains", "value": "align-items", "message": "Use align-items: center", "options": { "caseSensitive": false } } ] }, { "id": "layouts-2", "title": "Flex Advanced", "description": "Control wrapping, ordering, and flexible growth/shrink of items in a flex container.", "task": "Allow items to wrap and set .item to flex: 1 1 100px.", "previewHTML": "
A
B
C
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .item { background: #c5cae9; margin: 0.5rem; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Enable wrap and flexible items */\n.flex {", "initialCode": "", "codeSuffix": "}\n.item { }", "previewContainer": "preview-area", "concept": { "explanation": "The flex shorthand property combines three values: flex-grow (how much an item grows to fill available space), flex-shrink (how much it shrinks when space is limited), and flex-basis (the initial size before growing or shrinking). The syntax flex: 1 1 100px means each item starts at 100px width, can grow to take up extra space (factor of 1), and can shrink below 100px if needed (factor of 1). When combined with flex-wrap: wrap, items that can't fit on one line wrap to the next, creating responsive multi-line layouts without media queries. This makes flex perfect for responsive card grids, tag lists, or any layout that needs to adapt fluidly to container width.", "diagram": "Flex Shorthand: flex-grow flex-shrink flex-basis\n\nSyntax breakdown:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nflex: 1 1 100px\n ↓ ↓ ↓\n grow shrink basis (initial size)\n\nHow flex: 1 1 100px works:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nContainer: 400px wide\n3 items with flex: 1 1 100px\n\nStep 1: Start with basis (100px each)\n┌────────────────────────────────────┐\n│ [100px][100px][100px] ←100px │\n│ A B C extra space │\n└────────────────────────────────────┘\n 300px used, 100px remaining\n\nStep 2: Distribute extra space (flex-grow: 1)\nEach item grows by: 100px / 3 = 33.33px\n┌────────────────────────────────────┐\n│ [133px][133px][133px] │\n│ A B C │\n└────────────────────────────────────┘\n All items grow equally (same grow factor)\n\nWith flex-wrap: wrap\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nNarrow container (250px):\n3 items @ 100px each = 300px needed\n\nWithout wrap (overflow):\n┌──────────────────────┐\n│[83px][83px][83px]│ overflow\n│ A B C │\n└──────────────────────┘\n Items shrink to fit (flex-shrink: 1)\n\nWith wrap (responsive):\n┌──────────────────────┐\n│ [125px][125px] │ Line 1: 2 items\n│ A B │\n│ [125px] │ Line 2: 1 item\n│ C │\n└──────────────────────┘\n Items wrap to new line, grow to fill\n\nCommon flex patterns:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nflex: 1 → flex: 1 1 0 (equal widths)\nflex: auto → flex: 1 1 auto (content-based)\nflex: none → flex: 0 0 auto (fixed size)\nflex: 1 1 100px → grow, shrink, 100px base", "containerVsItem": "flex-wrap is a CONTAINER property that controls whether items wrap to new lines. The flex shorthand (flex-grow, flex-shrink, flex-basis) is an ITEM property that controls how individual items grow, shrink, and their starting size." }, "validations": [ { "type": "contains", "value": "flex-wrap: wrap", "message": "Use flex-wrap: wrap", "options": { "caseSensitive": false } }, { "type": "regex", "value": ".item.*flex:\\s*1\\s+1\\s+100px", "message": "Set flex: 1 1 100px on items", "options": { "caseSensitive": false } } ] }, { "id": "layouts-3", "title": "Grid Basics", "description": "Define grid containers, set rows and columns, and place items in a structured grid.", "task": "Set .grid to display: grid with three equal columns and a 1rem gap.", "previewHTML": "
1
2
3
4
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .grid > div { background: #ffe082; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Define Grid */\n.grid {", "initialCode": "", "codeSuffix": "}", "previewContainer": "preview-area", "concept": { "explanation": "CSS Grid is a two-dimensional layout system that divides space into rows and columns, creating a matrix of cells where items can be placed. Unlike Flexbox (which handles one dimension at a time), Grid controls both dimensions simultaneously, making it ideal for page layouts, complex card arrangements, or any design that needs precise row and column alignment. The fr (fraction) unit divides available space proportionally—repeat(3, 1fr) creates three equal columns that each take 1/3 of the container width. The gap property adds spacing between grid items without affecting the outer edges, eliminating the need for complex margin calculations. Grid's ability to align content in both dimensions makes it the best choice for two-dimensional layouts.", "diagram": "CSS Grid: Two-Dimensional Layout System\n\nGrid vs Flexbox:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nFlexbox (1D):\n┌────────────────────────┐\n│ [1] [2] [3] [4] │ One axis (row)\n└────────────────────────┘\n\nGrid (2D):\n┌───────┬───────┬───────┐\n│ [1] │ [2] │ [3] │ Rows AND\n├───────┼───────┼───────┤ Columns\n│ [4] │ │ │ simultaneously\n└───────┴───────┴───────┘\n\nGrid with repeat(3, 1fr) and gap: 1rem:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n 1fr 1fr 1fr\n (33%) (33%) (33%)\n ↓ ↓ ↓\n┌─────────┬─────────┬─────────┐\n│ 1 │ 2 │ 3 │ ← Row 1\n├─────────┼─────────┼─────────┤\n│ 4 │ │ │ ← Row 2 (auto)\n└─────────┴─────────┴─────────┘\n ↑ gap: 1rem between cells\n\nHow fr units work:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nContainer: 600px wide, gap: 20px (2 gaps)\nAvailable: 600px - 40px = 560px\n\nrepeat(3, 1fr) = 3 equal fractions\nEach column: 560px / 3 = 186.67px\n\n┌──186px──┬──186px──┬──186px──┐\n│ 1 │ 2 │ 3 │\n└─────────┴─────────┴─────────┘\n 20px gap 20px gap\n\nDifferent fr ratios:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\ngrid-template-columns: 1fr 2fr 1fr\n(1/4 + 2/4 + 1/4 = 4 parts total)\n\n┌──140px──┬───280px───┬──140px──┐\n│ 1fr │ 2fr │ 1fr │\n│ (25%) │ (50%) │ (25%) │\n└─────────┴───────────┴─────────┘\n\nWhen to use Grid vs Flexbox:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nGrid: Page layouts, card grids, forms\nFlexbox: Navigation bars, button groups,\n single-axis content flow", "containerVsItem": "display: grid, grid-template-columns, and gap are CONTAINER properties that define the grid structure. Items automatically flow into grid cells in order, or can be explicitly placed using grid-column/grid-row (item properties)." }, "validations": [ { "type": "contains", "value": "display: grid", "message": "Use display: grid", "options": { "caseSensitive": false } }, { "type": "contains", "value": "grid-template-columns", "message": "Define grid-template-columns", "options": { "caseSensitive": false } }, { "type": "regex", "value": "grid-template-columns:\\s*repeat\\(3,\\s*1fr\\)\\s*", "message": "Create three equal columns with repeat(3, 1fr)", "options": { "caseSensitive": false } }, { "type": "contains", "value": "gap", "message": "Use gap property", "options": { "caseSensitive": false } } ] }, { "id": "layouts-4", "title": "Grid Placement", "description": "Control the span and position of grid items with grid-column and grid-row.", "task": "Span the first grid item across 2 columns using grid-column: 1 / span 2.", "previewHTML": "
Featured
2
3
4
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .grid > div { background: #c8e6c9; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Span item */\n.item1 {", "initialCode": "", "codeSuffix": "}", "previewContainer": "preview-area", "concept": { "explanation": "CSS Grid uses numbered lines (not cells) to position and span items across the grid. Grid lines run between columns and rows, starting at 1 on the left/top edge. The syntax grid-column: 1 / span 2 means 'start at line 1 and span across 2 column tracks,' effectively occupying the space from line 1 to line 3. The browser automatically flows remaining items around the spanning item, filling available cells. This line-based placement system allows for complex overlapping layouts, featured items that span multiple columns/rows, and precise control over where each element appears in the grid. Unlike absolute positioning, grid-placed items still participate in the document flow and respond to content changes.", "diagram": "Grid Lines & Spanning Items\n\nGrid line numbering:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n 1 2 3 4 ← Column lines\n ↓ ↓ ↓ ↓\n1 → ┌───────┬───────┬───────┐\n │ │ │ │ Row 1\n2 → ├───────┼───────┼───────┤\n │ │ │ │ Row 2\n3 → └───────┴───────┴───────┘\n\nLines run BETWEEN cells, not through them!\n\nSpanning with grid-column: 1 / span 2\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n 1 2 3 4\n ↓ ↓ ↓ ↓\n1 → ┌───────────────┬───────┐\n │ Featured │ 2 │\n │ (spans 2) │ │\n2 → ├───────┬───────┼───────┤\n │ 3 │ 4 │ │\n3 → └───────┴───────┴───────┘\n ↑\n Starts at line 1\n Spans 2 column tracks (1→2→3)\n Ends at line 3\n\nSpan syntax variations:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\ngrid-column: 1 / span 2\n Start at line 1, span 2 tracks\n\ngrid-column: 1 / 3\n Start at line 1, end at line 3\n (Same result as above)\n\ngrid-column: span 2\n Auto-place and span 2 tracks\n\ngrid-row: 1 / span 2\n Span 2 rows vertically\n\nComplex spanning example:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n.header { grid-column: 1 / 4; } /* Full width */\n.sidebar { grid-row: 2 / 4; } /* 2 rows tall */\n\n 1 2 3 4\n ↓ ↓ ↓ ↓\n1 → ┌───────────────────────┐\n │ Header │ ← Spans 3 columns\n2 → ├───────┬───────────────┤\n │ Side │ Content │\n │ bar │ │ ← Sidebar spans\n3 → │ ↓ ├───────┬───────┤ 2 rows\n │ │ 3 │ 4 │\n4 → └───────┴───────┴───────┘\n\nWhy use line-based placement:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n✓ Create magazine-style layouts\n✓ Feature items spanning multiple cells\n✓ Overlap items intentionally (z-index works)\n✓ Precise control without absolute positioning\n✓ Items still flow with content changes", "containerVsItem": "grid-column and grid-row are ITEM properties that control where individual items are placed and how many tracks they span. The grid container defines the track structure (grid-template-columns/rows), while items decide their own placement within that structure." }, "validations": [ { "type": "contains", "value": "grid-column", "message": "Use grid-column property", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "grid-column", "expected": "1 / span 2" }, "message": "Span across 2 columns with grid-column: 1 / span 2", "options": { "caseSensitive": false } } ] } ] }