CSS Grid over Flexbox for Layout System
Context
The original layout system used Flexbox with calc()-based percentage widths for multi-column arrangements. Technology grids used `flex-wrap: wrap` with `width: calc(25% - gap)`, education lists used similar patterns for two-column layouts, and the homepage sections grid relied on percentage-based flex children. This approach required manual gap compensation in width calculations, produced inconsistent wrapping behavior at breakpoints, and made responsive adjustments fragile — every breakpoint needed its own recalculated width percentage. The underlying problem: Flexbox is a one-dimensional layout model being forced into two-dimensional grid behavior through arithmetic workarounds.
Decision
Migrate all multi-column layouts from Flexbox with calc() widths to CSS Grid with explicit grid-template-columns definitions. Technology grids use `repeat(4, 1fr)` with media query overrides to `repeat(3, 1fr)` and `repeat(2, 1fr)`. The homepage sections grid uses `repeat(2, 1fr)`. Education and experience lists use single-column flow with Grid for internal row alignment. The gap property handles all spacing natively without width compensation. Flexbox is retained exclusively for one-dimensional patterns: navigation items, inline metadata, and button groups.
Consequences
Positive: Eliminated all calc() expressions from layout code — reducing cognitive overhead and eliminating a class of off-by-one pixel bugs. Responsive breakpoints are now single-line changes (grid-template-columns override) rather than multi-property recalculations. Grid's implicit row sizing handles variable-height cards without the flex `align-items: stretch` workaround. The layout intent is declarative: `repeat(4, 1fr)` communicates '4 equal columns' more clearly than `width: calc(25% - 9px)`. Negative: CSS Grid's minimum content size behavior can cause unexpected overflow with long unbroken strings — requires explicit `min-width: 0` on grid children in some cases. Browser support is universal for the features used, but subgrid (not used here) would enable more sophisticated nested alignments. The migration touched 12+ components, requiring careful visual regression testing across breakpoints.
Predictions at Decision Time
Expected Grid to eliminate all calc()-based layout bugs and simplify responsive breakpoint management. Predicted the migration would touch 10-15 components. Assumed the min-width: 0 overflow issue would affect 2-3 components. Expected the migration to take 3-4 hours.
Measured Outcomes
All calc() expressions were eliminated as predicted — 18 total across 12 components (within the predicted 10-15 range). The min-width: 0 issue affected exactly 2 components (technology tags with long names, and education titles on mobile) — matching the estimate. The migration took approximately 5 hours, slightly over estimate due to visual regression testing on the technology grid's 4→3→2 column cascade. The unexpected win: the Grid-based layouts exposed and forced fixes for 3 pre-existing alignment issues that were masked by Flexbox's more forgiving overflow behavior.
Unknowns at Decision Time
Did not know whether the visual regression testing would reveal hidden layout issues. Flexbox's default behavior of shrinking items to avoid overflow was masking alignment problems that Grid's strict column boundaries exposed. Also unknown: whether any component's layout would fundamentally resist the Grid model. All components migrated cleanly — the layouts were inherently two-dimensional patterns incorrectly expressed with Flexbox.
Reversibility Classification
Reverting from Grid to Flexbox is a mechanical CSS change per component — replace grid-template-columns with flex-wrap and percentage widths. The HTML structure is identical for both approaches. Components can be reverted individually without affecting others. Estimated reversal: 3-4 hours for all 12 components. There is no scenario where reversal is desirable — Grid is objectively more appropriate for two-dimensional layouts.
Strongest Counter-Argument
Flexbox with modern gap support (now universally available) addresses the primary complaint about calc()-based spacing. The arithmetic was the problem, not Flexbox itself. A Flexbox-with-gap approach would have required fewer component changes and maintained consistency with the one-dimensional patterns that still use Flexbox. Mixed layout models (Grid for multi-column, Flexbox for everything else) create a dual mental model that increases cognitive overhead for future development. The counter-counter: the dual model is actually the correct model — Grid for 2D, Flexbox for 1D — and encoding that distinction explicitly improves architectural clarity.
Technical Context
- No subgrid usage for broader compatibility
- min-width: 0 required on some grid children