Does Any of This Scare You?

That’s the last ques­tion our Chief Cre­ative Offi­cer asked me dur­ing the design review over the home­page of Dixon Schwabl’s 2018 web­site redesign. That design includ­ed a grid at the bot­tom that looked like this:

DS homepage grid

On the one hand, I was excit­ed because I had just spent some time pour­ing over Racheal Andrew’s fan­tas­tic site, Grid By Exam­ple. How­ev­er, in the back of my mind I had to think through how it would be at all pos­si­ble to con­tent man­age this entire thing in Craft CMS. The idea of the con­tent con­stant­ly chang­ing was a pri­or­i­ty to the team and I real­ly want­ed to add the abil­i­ty to let the lay­out change with it.

Fast for­ward a bit.

After some tri­al and error, we land­ed on a matrix field dri­ving the dif­fer­ent types of con­tent. Each matrix block could link to an entry, dis­play a video, add cus­tom text, or be a well for a cus­tom JS com­po­nent. This matrix field would also be used around the rest of the site, so it was impor­tant that the lay­out would be han­dled out­side of the matrix field. This way we didn’t com­pli­cate things for con­tent edi­tors who need­ed to pop­u­late the matrix field in places that it wouldn’t be set in a two-dimen­sion­al layout.

They lay­out of the matrix wound up being based on a set of table fields that laid out the blocks by the order they appeared in the matrix. For exam­ple, the two-col­umn grid would be made from a two-col­umn table field and the first row would be 1|1”; the sec­ond row would be 2|2”; then to put two blocks side-by-side, the third row would be 3|4”.

If that sounds con­fus­ing, it was. I had to draw sketch­es of each lay­out to make sure that the num­ber of items in our matrix block would fit on each of our six lay­outs. If we decid­ed to move items around or adjust any of the lay­outs, I wound up re-draw­ing the sketch­es each time.

This was a sit­u­a­tion where yes, the grid could be man­aged through the CMS, but it was so con­fus­ing and inef­fi­cient to do so that once we set up the grid for the first time, we left it where it was so we didn’t have to mess with it again. In that kind of sit­u­a­tion it’s bet­ter to just hard code the grid’s CSS, but we had already done all of the work, so we kept it.


There Has Got to Be a Bet­ter Way

After the site launched, I talked with friend and co-work­er, Marc Hartwig, about this set­up and through our dis­cus­sions we real­ized you could han­dle this by mak­ing a cus­tom UI for CSS Grid lay­out. This might come in the form of a field on the page, it could be a set of lay­out drop­downs that are added to matrix blocks them­selves, or it could be a check­box-based lay­out that lives in the side­bar on an entry edit page. There would have to be a way to store the lay­out info and attach it to the entry.

The end result turned into the Grid plugin.

Grid wound up tak­ing the form of a field that lets you lay out sequen­tial items in the Craft CP and then gen­er­ate CSS Grid Lay­out CSS that gets append­ed to the front-end markup.


Upgrad­ing Upgridding?

The actu­al upgrade of the home­page grid took about an hour in total. Some of that time was just mak­ing sure the code was going to be applied only to the home­page (since anoth­er grid is used on oth­er pages to lay out the matrix field). Also, the ren­der­ing of the items on the grid need­ed to be abstract­ed to a Twig macro so I could use them on the home­page and on inte­ri­or pages.

I start­ed the upgrade by installing Grid, then set­ting up my field. It helped that I had fig­ured out the grid tem­plate and row siz­ing when I man­u­al­ly cre­at­ed the grid with CSS. I knew that each col­umn would use 1fr (to cal­cu­late even columns with­in the allot­ted space). For most lay­outs, the rows were all 150px high (more on that in a lit­tle bit). Also, there’s a grid-gap of 20px on all layouts.


The first thing I did was set my Tar­get Field to the matrix field that pop­u­lates the grid. Then I looked at my CSS and fig­ured out which break­points are used to go from a one-col­umn grid up to the six-col­umn grid. In my Grid field set­tings, I set the break­points and device icon for each of my lay­outs then I went back up to set the col­umn and row sizes.


On the first lay­out (used for mobile devices), I want­ed to main­tain the square size as much as pos­si­ble, so instead of set­ting a fixed row size of 150px, I looked at my padding and came up with a vw cal­cu­la­tion that kept the size of the grid items close to a square: calc(85vw - 10px).



DS grid screenshot

At first glance, the grid looks pret­ty sim­ple. There are items that span a 1:1 square size and oth­er items that span either one or two columns and one or two rows. But actu­al­ly — at the behest of this website‘s art direc­tor — if you look close­ly in the top-right area you‘ll see that one item spans 1.5 rows and anoth­er 0.5 to even things out. This added a bit of com­plex­i­ty when the grid was made with CSS and table fields, and this com­plex­i­ty remained when con­vert­ing it to a Grid field.

The mobile layout was straightforward and each grid row was equal to the height of each grid item.
All of the other layouts were based on each row being 50% of the height of a square grid item.

Lay­ing out the grid was pret­ty quick and before I knew it, all of the lay­outs in the Grid field matched the old table field lay­outs. Speak­ing of those table fields, at this point I hap­pi­ly removed them from my Homepage’s field layout.


I made my way to my craft-grid doc­u­men­ta­tion page and grabbed the embed code from the tem­plat­ing sec­tion. By default, the demo code had most of the things I needed:

{% grid entry.gridHandle as gridItems %}
    {% for item in gridItems %}
        {% griditem %}
            {# use item.content.fieldHandle to render grid item content #}
        {% endgriditem %}
    {% endfor %}
{% endgrid %}

I had to add in my new ren­der­ing macro to han­dle each grid item. This required me to pass in each matrix block using item.content.

{% grid entry.featureGridLayout as gridItems %}
    {% for item in gridItems %}
        {% griditem %}
            {{ featureGrid.render(item.content) }}
        {% endgriditem %}
    {% endfor %}
{% endgrid %}

The only thing that was miss­ing was the wrap­per class that set the mar­gins around the grid, and anoth­er class used to set the grid-gap via CSS.

{% grid entry.featureGridLayout as gridItems with { classes: 'home__feature_grid wrapper--grid' } %}
    {% for item in gridItems %}
        {% griditem %}
            {{ featureGrid.render(item.content) }}
        {% endgriditem %}
    {% endfor %}
{% endgrid %}
The final code to embed the grid (with all of the ren­der­ing done in a Twig macro).


So that was it. After doing all of this plan­ning, test­ing, and writ­ing of the Grid plu­g­in, I was hap­py to see that my plu­g­in worked for its intend­ed pur­pose. Since releas­ing the plu­g­in I‘ve found a few oth­er use cas­es and — thanks to some folks in the Craft com­mu­ni­ty — I learned about some use cas­es I didn’t even plan for.

Grid icon


A field that lets you con­tent man­age CSS Grid in Craft CMS.