Installing PHP on MacOS Monterey

Here is the solution to a problem faced by any PHP developer that has upgraded their Mac to Monterey only to then re-attempt to run their PHP-based application afterwards and get back the dreaded response:

php: command not found

😨

That’s not good! Did Apple get rid of PHP in their latest operating system? Surely not!

Me – an hour after upgrading my MacBook

Oh, I see, that’s exactly what they did.

Me – 5 minutes later after Googling it

Right, so, how do we fix it?

First things first, make sure you have Homebrew installed. Chances are you already do, it being similar to apt for most Linux distros, but it’s best just to check.

brew -v

If you get back something that looks like the below, then you have it installed, otherwise you’ll need to go and install it.

Homebrew 3.5.9
Homebrew/homebrew-core (git revision 3eda28188e5; last commit 2022-08-22)
Homebrew/homebrew-cask (git revision eacfe8f6c1; last commit 2022-08-22)

Right, so now, just brew install php right? Unfortunately, this won’t work in quite the way you’re expecting, so it’s best now to install a formula that actually allows for more versions of PHP to become available, than were available on older versions of MacOS anyway:

brew tap shivammatur/php

Now you can install it, the only caveat is that you need to specifically choose the version (unless it’s 8.0).

brew install shivammatur/php/php@8.1

Other options available are:

  • 5.6
  • 7.0
  • 7.1
  • 7.2
  • 7.3
  • 7.4
  • 8.0 – as above, you can remove the @ part for this version

Now you need to link it to the php command:

brew link --overwrite --force php@8.1

Where possible of course, I’d usually recommend using the latest available version.

Now all these changes mean we need to restart our terminal before PHP will work, so once you’ve done that run php -v and you should get back something like the below:

PHP 8.1.9 (cli) (built: Aug  4 2022 15:11:08) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.9, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.9, Copyright (c), by Zend Technologies

What if I want to change my PHP version?

You can change the PHP version that is installed and linked, but you will need to restart your terminal again after doing so. Let’s say I want to switch back to 7.4 for some reason:

brew install shivammatur/php/php@7.4

(You will only need to do the install step once.)

brew unlink php && brew link --overwrite --force php@7.4

Of couse, you need to unlink the command from its current destination before you can link it somewhere else!

But, wait, couldn’t I just use Docker?

Docker Moby Logo

In most cases, indeed you could! I’d actually recommend this method wherever possible, as you can spin up an image in moments with all the necessary dependencies, configurations, and of course PHP version for your project. However, my situation is rather unique (although maybe not as unique as I think) in that when accessing private Gitlab repositories for my company, even so far that I have to be connected to the company’s VPN to even access the login page, your SSH keys that are on your machine can’t then be used by a Docker container even if it is running on that same machine.

Docker also givs you the advantage of offering more versions. A lot more versions. A huge number of versions! A cursory glance at the tags available on the Docker Hub should give you an idea.

Conclusion

Is there a roadblock preventing you from using Docker, or a very good reason why you shouldn’t? Perhaps you haven’t learned Docker yet, luckily the learning curve is easy for beginners to get onto so I may well do an article on that as well (maybe even a video, I haven’t decided yet). If there is such a reason or roadblock, then go through the process I’ve described above. Otherwise, Dock away!

Oh, and, even if you do go down the Docker route, I’d still suggest at least completing the Homebrew step at the start of this post. A Mac without Homebrew is a bit like an internal combustion car without a gearbox, it will still work, but not nearly as well as it should!

Creating a setter function in JavaScript Objects

As part of one of my projects I have had a central object called "layout" with it being to do with manipulating SVGs, and as part of that I decided it would be easier to write said layout object such that it has a getter and setter function, so that:

layout.item.points.X = 20;

…becomes:

layout.set('item.points.X',20);

…much more verbose and much more functional, I’m sure you’ll agree!

So first, the getter function, well, this couldn’t be easier really:

const layout = {
  get: function() {
    return this;
  },
  set: // To be confirmed
  /* All other properties */
}

But now, what about the setter? Well, that’s where it gets a bit more complicated. First, we need to take the propChain and newState from the user:

const layout = {
  set: function(propChain,newState) {}
}

I have written it so that it follows the usual object notation, as per the example above, so if you want to set item’s pointX value to 20 you give it layout.set('item.points.X',20).

First of all, let’s prevent the user from being able to change either the setter or getter:

const layout = {
  set: function(propChain,newState) {
    if (['get','set'].includes(propChain)) {
      console.warn(`🛑 What are you doing? Should you really be changing either of these functions? `);
      return;
    }
  }
}

And get our function to exit if the propChain is empty:

const layout = {
  set: function(propChain,newState) {
    if (['get','set'].includes(propChain)) {
      console.warn(`🛑 What are you doing? Should you really be changing either of these functions? `);
      return;
    }
    if (!propChain) return;
  }
}

Now for the fun bit! We want to split our propChain into sections and:

  • Check for the first property on our original object, and create it if it isn’t there
  • If it is there (which it now is) check if we’re at the end of the provided chain
  • If we are, set the property to equal the given newState
  • If not, go one level further into the object (having just created the new property) and repeat from step 1
const layout = {
  set: function(propChain,newState) {
    if (['get','set'].includes(propChain)) {
      console.warn(`🛑 What are you doing? Should you really be changing either of these functions? `);
      return;
    }
    if (!propChain) return;
    propChain.split('.').reduce((original/*Object*/,prop,level/*how deep we are into the chain*/) => {},this);
  }
}

So, um, what?

Let’s go through it bit-by-bit.

We use the split function to split propChain from a string into an array, using . as the breaking point (just how you’d access an object property in JavaScript anyway) on which we can now use the reduce function.

The reduce function is immensely powerful and I’m often guilty of discarding it in favour of map because I’m more comfortable there.

The reduce function takes up to 4 parameters (read more on MDN) but we’re only interested in the first 3: the accumulated value, the current value and the current index, which we’re calling original, prop and level.

const layout = {
  set: function(propChain,newState) {
    if (['get','set'].includes(propChain)) {
      console.warn(`🛑 What are you doing? Should you really be changing either of these functions? `);
      return;
    }
    if (!propChain) return;
    propChain.split('.').reduce((original,prop,level) => {
      // Firstly, check if our original object has the property, and add it if not.
      if (!(prop in original)) {
        original[prop] = {}; // Why a blank object? In case we go deeper into the chain and need to add properties to this, which you can't on undefined, 0 or an empty string
      }
    },this);
  }
}

Could I not have used original.hasOwnProperty(prop)? In JavaScript yes but in TypeScript the linter shouts at you: Do not access Object.prototype method 'hasOwnProperty' from target object no-prototype-builtins.

const layout = {
  set: function(propChain,newState) {
    if (['get','set'].includes(propChain)) {
      console.warn(`🛑 What are you doing? Should you really be changing either of these functions? `);
      return;
    }
    if (!propChain) return;
    propChain.split('.').reduce((original,prop,level) => {
      if (!(prop in original)) {
        original[prop] = {};
      }
      // Now check if we're at the end of our given chain and if we are, set the property to the given newState
      if (level === propChain.split('.').length - 1 /*Remember, indexing starts at 0*/) {
        original[prop] = newState;
      }
      // Now return our object, and that's it!
      return original[prop];
    },this);
  }
}

Finally we arrive at:

const layout = {
  get: function() {
    return this;
  },
  set: function(propChain,newState) {
    if (['get','set'].includes(propChain)) {
      console.warn(`🛑 What are you doing? Should you really be changing either of these functions? `);
      return;
    }
    if (!propChain) return;
    propChain.split('.').reduce((original,prop,level) => {
      if (!(prop in original)) {
        original[prop] = {};
      }
      if (level === propChain.split('.').length - 1) {
        original[prop] = newState;
      }
      return original[prop];
    },this);
  }
}

Or, in TypeScript:

interface LayoutObject extends Record<string, unknown> {
  get: () => LayoutObject;
  set: (
    propChain: string,
    newState: Record<string, unknown> | string | number
  ) => void;
  // All the rest of our properties
}

// TypeScript uses interfaces to define, well, almost everything!

const layout: LayoutObject = {
  get: function (): LayoutObject {
    return this;
  },
  set: function (
    propChain: string,
    newState: Record<string, unknown> | string | number
  ): void {
    if (['get', 'set'].includes(propChain)) {
      console.warn(
        `🛑 What are you doing? Should you really be changing either of these functions?`
      );
      return;
    }
    if (!propChain) return;
    propChain.split('.').reduce((original, prop, level) => {
      if (!(prop in original)) {
        original[prop] = {};
      }
      if (level === propChain.split('.').length - 1) {
        original[prop] = newState;
      }
      return original[prop];
    }, this);
  },
  // All the rest of the properties
}

Randomising Flex Order of Items

This all stems from a random conversation this week, where a colleague was fiddling with the layout of our new website. She essentially wanted a row of vendor logos to display, but in the era of responsive web design, not cram 8 of them onto a mobile screen, and not use 3 of them to fill up a laptop, so our initial idea was to simply use media queries to hide/show certain ones.

Not bad, but not brilliant, it just seems a bit static and there’s too much chopping and changing, we could do with it being a bit more fluid. That’s when she came up with these 2 brilliant lines of code that got rid of the need for any media queries at all!:

.logos-container {
  /* other styles such as display: flex; and flex-flow: row wrap; */
  height: same-as-logos-height; /* They're all png files set to the same height */
  overflow: hidden;
}

So that’s the cramming issue sorted, and all these logos can now be displayed on a large screen, with only 2 or 3 of them on a mobile. But now there’s another problem:

A mobile user will only ever see the first 2 or 3 logos that are in the markup, and may never see the rest of the vendors we work with!

Kate: “It would be good to be able to randomise these.”

Me: “Well there’s flex-order that can change the order of how things appear no matter where they are in the markup, so I reckon that would be the answer. Randomising though? I’m not sure CSS can do that.”

Enter JavaScript.

Me: “I promise you, there is a way to do this and I will find that way.”

Me a few days later: “Here it is!”:

See the Pen Randomising Flex Order of Items by Gavin Sykes (@gavinsykesuk) on CodePen.

This was also my first step into using either a CSS or HTML preprocessor. I’d always viewed these tools in the past with a “well, what’s the point?” attitude, but have started slowly warming to them. Certainly, HAML’s ability to do for loops and create a lot of divs without having to change x lines of code to tweak them all slightly I found very helpful. Plus, change one number and you get more or fewer items, no mass deletion and no copying and pasting multiple times! SCSS is also another string to the bow, and I believe its ability to manage large-scale projects may well come in useful in the future.

Cross-posted on dev.to

Append a Child to an SVG Element in Pure JavaScript

Why write a blog post about this, Gavin? Well it turns out that appending a child to an SVG, or within an SVG group, isn’t as simple as it would be if we were working with plain HTML.

I’m sure there are good reasons for this, but part of me would really like to know what those reasons are! I imagine they will be something to do with SVGs being written in XML rather than HTML. That, however, is research for another day, unless someone reading this knows the answer in which case all comments are welcome.

Well, why not just use jQuery or D3? They both have an append function and it works just fine.

They do indeed, however, for this particular project I want something that ships as light as possible, and as light and as powerful as jQuery and D3 are, it would still mean downloading an entire library just for the sake of appending elements to an SVG, whereas this entire project (so far) has 21 lines of JavaScript.

Fear not though, because if you’ve ever tried the below code:

let newElement = document.createElement('rect');
newElement.setAttribute('fill','orange');
newElement.setAttribute('width','200');
newElement.setAttribute('height','200');
document.getElementById('svg-drawing').appendChild(newElement);

Only for nothing to appear, despite it appearing in the DOM (which really does call into question what those good reasons could possibly be), then look no further, I have the solution.

To really work properly with SVGs we have to enter the world of namespaces. Indeed, this is exactly what jQuery and D3 do. By making one small change to our code we can make it work as it should, observe below:

let newElement = document.createElementNS('http://www.w3.org/2000/svg','rect');
newElement.setAttribute('fill','orange');
newElement.setAttribute('width','200');
newElement.setAttribute('height','200');
document.getElementById('svg-drawing').appendChild(newElement);

Now, the differences under the hood between createElementNS and createElement, I couldn’t tell you, though it’s clear that it somehow tells the browser it is creating an SVG rectangle element, rather than just a rectangle element to go, um, somewhere else, somehow, I guess.

And that’s basically it! I’ve taken the liberty of writing a helpful function to append on object to SVG, as I’m sure you won’t want to be typing, or even copying and pasting, that URL each time.

const appendSVGChild = (element,target,attributes = {},text = '') => {
  let e = document.createElementNS('http://www.w3.org/2000/svg',element);
  Object.entries(attributes).map(a => e.setAttribute(a[0],a[1]));
  if (text) {
    e.textContent = text;
  }
  target.appendChild(e);
  return e;
};

// If applying attributes, they need to be in the format {'attr':'val','attr2':'val2',...}

Thoughts? Comments? Improvements? Improvements especially welcome as I’m sure there are some to be made!

Cross-posted on Dev.to

Making a responsive navbar and placing it on a page with a sticky footer.

See it in action on CodePen:

See the Pen Responsive Navbar in page with sticky footer by Gavin Sykes (@gavinsykesuk) on CodePen.

Starting with the footer positioning

A common issue with placing footers can be a page looking unneat when its content does not reach to the bottom of a user’s viewport. This was not so complicated in earlier times, when the web was still on computers and there were limited different screen sizes.

via GIPHY

Nowadays though, with all sorts of different screen sizes available, and in the mobile age, it is more important than ever to make your content adapt to any screen size.

There are all sorts of ways to do this, mostly involving JavaScript, that have all sorts of dependencies such as fixed-height footers, which considering how dynamic content can be, simply isn’t practical.

Now with recent CSS features though, it is very simple to achieve, in one of 2 ways:

Flexbox

As the name might suggest, flexbox allows its child items to be displayed in a flexible manner, manipulating their positioning and sizing along an axis, and you as the developer get to decide if this axis is the horizontal or the vertical. To achieve a sticky footer we use flexbox vertically.

Imagine a simple page layout, with a <header>, <nav> (which we’ll come to sorting later), <main> and <footer>:

<header></header>
<nav></nav>
<main></main>
<footer></footer>

We set our surrounding body tag as a flex container. This in itself doesn’t do anything to its child elements, but it does tell them that they are in a flex container and can use certain properties as a result.

The next important piece of code is telling our <body> what to do with its flex children, we want it to arrange them in a column (because we’re stacking everything on top of each other rather than to the side) and not wrap them, we can do this in one line of CSS with flex-flow: column nowrap;. I haven’t seen what would happen if it did wrap, and given how we will set up our child elements I doubt it would try to wrap anyway, but if it ever did the results would be bad, so that nowrap acts as a safety feature more than anything.

In order to get our header and footer right to the edge of the viewport, it is often useful to set the margins on our <body>, and even our <html>, to 0, and their minimum heights to 100vh;

One additional thing we can do is set align-items to stretch, this stretches all of our elements across its axis, removing the need to set their widths individually.

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-flow: column nowrap;
  align-items: stretch;
}

footer {
}

header {
}

main {
}

nav {
}

Now that the container is taken care of, we need to look at the children. The beauty of flex children is that they can stretch and shrink to accommodate any environment, but they can also be set to do the exact opposite of that, which is what we want to do here. We do this by utilising the flex-grow and flex-shrink properties.

flex-grow is set to a number and behaves according to that number in relation to its siblings, if two flex children each have a flex-grow value of 1 then they will grow at the same rate, if you change one of them to 2 however then that one will grow at twice the rate of the other one. flex-shrink works much the same way only it does the opposite, it shrinks items to fit in the available space rather than growing them to fill the available space.

If you set flex-grow to 0 it will not grow at all, and the same applies with shrinking when flex-shrink is set to 0.

With this in mind we want our <header>, <nav> and <footer> to keep their height i.e. not grow or shrink, and our <main> to take up the rest of the available room.

In order to have our <main> take up the rest of the available space, simply set its flex-grow to 1, this has the added bonus effect of not needing to set it to 0 on our other elements, as if only 1 element is expanding to take up all available room, no others will.

We should, however, make sure that none of our elements shrink, by setting their flex-shrink property to 0. On our <main> this can be done by the shorthand property flex, which sets flex-grow and flex-shrink in a single line, so we can set it to 1 0. It can also set flex-basis which you may want to play around with but isn’t really necessary here and defaults to auto.

Strangely, although we don’t need to set flex-grow on our other elements to 0, if we use this shorthand property to do it our CSS will end up shorter! Plus it provides a safety mechanism much like setting the nowrap on our flex container.

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-flow: column nowrap;
  align-items: stretch;
}

footer {
  flex: 0 0;
}

header {
  flex: 0 0;
}

main {
  flex: 1 0;
}

nav {
  flex: 0 0;
}

The one (and so far only that I’ve found) drawback to this is that all of your content must be in a line for it to work, you cannot have an <aside>, for example, or any other <div>s, unless they are directly above or below something else, rather than to the side. This is where grid comes in.

Grid

Just as the name of Flexbox suggests you can treat its children flexibly, the name of Grid allows you to lay that grid’s children out in a grid fashion.

This now allows us to add additional items to our layout, so rather than just our <header>, <nav>, <main> and <footer>, we can now have an <aside>.

To get started with our grid we need to set the display of the body to grid, and then define that grid.

html {
  margin: 0;
  min-height: 100vh;
}
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}

So what exactly have we done here? Without going too in depth into grid (because grid is insanely powerful and doing that would take us well away from what we’re focussing on here) we have defined 2 columns and 4 rows for a total of 8 cells. The columns have been defined as 2fr wide and 1fr wide, what this means is the left column is 2 portions wide of whatever space is left after any other columns that have rigidly-defined sizes (50px, 30% and so on) and 1fr means 1 portion wide. Essentially our left column in this case takes up 2 thirds and our right column takes up the remaining third.

Looking at our rows, the browser will first look at the rows defined as auto (which means “look at my content and set my height appropriately”) before portioning out the remaining space to the row set to 1fr – in theory as that’s our only fr-defined row, we could have that as any positive number!

Using grid-template-areas, in conjunction with the grid-area property on our child elements allows us to define where things will go on our page.

html {
  margin: 0;
  min-height: 100vh;
}
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}
aside {
  grid-area: aside;
}
footer {
  grid-area: footer;
}
header {
  grid-area: header;
}
main {
  grid-area: main;
}
nav {
  grid-area: nav;
}

We have told our <header> to fill up all areas where the grid displays header and so on, it is important to note that you cannot set items to be L-shaped or any other shape than a rectangle.

The reason I haven’t just defined one column is because we need our page/application to look good on different screen sizes, so lining all our elements up on top of one another means we can read everything on a small screen then use media queries to rearrange the items on larger screens.

You can define these media queries however you like, but the way I have done it is to go mobile-first (which I always recommend you do as well) and rearrange things as you go up the screen sizes. Borrowing a breakpoint from Bootstrap I have set it to be at 768px wide. If our screen is narrower than that we keep our <aside> underneath our <main>, and once it hits 768px we move it to the side.

@media (min-width: 768px) {
 body {
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main aside"
   "footer footer";
 }
}

Notice how we now only have 4 rows, and that <main> and <aside> share a row? This is also why the columns earlier were 2fr and 1fr wide, to keep the main content bigger, although you could set this how you want, 3fr and 1fr, 3fr and 2fr, 10fr and 1fr, even 1fr and 1fr!

When doing media queries, if one of them evaluates to true then the browser will overwrite only the rules that are part of that media query, so in our media query we can set only the grid-template-rows and grid-template-areas property, without having to set display: grid or anything else, as it simply keeps the already-defined rule.

By naming our grid areas and setting each element to take up the space defined by those areas, this also means we needn’t do a single thing to our child elements in media queries, simply rearranging the grid layout will move them.

And here we have it! A layout grid that adjusts to your screen size and always keeps the footer at the bottom of the page.

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}

footer {
  grid-area: footer;
}

header {
  grid-area: header;
}

main {
  grid-area: main;
}

nav {
  grid-area: nav;
}

@media (min-width: 768px) {
 body {
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main aside"
   "footer footer";
 }
}

TL;DR

Flexbox

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-flow: column nowrap;
  align-items: stretch;
}

footer {
  flex: 0 0;
}

header {
  flex: 0 0;
}

main {
  flex: 1 0;
}

nav {
  flex: 0 0;
}

Grid

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}

footer {
  grid-area: footer;
}

header {
  grid-area: header;
}

main {
  grid-area: main;
}

nav {
  grid-area: nav;
}

@media (min-width: 768px) {
 body {
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main aside"
   "footer footer";
 }
}

And now the navbar

First of all we need to lay out our navbar’s links inside a <nav> tag:

<nav>
 <a class="active" href="#top">Home</a>
 <a href="#flex-footer">Flexbox Footer</a>
 <a href="#grid-footer">Grid Footer</a>
 <a href="#tldr-footer">Footer (TL;DR)</a>
 <a href="#navbar">Navbar</a>
 <div class="middle"></div>
 <a  class="codepen" href="https://codepen.io/gavinsykesuk"  target="_blank"><i class="fab  fa-codepen"></i><span> Codepen</span></a>
 <a  class="github" href="https://github.com/gavinsykes"  target="_blank"><i class="fab fa-github"></i><span>  Github</span></a>
 <a class="linkedin"  href="https://www.linkedin.com/in/gavinsykes/" target="_blank"><i  class="fab fa-linkedin"></i><span>  LinkedIn</span></a>
 <a class="stackoverflow"  href="https://stackoverflow.com/users/8640133/gavin"  target="_blank"><i class="fab  fa-stack-overflow"></i><span> Stack  Overflow</span></a>
 <a class="twitter"  href="https://www.twitter.com/gavinsykes_uk" target="_blank"><i  class="fab fa-twitter"></i><span>  Twitter</span></a>
 <div id="nav-open" class="open-icon">≡</div>
</nav>

As with our grid, we want to lay it out differently based on the width of the screen we are viewing it on, but also we now want to introduce a bit of JavaScript to prevent the navbar taking up too much space when not needed, and show us all our navigation options when needed. I have used the jQuery library to make our code more concise.

$(() => {
 $('#nav-open').click(() => {
  $('nav').toggleClass('open');
 });
});

If you prefer to use raw JavaScript then you can, but all I’ll say is there is a reason I’ve used jQuery!:

document.addEventListener("DOMContentLoaded",() => {
 document.getElementById('nav-open').onclick = (e) => {
  document.getElementsByTagName('nav')[0].classList.toggle('open');
 }
});

It looks complicated, but basically what this code does is add a function to an item called nav-open (which we’ll come to later), so that when the user clicks it it either adds or removes the open class as necessary to (or from) the navbar.

Of course there is also the Pandora’s Box of frameworks that would have their own methods of doing this such as React and Vue!

We will use flexbox for our navbar, as it is linear i.e. everything will be in a line rather than some bits being to the side of each other.

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

The position: sticky and top: 0 properties tell the navbar that when we scroll away from it on the page, it is to stick to the top of our screen.

As we are mobile-first, we want to set everything inside our navbar to not display, then only pick the items we do want to show.

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

nav * {
 display: none;
 text-align: center;
}

nav a.active {
 display: block;
}

nav div.open-icon {
 display: block;
 font-weight: bold;
}

Depending what you have as your open icon you may not need to set font-weight: bold, but in this case I have. What we have done here is set everything within our navbar to not display, and then overridden that in our active link and the opening icon.

Next we need to worry about what our navbar looks like when it is open, meaning that we want to show everything when it has the open class, and hide it when we click the icon again.

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

nav * {
 display: none;
 text-align: center;
}

nav a.active {
 display: block;
}

nav div.open-icon {
 display: block;
 font-weight: bold;
}

nav.open a {
 display: block;
}

Now we need to turn to what our navbar looks like on bigger screens, one thing we can do straightaway is set our middle div to flex-grow: 1 to make it push everything to its left all the way to the left, and everything to its right all the way to the right, but as we’re doing it outside the media query we need to also set it to display: none so that it doesn’t appear on mobile and take up the whole screen!

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

nav * {
 display: none;
 text-align: center;
}

nav a.active {
 display: block;
}

nav div.open-icon {
 display: block;
 font-weight: bold;
}

nav.open a {
 display: block;
}

nav div.middle {
 flex-grow: 1;
 display: none;
}

@media (min-width: 768px) {
 nav {
  flex-flow: row nowrap;
 }
 nav a:not(.active),
 nav div.middle {
  display: block;
 }
 nav div.open-icon {
  display: none;
 }
}

So what have we done in our media query? Firstly we have set our navbar to display as a row rather than as a column, set all the links within the navbar to display given that we now don’t need to save any space, and set our middle div to display (although it’s empty) to push everything else to the relevant edges as above. Finally as we no longer need our opening button, set it to display: none.