This question was asked in Microsoft’s frontend interview.
Grid component is a 12 column placement of elements on the 100% parent width.
Example
export default function App() { return ( <Grid spacing={1}> <GridItem size={4} sm={12} /> <GridItem size={8} sm={12} /> </Grid> ); }
This will create a 4 column and 8 column grid on normal width and 12 column grid each on the small screen size sm
on the max width of 600px.
To create a grid component we will be doing some basic mathematical calculations using the modern Flexbox CSS property. We will have only one breakpoint sm
in the example, you can accordingly add as many as required.
We will create a container Grid
that will hold all the grid items GridItem
.
The Grid container will take the spacing prop and it will be set as gap
CSS property with the multiplier of 8. We will also set the CSS variable of spacing value, which will help us in the calculation of the grid item’s width.
const SPACING_MULTIPLIER = 8; const Grid = ({ spacing, children }) => { return ( <div className={"container"} style={{ gap: SPACING_MULTIPLIER * spacing, "--row-spacing": `${SPACING_MULTIPLIER * spacing - 1}px`, }} > {children} </div> ); };
Grid component will have the container
class that will apply the basic flex properties.
.container { display: flex; flex-wrap: wrap; align-items: center; }
The grid item will take size and the sm
size as props. Based on this we will be assigning the classes and that will apply the width.
const GridItem = ({ size = 1, sm = 1, children }) => { return ( <div className={cx("gridItem", `size-${size}`, `sm-size-${sm}`)}> {children} </div> ); };
gridItem
class will have the common CSS properties.
.gridItem { border: 1px solid; padding: 10px; height: 10px; flex-grow: 0; flex-basis: auto; }
Calculating the grid items width
As we are using 12 column width, each grid item can have the column width in the range of 1 to 12 and we will use the simple mathematical formula to calculate the width.
We will divide the width of the parent by 12 and then multiply this by the column width and then subtract the spacing from it.
For example, for the column width size 3 we will use the following formula.
calc(((100% / 12) * 3) - (var(--row-spacing) * 4));
Remember, we had set the variable row spacing on the Grid container upfront, thus we can use it for calculation. We are multiplying the row spacing by 4 to consider left and right space.
For all the column widths between 1 to 12, we will create different variables and store the calculated values in that. To the classes we will just reuse these variables. All these variables will be available on the container
class.
.container { display: flex; flex-wrap: wrap; align-items: center; --col-grid-1: calc(((100% / 12) * 1) - (var(--row-spacing) * 4)); --col-grid-2: calc(((100% / 12) * 2) - (var(--row-spacing) * 4)); --col-grid-3: calc(((100% / 12) * 3) - (var(--row-spacing) * 4)); --col-grid-4: calc(((100% / 12) * 4) - (var(--row-spacing) * 4)); --col-grid-5: calc(((100% / 12) * 5) - (var(--row-spacing) * 4)); --col-grid-6: calc(((100% / 12) * 6) - (var(--row-spacing) * 4)); --col-grid-7: calc(((100% / 12) * 7) - (var(--row-spacing) * 4)); --col-grid-8: calc(((100% / 12) * 8) - (var(--row-spacing) * 4)); --col-grid-9: calc(((100% / 12) * 9) - (var(--row-spacing) * 4)); --col-grid-10: calc(((100% / 12) * 10) - (var(--row-spacing) * 4)); --col-grid-11: calc(((100% / 12) * 11) - (var(--row-spacing) * 4)); --col-grid-12: calc(((100% / 12) * 12) - (var(--row-spacing) * 4)); } .gridItem.size-1 { width: var(--col-grid-1); } .gridItem.size-2 { width: var(--col-grid-2); } .gridItem.size-3 { width: var(--col-grid-3); } .gridItem.size-4 { width: var(--col-grid-4); } .gridItem.size-5 { width: var(--col-grid-5); } .gridItem.size-6 { width: var(--col-grid-6); } .gridItem.size-7 { width: var(--col-grid-7); } .gridItem.size-8 { width: var(--col-grid-8); } .gridItem.size-9 { width: var(--col-grid-9); } .gridItem.size-10 { width: var(--col-grid-10); } .gridItem.size-11 { width: var(--col-grid-11); } .gridItem.size-12 { width: var(--col-grid-12); }
As we are assigning the classes to the grid items based on the column width provided. Styles will be automatically applied to them.
For the following grid at the normal size:
export default function App() { return ( <Grid spacing={1}> <GridItem size={4} sm={12} /> <GridItem size={8} sm={12} /> </Grid> ); }
It will generate the following grid:
To add the breakpoints just create new set of classes and assign them to the grid item.
@media only screen and (max-width: 600px) { .gridItem.sm-size-1 { width: var(--col-grid-1); } .gridItem.sm-size-2 { width: var(--col-grid-2); } .gridItem.sm-size-3 { width: var(--col-grid-3); } .gridItem.sm-size-4 { width: var(--col-grid-4); } .gridItem.sm-size-5 { width: var(--col-grid-5); } .gridItem.sm-size-6 { width: var(--col-grid-6); } .gridItem.sm-size-7 { width: var(--col-grid-7); } .gridItem.sm-size-8 { width: var(--col-grid-8); } .gridItem.sm-size-9 { width: var(--col-grid-9); } .gridItem.sm-size-10 { width: var(--col-grid-10); } .gridItem.sm-size-11 { width: var(--col-grid-11); } .gridItem.sm-size-12 { width: var(--col-grid-12); } }
For the following grid at the 600px width, it will apply the sm
classes:
export default function App() { return ( <Grid spacing={1}> <GridItem size={4} sm={12} /> <GridItem size={8} sm={12} /> </Grid> ); }
Which will generate this grid
Similar to this, you can add many different breakpoints and props to your grid component as per your requirements.
Complete code for Grid component in React
import "./styles.css"; import cx from "classnames"; export default function App() { return ( <Grid spacing={1}> <GridItem size={4} sm={12} /> <GridItem size={8} sm={12} /> </Grid> ); } const SPACING_MULTIPLIER = 8; const Grid = ({ spacing, children }) => { return ( <div className={"container"} style={{ gap: SPACING_MULTIPLIER * spacing, "--row-spacing": `${SPACING_MULTIPLIER * spacing - 1}px`, }} > {children} </div> ); }; const GridItem = ({ size = 1, sm = 1, children }) => { return ( <div className={cx("gridItem", `size-${size}`, `sm-size-${sm}`)}> {children} </div> ); };
.container { display: flex; flex-wrap: wrap; align-items: center; --col-grid-1: calc(((100% / 12) * 1) - (var(--row-spacing) * 4)); --col-grid-2: calc(((100% / 12) * 2) - (var(--row-spacing) * 4)); --col-grid-3: calc(((100% / 12) * 3) - (var(--row-spacing) * 4)); --col-grid-4: calc(((100% / 12) * 4) - (var(--row-spacing) * 4)); --col-grid-5: calc(((100% / 12) * 5) - (var(--row-spacing) * 4)); --col-grid-6: calc(((100% / 12) * 6) - (var(--row-spacing) * 4)); --col-grid-7: calc(((100% / 12) * 7) - (var(--row-spacing) * 4)); --col-grid-8: calc(((100% / 12) * 8) - (var(--row-spacing) * 4)); --col-grid-9: calc(((100% / 12) * 9) - (var(--row-spacing) * 4)); --col-grid-10: calc(((100% / 12) * 10) - (var(--row-spacing) * 4)); --col-grid-11: calc(((100% / 12) * 11) - (var(--row-spacing) * 4)); --col-grid-12: calc(((100% / 12) * 12) - (var(--row-spacing) * 4)); } .gridItem { border: 1px solid; padding: 10px; height: 10px; flex-grow: 0; flex-basis: auto; } .gridItem.size-1 { width: var(--col-grid-1); } .gridItem.size-2 { width: var(--col-grid-2); } .gridItem.size-3 { width: var(--col-grid-3); } .gridItem.size-4 { width: var(--col-grid-4); } .gridItem.size-5 { width: var(--col-grid-5); } .gridItem.size-6 { width: var(--col-grid-6); } .gridItem.size-7 { width: var(--col-grid-7); } .gridItem.size-8 { width: var(--col-grid-8); } .gridItem.size-9 { width: var(--col-grid-9); } .gridItem.size-10 { width: var(--col-grid-10); } .gridItem.size-11 { width: var(--col-grid-11); } .gridItem.size-12 { width: var(--col-grid-12); } @media only screen and (max-width: 600px) { .gridItem.sm-size-1 { width: var(--col-grid-1); } .gridItem.sm-size-2 { width: var(--col-grid-2); } .gridItem.sm-size-3 { width: var(--col-grid-3); } .gridItem.sm-size-4 { width: var(--col-grid-4); } .gridItem.sm-size-5 { width: var(--col-grid-5); } .gridItem.sm-size-6 { width: var(--col-grid-6); } .gridItem.sm-size-7 { width: var(--col-grid-7); } .gridItem.sm-size-8 { width: var(--col-grid-8); } .gridItem.sm-size-9 { width: var(--col-grid-9); } .gridItem.sm-size-10 { width: var(--col-grid-10); } .gridItem.sm-size-11 { width: var(--col-grid-11); } .gridItem.sm-size-12 { width: var(--col-grid-12); } }