Blog.

This is where I write about the things I like the most.
The articles dont have a fixed periodicity, it depends on my free time!

Creating a dark theme switch with Tailwind & Framer Motion

Dark themes are all the rage, most of the sites you visit today will have some sort of dark theme switch. Allowing you to switch between a light theme and a dark theme on the site you're visiting. I will hopefully explain how to create an awesome switch using a little bit of Tailwind and Frame Motion. Framer motion is an animation library for React, it's super cool and I recommend that you check it out. This is what we will be knocking up today. First let's install framer and then import it into our component ```bash npm install framer-motion ``` Once installed let's add it to our component. ```js import { motion } from "framer-motion" ``` We need to then import `useState` from React so we can capture the state of `isOn` our component should look something like this now. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) return() } ``` Above we have a state of `false` to `isOn` we're currently returning nothing but let's change that now. If you take a look at the Framer [example](https://codesandbox.io/s/framer-motion-2-layout-animations-kij8p?from-embed=&file=/src/App.js) it looks very straightforward. With the example, they're using vanilla CSS. Let's use Tailwind CSS with ours. First, we need to create a container `div` for our switch. ```js <div className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}></div> ``` I have included a ternary operator in my `className` string this is because we need to conditional move the switch when `isOn` is true or false. ```js ${isOn && 'place-content-end'}`} ``` We're using **place-content-end** here which allows us to place the element at the end of its container. This is similar to using `justify-end` in Tailwind. The other styles in `className` are just for my preference you can change these to what you like. Now we have our container div, let's give it some magic. We need to give it an `onClick` attribute. So let's do that now. ```js <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}></div> ``` As you can see we have given the `onClick` a function to execute so let's add that and the div container into our component. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}></div> ) } ``` What are we doing with then `toggleSwitch` why aren't we setting it true? I will explain that later but for now let's leave it the way it is. Now time to add the switch. With the container div we should just have a rectangle with rounded edges, let's change that now. This is where motion comes in, we need to create another `div` but this time it will be a `motion.div` this allows us to give it some frame magic. Let's add that below with some classes from Tailwind. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > </motion.div> </div> ) } ``` We now have out `motion.div` with the additional attributes of `layout` and `transition` let's go through those now. **layout**: `boolean` | `"position"` | `"size"` If `true`, this component will automatically animate to its new position when its layout changes. More info [here](https://www.framer.com/docs/component/###layout) **transition**: Transition Defines a new default transition for the entire tree. More info [here](https://www.framer.com/docs/motion-config/###transition) Let's add our `transition` animations, this is going to be an object like so. ```js const spring = { type: 'spring', stiffness: 700, damping: 30, } ``` - [spring](https://www.framer.com/docs/transition/#spring): An animation that simulates spring physics for realistic motion. - [stiffness](https://www.framer.com/docs/transition/###stiffness): Stiffness of the spring. Higher values will create more sudden movement. Set to 100 by default. - [damping](https://www.framer.com/docs/transition/###damping): Strength of opposing force. If set to 0, spring will oscillate indefinitely. Set to 10 by default. After adding our `motion.div` and `spring` object we should have something like this: ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) const spring = { type: 'spring', stiffness: 700, damping: 30, } return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > </motion.div> </div> ) } ``` This would be our finished switch, but wait there is more..what about the icons and the cool click animation??? Ok, so let's install [React Icons](https://react-icons.github.io/react-icons/) and grab those icons. Install React Icons via npm. ```bash npm install react-icons --save ``` I have chosen the following icons, they're from the Remix library. Let's add those now. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' import {RiMoonClearFill, RiSunFill} from 'react-icons/ri' ... ``` Now we need to place our icons, inside of our toggle switch. Our toggle switch is the `motion.div` we made earlier. This stage is pretty simple, we just need to create another `motion.div` inside of the parent `motion.div` and give it some ternary operators and a `whileTape` attribute like so: ```js <motion.div whileTap={{rotate: 360}}> {isOn ? (<RiSunFill className="h-6 w-6 text-yellow-300" />) : (<RiMoonClearFill className="h-6 w-6 text-slate-200" />)} </motion.div> ``` You can give your icons your own styling but this is how I have set mine up. Using the ternary operator allows us to switch the icon on the status of `isOn` we should now have the following: ```js import {motion} from 'framer-motion' import React, {useState} from 'react' import {RiMoonClearFill, RiSunFill} from 'react-icons/ri' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) const spring = { type: 'spring', stiffness: 700, damping: 30, } return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > <motion.div whileTap={{rotate: 360}}> {isOn ? (<RiSunFill className="h-6 w-6 text-yellow-300" />) : (<RiMoonClearFill className="h-6 w-6 text-slate-200" />)} </motion.div> </motion.div> </div> ) } ``` ### Adding into Local Storage Now we have a working component, but it's not completely done we need to handle our dark mode with `localStrogae` so the user can keep their preference for next time. Reading over the [Tailwind Docs](https://tailwindcss.com/docs/dark-mode) on dark mode, we need to be able to toggle dark mode manually. To do this we need to add ` darkMode: 'class',` into our `tailwind.config.js` file. Something like this. ```js module.exports = { darkMode: 'class', ... ``` Now we can toggle dark mode manually via the switch. I have used the example on the Tailwind website for supporting light mode, dark mode, as well as respecting the operating system preference. However I have tweaked it a little bit, remember the state `const [isOn, setIsOn] = useState(false)` lets change that to read `localStorage` and check if the `theme` is set to `light` ```js // before const [isOn, setIsOn] = useState(false) // after const [isOn, setIsOn] = useState(() => { if (localStorage.getItem('theme') === 'light') { return true } else { return false } }) ``` Instead of the state returning `false` it fires off a function and checks if the theme within local storage is `light` if it is, `isOn` is true if not it's false. Now let's use the state of `isOn` to manage the theme within local storage. ```js if (isOn) { document.documentElement.classList.remove('dark') localStorage.setItem('theme', 'light') } else { document.documentElement.classList.add('dark') localStorage.setItem('theme', 'dark') } ``` The above will do the following: ```html <!-- Dark mode not enabled --> <html> <body> <!-- Will be white --> <div class="bg-white dark:bg-black"> <!-- ... --> </div> </body> </html> <!-- Dark mode enabled --> <html class="dark"> <body> <!-- Will be black --> <div class="bg-white dark:bg-black"> <!-- ... --> </div> </body> </html> ``` Lastly, we add the following which allows us to avoid [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content#:~:text=A%20flash%20of%20unstyled%20content,before%20all%20information%20is%20retrieved.) when changing themes of page loads ```js if ( localStorage.theme === 'light' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches) ) { document.documentElement.classList.add('dark') } else { document.documentElement.classList.remove('dark') } ``` So that's it...our final component should look like this... ```js import {motion} from 'framer-motion' import React, {useState} from 'react' import {RiMoonClearFill, RiSunFill} from 'react-icons/ri' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(() => { if (localStorage.getItem('theme') === 'light') { return true } else { return false } }) const toggleSwitch = () => setIsOn(!isOn) const spring = { type: 'spring', stiffness: 700, damping: 30, } if (isOn) { document.documentElement.classList.remove('dark') localStorage.setItem('theme', 'light') } else { document.documentElement.classList.add('dark') localStorage.setItem('theme', 'dark') } if ( localStorage.theme === 'light' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches) ) { document.documentElement.classList.add('dark') } else { document.documentElement.classList.remove('dark') } return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > <motion.div whileTap={{rotate: 360}}> {isOn ? (<RiSunFill className="h-6 w-6 text-yellow-300" />) : (<RiMoonClearFill className="h-6 w-6 text-slate-200" />)} </motion.div> </motion.div> </div> ) } ```
Published

Setting up Docker with Pipenv

I have always struggled with Docker as I mainly build front end web apps, where I have used services like Vercel to host my applications. Sometimes for work, I am required to build some scripts that need to run in a container. And this where sh*t fits the fan generally. But no more!! I think I have finally cracked it, hello `docker-compose` I have always seen these files but never really used them. I'll give you a rundown of how I am using my `Dockerfile` and `docker-compose` together to get python running with pipenv. ```bash # For more information, please refer to https://aka.ms/vscode-docker-python FROM python:3.9-slim ENV VAR1=10 # Keeps Python from generating .pyc files in the container ENV PYTHONDONTWRITEBYTECODE=1 # Turns off buffering for easier container logging ENV PYTHONUNBUFFERED=1 # Install & use pipenv COPY Pipfile Pipfile.lock ./ RUN python -m pip install --upgrade pip RUN pip install pipenv && pipenv install --dev --system --deploy WORKDIR /app COPY . /app # Creates a non-root user and adds permission to access the /app folder RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app USER appuser # During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug CMD ["python", "main.py"] ``` This is my Docker file for running Python with Pipenv works well enough. This is generally the boilerplate from VSC when you add a Docker file to the workspace. I have only really replaced the `requirements.txt` and `pip` stuff with the below ```bash COPY Pipfile Pipfile.lock ./ RUN python -m pip install --upgrade pip RUN pip install pipenv && pipenv install --dev --system --deploy ``` Which in turns copies my `Pipfile` & `Pipfile.lock` and then installs my dependencies. If you have a file named .env in your project, it’s only used to put values into the docker-compose.yml file which is in the same folder. Those are used with Docker Compose and Docker Stack. It has nothing to do with ENV, ARG, or anything Docker-specific. The key-value pairs, in your `.env` file are used to substitute dollar-notation variables in the docker-compose.yml file. It’s kind of a pre-processing step, and the resulting temporary file is used. This is a nice way to avoid hard-coding values. You can also use this to set the values for environment variables, by substituting the string, but that does not happen automatically. Here is an example docker-compose.yml file, relying on values provided from a .env file: ```yaml version: '3' services: plex: image: linuxserver/plex environment: - env_var_name=${VARIABLE_NAME} # here it is ``` > Hint: When working with an .env file, you can debug your docker-compose.yml files quite easily. Just type `docker-compose config`. This way you’ll see how the docker-compose.yml file content looks after the substitution step has been performed without running anything else. Once you're done just run `docker-compose -f docker-compose.yaml up` this will run a container with your image and all the specified env variables. Like anything it's easy when you understand it.
Published

Stop using multiple states for status

When it comes to fetching API data in React using `fetch` there can be ways to update the user or run conditional code. This can generally mean using a selection of different states. Which can potentially get messy and complicated. You could have something like this: ```js const [data, setData] = useStatus([]) const [success, setSuccess] = useState('') const [loading, setLoading] = useState('') const [rejected, setRejected] = useState('') const [error, setError] = useState('') ``` Which in turn could be like the following: ```js useEffect(() => { setLoading('loading') // state 1 fetch('http://some-api-url') .then(response => { setData(response) setSuccess('success') // state 2 }) .catch(err => { setError(err) setRejected('rejected') // state 3 }) }, []) ``` With the above we have three states to manage and keep an eye on. When it comes to using this conditionally it's going to be a pain in the ass. Something like this. ```js if (success === 'success') { //do something } else if ( loading === 'loading' ) { // do something } else if (rejected === 'rejected') { // do something } ``` Now wouldn't it be easier to use a `status` state instead of worrying about 3 other states. We can include something like a `const [status, setStatus] = useStatus('idle')` and remove all the other states apart from the `setData` and `setError` we need the error state so we can handle the displaying of error messages. So now we have the following which is going to be much cleaner and easier to manage. ```js const [data, setData] = useStatus([]) const [error, setError] = useState('') const [status, setStatus] = useState('idle') useEffect(() => { setStatus('loading') fetch('http://some-api-url') .then(response => { setData(response) setStatus('success') }) .catch(err => { setError(err) setStatus('rejected') }) }, []) if (status === 'success') { // do something } else if ( status === 'loading' ) { // do something } else if (status === 'rejected') { // do something } ``` For me this is much a cleaner way of doing things, I know it's not that much different from using multiple states. However, we only have one state to deal with instead of three. I got inspired to do things this way by Kent C Dodds. More information [here](https://kentcdodds.com/blog/stop-using-isloading-booleans)
Published

Rending arrays in React properly.

I guess you have used [`.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) quite a bit and understand how it works well. We can use `.map` in React too. It helps us render arrays to the page. For me I have never really used a key within a map function for vanilla JS, for React though it's pretty essential, let's see why. Let's say we have the following component. That renders an array of fruit. ```js const allItems = [ {id: 'apple', value: '🍎 apple'}, {id: 'orange', value: '🍊 orange'}, {id: 'grape', value: '🍇 grape'}, {id: 'pear', value: '🍐 pear'}, ] function Keys() { return ( <> {allItems.map((item => ( <li> <label>{item.value}</label> </li> )))} </> ) } ``` With the above example, React will throw an warning: > Warning: Each child in a list should have a unique "key" prop. Now your `.map` function will render the contents of the array without any issue, I mean it is just a "warning" however without a key prop added it can all go wrong. We need to add a key prop to the child of our `.map` function to allow React to know the element's position. You can test this going to the below link and clicking on the elements. https://react-fundamentals.netlify.app/isolated/final/07.extra-1.js See how the first two examples differ from the last one. You’ll notice that using the array index as a key is no different from React’s default behaviour. Using an index is incorrect, as you can see from the focus states here, the focus will always stay on that index. Instead of moving with the element, like it does when using a key. As the key must be unique, we should code it up as such: ```js const allItems = [ {id: 'apple', value: '🍎 apple'}, {id: 'orange', value: '🍊 orange'}, {id: 'grape', value: '🍇 grape'}, {id: 'pear', value: '🍐 pear'}, ] function Keys() { return ( <> {allItems.map((item => ( <li key={item.id}> <label>{item.value}</label> </li> )))} </> ) } ``` Thanks to [Kent C Dodds](https://twitter.com/kentcdodds) for explaining this like a true hero.
Published

Make a star rating in React

When I was attempting to create a card, that allows for a star rating. It wasn't that easy for me...especially when watching a TailWind tutorial that was using Vue.js I did in the end manage to create my own star rating component. ```js <div> { [...Array(5)].map((star, i) => { const ratingValue = i + 1; return ( <AiFillStar color={ratingValue > card.rating ? 'grey' : 'teal'} /> ); }); } </div> ``` Using the above I was able to create these delightful looking cards. ![react card components](//images.ctfassets.net/53u3hzg2egeu/7hsvAcfwsHGglKWkL7xkGO/218ed1576fc1e2e676209d3f6f6d47df/Screenshot_2020-07-01_at_17.02.15.png) ### Step 1. First, we need to create an empty array of 5 items by adding the following: `[...Array(5)]` which will take `(star, i)` as it's arguments we will then map over it. Giving us 5 items these items will be the star icon `<AiFillStar />` which we will return. ### Step 2. We then need to create an iterator so we can give our stars a value of 1 - 5. `const ratingValue = i + 1;` using 1 allows us to start our array at 1 instead of 0. ### Step 3. In our return statement, we have our icon which is imported from [react-icons](https://react-icons.github.io/react-icons/) with a `color` prop. We need to conditionally check if our `ratingValue` is greater than our `card.rating` to be able to produce the number of coloured stars we need. We do this by using an inline if-else with an conditional operator `condition ? true : false.`. Here we are comparing if `ratingValue` is greater than `card.rating` if true then produce grey stars if false produce teal stars. ```js <AiFillStar color={ratingValue > card.rating ? 'grey' : 'teal'} /> ``` If you're wondering where my `card.` is coming from the star rating sits in another map method which is how we're using `card.rating` in our conditional: ```js <div className='cardContainer'> {cardData.map((card) => ( <div className='card'> <img src={card.imgUrl} /> ... ``` Below is the data set I used to get the card rating from. ```js const cardData = [ { imgUrl: 'https://images.unsplash.com/photo-1479502806991-251c94be6b15?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80', beds: 3, baths: 2, title: 'Modern apartment in city center', price: 390000, formattedPrice: '£3,900.00', reviewCount: 69, rating: 4, }, { imgUrl: 'https://images.unsplash.com/photo-1445019980597-93fa8acb246c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80', beds: 1, baths: 2, title: 'Desert get away in the sun', price: 230000, formattedPrice: '£2,300.00', reviewCount: 108, rating: 3, }, { imgUrl: 'https://images.unsplash.com/photo-1553653924-39b70295f8da?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80', beds: 2, baths: 1, title: 'Coastal apartment with communal pool', price: 900, formattedPrice: '£900.00', reviewCount: 2567, rating: 5, }, ]; ``` That is it really...I got stuck with this a couple of time but now it's all working I am happy with the results. These star ratings are always the perfect edition to a card or anything that requires a rating.
Published

Rending navigations with .map() in React

React utilises `.map()` to render items in a list, this can be great for dynamically adding links to our navigation. Instead of copying the markup, again and again, ill run through how I have used `.map()` on my navigation. If you don't know already my site is built on Gatsby a static site generator built on React. Gatsby’s [`<Link>`](https://www.gatsbyjs.org/docs/gatsby-link/) component enables linking to internal pages as well as a powerful performance feature called preloading. In any situation where you want to link between pages on the same site, use the `Link` component instead of an `a` tag. ```js // standard a tag <a href="/posts">Posts</Link> // Gatsby Link component <Link to="/posts">Posts</Link> ``` ### The basis of our navigation Let's start off by creating the foundation of our component. ```js import React from 'react' import { Link } from 'gatsby' const Nav = () => { return() } export default Nav ``` ### Styling our links Now we have everything set up we can now add an object called `styles` this will allow us to use `className` and `activeClassName` It’s often a good idea to show which page is currently being viewed by visually changing the link matching the current page. This can be done by using `activeClassName` — a class name that will only be added to the Link when the current item is active. As I am using [TailWind](https://tailwindcss.com/) here, my styles object is going to look like this: ```js const styles = { className:'text-gray-600 block px-2 py-1 hover:underline hover:text-gray-900', activeClassName: 'underline text-gray-900' } ``` We can then access those properties by dot notation as follows: ```js styles.className styles.activeClassName ``` ### Generating Link data. Now we have our styles in an object, we can now move on to the Link data we want to pass. This will be an array of objects allowing us to use `.map()` on the array. ```js const links = [ { className: styles.className, activeClassName: styles.activeClassName, to: '/projects', name: 'Projects' }, { className: styles.className, activeClassName: styles.activeClassName, to: '/posts', name: 'Posts' }, { className: styles.className, activeClassName: styles.activeClassName, to: '/contact', name: 'Contact' }, ] ``` Above each object has 4 key pairings. - __className__: Our link styles - __activeClassName__: Our styles for active links - __to__: The page we wish to link to once clicked - __name__: What we what our link to display on site Our navigation component should now look something like this: ```js import React from 'react' import { Link } from 'gatsby' const Nav = () => { const styles = { className:'text-gray-600 block px-2 py-1 hover:underline hover:text-gray-900', activeClassName: 'underline text-gray-900' } const links = [ { className: styles.className, activeClassName: styles.activeClassName, to: '/projects', name: 'Projects' }, { className: styles.className, activeClassName: styles.activeClassName, to: '/posts', name: 'Posts' }, { className: styles.className, activeClassName: styles.activeClassName, to: '/contact', name: 'Contact' }, ] return ( ) } export default Nav ``` ### Adding our data to our markup. Within our return statement, we need a parent `<div>` so lets do that ```js return( <div></div> ) ``` You can then build out your navigation bar how ever you like the next bit will be how to dynamically display the links. This can be done by using `.map()` on our links array of objects as below shows: ```js <div> {links.map(link => (<Link className={link.className} activeClassName={link.activeClassName} to={link.to}>{link.name}</Link> ))} </div> ``` When using `.map()` we have to provide it with a callback function which will be passed every item in the `links` array. For the above example, our one required argument for our callback function is `link` which now allows us to access items on the links array by using dot notation again. If you were to check the Gatsby docs for `Link` you can see it should look something like this: ```js <Link className= activeClassName= to= ></Link> ``` Now this wouldnt do anything so we have to add the following to it so `map()` can populate it as so: ```js <Link className={link.className} activeClassName={link.activeClassName} to={link.to}>{link.name}</Link> ``` If you want to learn more about how `map()` works you can see that [here](https://developer.mozilla.org/en-US/docs/Web/js/Reference/Global_Objects/Array/map). ### Finished Item Now we have that in place our navigation component should look like this: ```js import React from 'react' import { Link } from 'gatsby' const Nav = () => { const styles = { className:'text-gray-600 block px-2 py-1 hover:underline hover:text-gray-900', activeClassName: 'underline text-gray-900' } const links = [ { className: styles.className, activeClassName: styles.activeClassName, to: '/projects', name: 'Projects' }, { className: styles.className, activeClassName: styles.activeClassName, to: '/posts', name: 'Posts' }, { className: styles.className, activeClassName: styles.activeClassName, to: '/contact', name: 'Contact' }, ] return ( <div> <div>LOGO</div> <div> {links.map(link => (<Link className={link.className} activeClassName={link.activeClassName} to={link.to}>{link.name}</Link> ))} </div> </div> ) } export default Nav ``` Hope this helps. You can see my navigation component [here](https://github.com/mrpbennett/gatsby-me/blob/master/src/components/nav.js "Link to Nav source code"), if you get stuck you can hit me up on Twitter. ### Learn more about map - [Programming with Mosh. .map()](https://youtu.be/G3BS3sh3D8Q) - [Part 2 of Functional Programming in js - Fun Fun Function](https://www.youtube.com/watch?v=bCqtb-Z5YGQ)
Published

Getting started with Tailwind & Gatsby

I redesigned this site a while ago, moving from Jekyll over to Gatsby mainly because I wanted to learn some React. The learning curve was steep as I was coming from mainly an HTML, CSS and Python background. Never really spent too much time playing with JS, apart from JQuery the much more simplified JS Library. Playing around with Gatsby and React, I started building out separate `.scss` files for each component which got harder and harder to manage. I then found TailWindCSS a utility first CSS framework, the way I styled by components changed for the better. At first, I struggled to get to grips with setting it up within Gatsby, after a few links and youtube videos I got everything set up the way I wanted and below is how I did just that. ## Create your Gatsby project Install the Gatsby CLI globally if you don't have it already ```bash npm install -g gatsby-cli ``` Create your new site and then `cd` into the directory ```bash gatsby new <project-name> && cd <project-name> ``` ## Adding TailWindCSS Once the project has finished building you're now able to add TailWind ```bash # Using npm npm install tailwindcss # Using Yarn yarn add tailwindcss ``` Once that has completed then add a `.css` file to your `src/components` folder to inject Tailwind's `base`, `components`, and `utilitiesstyles` into your CSS: ```css @tailwind base; @tailwind components; @tailwind utilities; ``` ### Create a TailWind config file (optional) You can skip this bit if you want too, but I would recommend creating one as you can change the behaviour of TailWind with it. I generally use it to center my containers as a default. ```bash npx tailwindcss init ``` Which will create a file with the below structure. I have added my own file to help show how I use it. ```js // tailwind.config.js module.exports = { theme: { container: { center: true }, } ``` Learn more about configuring Tailwind in the [configuration documentation](https://tailwindcss.com/docs/configuration). ## Using TailWind with PostCSS [This](https://tailwindcss.com/docs/installation#4-process-your-css-with-tailwind) is where I got stuck when following the TailWind instructions on installing it. Gatsby being Gatsby there was a plugin for PostCSS, so we need to install that. ```bash npm install --save gatsby-plugin-postcss ``` Once the plugin has finished installing, we need to add the config to our `gatsby-config.js` file by adding the following. ```js // gatsby-config.js { resolve: 'gatsby-plugin-postcss', options: { postCssPlugins: [require('tailwindcss')('./tailwind.config.js')], }, }, ``` This now includes the `tailwind.css` and `tailwind.config.js` file, so we're able to process the CSS. ## Final step Now all that is left is to import the tailwind.css file via our `gatsby-browser.js` file by simply adding the following line. ```js // gatsby-browser.js import "./src/components/tailwind.css" ``` ## Purging the CSS Now everything is set up, it's time to set up purge css so we're able to remove any unused CSS. ```bash npm i --save gatsby-plugin-purgecss ``` ```js // gatsby-config.js { resolve: `gatsby-plugin-purgecss`, options: { tailwind: true } } ``` Now this should clean up your unused CSS making your site even faster! ## Done Now you're able to use TailWind within your Gatsby project with ease. Simply just add the class names to your JSX and then run `gatsby develop` to see the changes.
Published

Setting up for JavaScript. Prettier & ESLint in VSC

This is a run-through of how I set up my JavaScript environment for Vanilla, React and Node. Now I am no developer as the title of this website states. This set-up works for me and does what I need it to do, I don't use any style guide although I would like to use the [AirBnb](https://github.com/airbnb/javascript) one, my JS skills just aren't up to refactoring to its needs yet. ### 1. First let us install node via homebrew. I generally use homebrew to install most packages on my Mac. ```zsh brew install node ``` This will install node here `/opt/homebrew/lib` if you install any global packages they will live in this directory. ### 2. I use [VSC](https://code.visualstudio.com) as my editor of choice, there are two extensions that I use [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) and [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) these will keep my JS looking pretty and lint any formatting errors. Then I install the two packages via npm globally. I install them globally as I can use the same setup throughout all my projects, as I am not working in a team I can set my own styling and formatting. ```zsh npm i -g prettier eslint ``` ### 3. Now it's time to get under the hood of VSC. I have my own `.eslintrc.json` setup in my dotfiles dir. I link VSC to this file, which saves me from creating a new eslint config file each time. In VSC I add the following: ```json "eslint.options": { "overrideConfigFile": "/Users/paul/.eslintrc.json" }, ``` I then install the [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier) which allows ESLint and Prettier to play nice with each other. ```zsh npm install --save-dev eslint-config-prettier ``` This **has to be** installed locally, I have tested it being installed globally. It never worked, this is slightly annoying as I now have `nodes_modules` and `package.json` in my User directory but it is what it is. Next, add `prettier` to the `extends` array in your `.eslintrc.*` file. Make sure to put it last, so it gets the chance to override other configs. ```json { "extends": [ "some-other-config-you-use", "prettier" ] } ``` I have played around with some settings in VSC and these seem to work for me. I get the linting from ESLint and then Prettier fixes it for me. There were some YouTube tutorials and online articles that mentioned adding `"editor.formatOnSave": false,` into JavaScript. ```json "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": true } }, ``` I also have the following for ESLint, again not sure if these work but it seems to get things the way I want them. ```json "eslint.enable": true, "eslint.run": "onSave", "eslint.format.enable": true, ``` That's about it. I have installed a few more ESlint / Prettier plugins such as: - [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) - [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) And that's about it. I hope this has helped one of you. If not please let me know on Twitter or if there are anyways of improving this set-up.
Published

When reading docs click...

I would still consider myself a noob developer I have built some cool applications and built some nice looking websites. There is still so much more I could learn, being a developer of any skill level is always a never-ending learning experience. Now I can read developer docs and I can understand them but last night (2022-02-03) something just __"CLICKED"__! I have been building a chrome extension for work. Something I have never even attempted before, I am not the biggest fan of JavaScript but I am coming round to enjoying it more. Anyways I was trying to figure out how to do something when a user reloads the tab they're currently on. After searching good old Stackoverflow I saw an answer about `webNavigation` API. So I took a [look](https://developer.chrome.com/docs/extensions/reference/webNavigation/) it looked like I needed something called onCommitted from the API. > Fired when a navigation is committed. The document (and the resources it refers to, such as images and subframes) might still be downloading, but at least part of the document has been received from the server and the browser has decided to switch to the new document. ```javascript chrome.webNavigation.onCommitted.addListener( callback: function, filters?: object, ) ``` So I pasted the above code block and got to work, what next I was thinking. It takes a callback function ok cool so let us give it a parameter of `details` looks like `details` is used a lot through the docs. ```javascript chrome.webNavigation.onCommitted.addListener((details) => { // ... }) ``` But what on earth is `filters` I was thinking to myself, ok I know it's an object but how do I access it? Good Ol' `console.log()` that's how. After that, the docs made even more sense. Of course `filters` was an object and now I could see the object filters and what I needed to locate was `transistionType` ```javascript chrome.webNavigation.onCommitted.addListener((details) => { console.log(details) }) // This was returned {frameId: 311, parentFrameId: 0, processId: 337, tabId: 278, timeStamp: 1643980974806.526, …} frameId: 311 parentFrameId: 0 processId: 337 tabId: 278 timeStamp: 1643980974806.526 transitionQualifiers: [] transitionType: "auto_subframe" url: "chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/main.html" [[Prototype]]: Object ... } ``` transistionType was the cause of the navigation. From the docs I could see I needed something called `reload` this is where the tab has been reloading. Perfect just what I needed. The rest was now pretty simple I just needed to do some conditional logic on `transitionType` like so. ```javascript chrome.webNavigation.onCommitted.addListener((details) => { if (details.transitionType === "reload") { // Do something ... } }); ``` This is when it all clicked for me, I just needed a call back function that returned an object and within that object was what I needed to complete the task I needed. Thinking back it was all pretty simple, but again everything is more simple when you know it.
Published

Rebuild your static site automatically with Contentful web hooks and Netlify

I finally got [Contentful](https://api.netlify.com/build_hooks/5da887040cdf95770ff25e4f) to work. It took a bit of tweaking here and there, as I am still new to React and GraphQL, I got there in the end though. I was clicking through my site, making sure all the posts were working as intended. I had to re-publish a post as I was having issues with images in markdown files. But my site never started a build phase within Netlfiy after I re-published the post. Whaaaaaat?!!. Why wasn't this working? After some Googling, it seemed I needed something called a webhook. I have seen the word webhook mentioned many times, but was never too sure what they did. So I also googled that: > A webhook in web development is a method of augmenting or altering the behavior of a web page, or web application, with custom callbacks. Makes sense. How could I get a webook to build my site in Netlify once I published or un-published a blog post? Luckily Contentful has some good docs... ### Update your environment variables within Netlify ![netlify-link-to-git.8f403b2067](//images.ctfassets.net/53u3hzg2egeu/7ybgDjEoaI8hzK0qRgwEym/36b8ae5868aa31c9a29252191c5355a5/netlify-link-to-git.8f403b2067.png) For more information on how to configure a static site for continuous deployment, refer to the guide on the [Netlify documentation](https://www.netlify.com/docs/continuous-deployment/). ### Configuring a Netlify build hook ![netlify-build-hooks.601518971e](//images.ctfassets.net/53u3hzg2egeu/1RTHi69ot1LlOvmd2CChus/a7915e09d7e328c751aadc14792b1d2a/netlify-build-hooks.601518971e.png) It's as simple as clicking **Add build hook**, giving it a memorable name, and choosing the branch you'd like to build. Netlify will then do some magic and create a HTTPS URL, which will then respond to a `POST` request by trigging your build and deploying it when you publish or unpublish new content. Copy the url, which will look something like this: `https://api.netlify.com/build_hooks/<someString>` and then use Contentfuls [Netlify webhook template](https://app.contentful.com/deeplink?link=webhook-template&id=netlify-deploy-site) to configure the webook quickly. ![webhook-template-netlify.6cd8664d25](//images.ctfassets.net/53u3hzg2egeu/63dLQEnKRCDJJjMHtgGFL4/7f4aecd1da8d86667280ec0464bb1d41/webhook-template-netlify.6cd8664d25.png) Easy as that...this post was created in Contentful, which trigged my webhook, which built and deployed my site with this new post. Images: from Contentful
Published

Getting API into your templates with Django

It started by giving myself a project. Building a dashboard for our Customer Success team, bringing all our platforms into one UI. This, of course, meant I had to use APIs to populate the data. You can read about this more in depth [here](https://developer.mrpbennett.com/projects/csm-portal) I started to build the dashboard using [Corey Schafer Django Tutorials](https://www.youtube.com/playlist?list=PL-osiE80TeTtoQCKZ03TU5fNfx2UY6U4p) on YouTube as I generally know more Python than anything else. Cory is the MAN!!! I have learnt so much from him, over anything else I have used included [Udemy](https://www.udemy.com). Although I am not knocking Udemy, Cory is just an excellent teacher. Ok, so how did I end up getting my API responses into my template. ### PostMan Postman has now for me, became an invaluable tool. It allows me to test all my APIs before putting them into code. I can dig down into the response so I know what to pull. ### Code I now know what I want to get from the API and what `keys` to use to get the relevant data. Making an API call in Python is pretty straight forward using [Requests](https://2.python-requests.org/en/master/). Something as simple as this will get you a response from an API. ```python import requests def function(): url = 'https://swapi.co/api/starships/9/' r = requests.get(url) data = r.json() return data ``` How did I get my API data into my templates? Well, this was where the headache came from, using Cory's tutorial I was coding blind more or less. As I was learning Django from those tutorials, so when it came to be doing my tweaks I struggled. In Cory's tutorial, we created a class called `PostDetailView` inherits from `DetailView`. This is where our client's data would live, the data was being populated from my model. I then had to create a separate file which would store my API calls, the calls would be as simple as the below: (_keys and domains removed for security_) ```python import requests import json import os def jira_data(): url = 'https://domain.atlassian.net/rest/api/2/search?jql=project=CS' r = requests.get(url, auth=( 'paulb@domain.com', 'JIRA_API_KEY')) data = r.json() return data def pendo_data(): url = 'https://app.pendo.io/api/v1/aggregation' headers = { 'x-pendo-integration-key': "PENDO_API_KEY", 'content-type': "application/json" } r = requests.get(url, headers=headers) data = r.json() first_visit = data['metadata']['auto']['firstvisit'] return first_visit ``` Nothing too complicated here...but how did I get the responses in my template? It was a major headache and a great learning curve for me, after being introduced to `render_to_response` function it was pretty easy to understand. ### The class I had the functions pulling the API data as I wanted in my separate file, now to get them to populate in my template. I had to use the `render_to_repsonse` function in my class to do this. ```python class PostDetailView(DetailView): model = Post template_name = 'clients/post_detail.html' def render_to_response(self, context, **response_kwargs): # pendo api pendo_result = api_calls.pendo_data() context['pendo_data'] = pendo_result # jira api jira_result = api_calls.jira_data() context['jira_data'] = jira_result['issues'] return super().render_to_response(context, **response_kwargs) ``` Using [`context`](https://docs.djangoproject.com/en/2.2/ref/templates/api/#django.template.Context) to fill the data into the template. > Once you have a compiled Template object, you can render a context with it. You can reuse the same template to render it several times with different contexts. The above functions, called the functions which were calling the APIs and then putting the response data into `context` so I was able to use a template expression to populate the data. ### The template Once I was able to get the data out from the APIs and into my views, the rest was pretty simple. YAY! I just had to use Jinga and some template expressions. I won't go into much detail about this step but to populate the data within my template and into my HTML I used the following expression `{{ pendo_data }}` or `{{ jira_data }}` this will then render what was returned by the functions.
Published

Loading CSS via prefered-color-scheme

After learning about `prefers-color-scheme` and how to implement Dark Mode within your website. I also found out that you're able to load different CSS files depending on the users choice of Light or Dark mode. This comes in very handy, when you're build a site that uses syntax highlighting such as [pygments](http://pygments.org) you're able to find some CSS pygment files on Github. Like these [ones](https://github.com/idleberg/base16-pygments/tree/master/css). I have a light syntax theme and dark syntax theme on this site, I can switch between the two by doing the following: ```html <link rel="stylesheet" href="{{ site.url }}{{ site.baseurl}}/css/tomorrow-light.css" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: light)" /> <link rel="stylesheet" href="{{ site.url }}{{ site.baseurl}}/css/dracula.css" media="(prefers-color-scheme: dark)" /> ``` Using `media="(prefers-color-scheme: no-preference), (prefers-color-scheme: light)"` to load a style sheet, when the user is in Light Mode and then using `media="(prefers-color-scheme: dark)"` when they're in Dark mode. I found this neat trick after watching this [tutorial](https://youtu.be/kVKuH5H7nuA) by DesignCourse. You can read more about `prefers-color-scheme` [here](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) Having my CSS set up like this, allows the highlighted code on the site to keep in theme with the color scheme. Nioce!
Published

Converting dates from an API response in a Django Template

As you might of read I struggled [getting data from an API](https://developer.mrpbennett.com/2019/09/02/getting-api-data-into-your-templates-with-django/) into my Django templates. Once I did I came across another issue, dates...bloody full formatted dates. Dates formats such as this: `2019-08-30T08:22:32.245-0700` this data format was how the API presented one of the fields I needed. How the hell could I convert it? I couldn't just use [`datetime`](https://docs.python.org/2/library/datetime.html) So off to [Stackoverflow](https://stackoverflow.com/questions/57759653/how-to-convert-date-from-an-api-response-in-a-django-template) I went. As the brilliant @Ralf said if the data I retrieved from the API was a string and not a Python `datetime.datetime` object. I had 2 options: - Custom template filter that converts string to date and formats it into a string with the desired format - In your view convert strings to dates and format them into strings with the desired format The idea was to start creating a [custom template filter](https://docs.djangoproject.com/en/2.2/howto/custom-template-tags/#writing-custom-template-filters). I copied the following from the source code from the [built-in date filter](https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#date) so I was able to convert back to the date format I wanted, i ended up using `{{ custom_date:"d/b/y" }}` I then went ahead and used the following to create a [custom template tag](https://docs.djangoproject.com/en/2.2/howto/custom-template-tags/#code-layout) ### Step 1 First I had to create a `templatetags` directory inside my app, at the same level as models.py, views.py, etc. I then had to create a `__init__.py` file to ensure the directory is treated as a Python package. Within the `templatetags` dir I created my `custom_date.py` which would hold my custom filter. So the app layout would look something like this: ```bash app/ __init__.py models.py templatetags/ __init__.py custom_date.py views.py ``` ### Step 2 Now on to adding the filter into `custom_date.py` going back to my Stackoverflow question, I copied the example Ralf created. Which is the below ```python from django import template from django.utils import formats import datetime register = template.Library() @register.filter(expects_localtime=True, is_safe=False) def custom_date(value, arg=None): if value in (None, ''): return '' if isinstance(value, str): api_date_format = '%Y-%m-%dT%H:%M:%S.%f%z' # 2019-08-30T08:22:32.245-0700 value = datetime.datetime.strptime(value, api_date_format) try: return formats.date_format(value, arg) except AttributeError: try: return format(value, arg) except AttributeError: return '' ``` I had some bugs with `api_date_format` because the API produced a whole load of extras I checked out the [datetstrftime() and strptime() Behavior table](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior) and made some edits. Once I got rid of the errors I moved on to the template. First I had to load the custom filter into my template, then I used the `custom_date` to change it to what I needed. ```html {% raw %} <td class="text-center">{{ ticket.fields.updated|custom_date"Y,D,M" }}</td> {% endraw %} ``` And there we have it...I successfully formatted the date, from an API response string.
Published

Keeping a changelong

I never use to keep a changelog for my projects, until I started to get more involved in making more complex projects for my own use. I will now not create a project without one, as it allows me to keep track of the project and its changes. So why should you create a changelog? ### What is a changelog? A changelog is a file which contains a curated, chronologically ordered list of notable changes for each version of a project. ### Why keep a changelog? To make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project. ### How do I make a good changelog? #### Guiding Principles - Changelogs are for humans, not machines. - There should be an entry for every single version. - The same types of changes should be grouped. - Versions and sections should be linkable. - The latest version comes first. - The release date of each version is displayed. - Mention whether you follow Semantic Versioning. - Types of changes - Use [ISO Standard](https://www.iso.org/iso-8601-date-and-time-format.html) for dates. - `Added` for new features. - `Changed` for changes in existing functionality. - `Deprecated` for soon-to-be removed features. - `Removed` for now removed features. - `Fixed` for any bug fixes. - `Security` in case of vulnerabilities. You can read more about keeping a changelog at [keepachangelog.com](https://keepachangelog.com/en/1.0.0/) which is run by [Olivier Lacan](http://olivierlacan.com/)
Published

My CSS style guide for easy readable CSS

After reading many articles on writing the perfect CSS, I found that there is no perfect way to write CSS. I mean CSS isn’t the prettiest ‘language,’ but it has successfully powered the styling of the web for over 20 years now. Not doing too bad, huh? As I have written more and more CSS I found it darn difficult to maintain my own CSS, poorly written CSS will quickly turn into a nightmare. Therefore to make my life easier, I wrote my own CSS style guide, let me run you through it below. ```css /* Filename Description of the file and it's contents (sometimes followed by a table of contents) */ /* Section heading -------------------------------------------------- */ /* Sub heading ------------------------- */ /* Short "why" messages */ .wrapper { width: 800px; margin: 0 auto; /* inline note */ } ``` To start, each file should include a repeated name and short description. While the name here can be the same as the filename (as I’ve indicated), combined with the description, it can help you add clarity for other designers. Moving along, every major component is broken down into its own section. Not shown above is that each section has two to three lines after it to space things out. Subsections of that component have their own sub headings as well to further break things apart for readability and documentation. Really though, the last two types of comments are the most important to me. The others are honestly more of a formality and sanity check as searching is just as fast in most cases. ```css /* Topbar inner */ .topbar-inner { ... } ``` The comment adds no value to anyone because it doesn’t tell you why this code is there in the first place. Here’s what might work a bit better: ```css /* Required inner div to stack background effects */ .topbar-inner { ... } ``` While not the greatest example in the world, it illustrates the point. #### Property order A consistent CSS property order makes debugging faster, allows other developers to easily read your code and expectations of what exactly a block of CSS does that much clearer. ##### High level overview At a high level, here’s our breakdown: - `position` - `display` and box model - `font`, leading, `color`, text - `background` and `border` - CSS3 properties like `border-radius` and `box-shadow` - and a handful of other purely visual properties It mixes the common structural and alphabetical approaches into one in an effort to ensure our property order is logical, scannable, and all-encompassing. #### Show me! Getting more specific, here are some blocks of CSS might look with this property order in mind. ```css .topbar { position: fixed; top: 0; right: 0; left: 0; height: 40px; padding: 0 20px; background-color: #333; border-bottom: 1px solid rgba(0,0,0,.1); box-shadow: 0 2px 3px rgba(0,0,0,.5); } ``` Here you can see positioning, box model (`height` and `padding`), and visual (`background-color`, `border-bottom` and `box-shadow`) in their proper order. This flows in such a way that you can discern that this is a fixed dark gray bar with a drop shadow–you know exactly what it is you’re dealing with as you read line-by-line. #### Naming Convention We now have the comment structur, the property order now lets dig into the naming convention is use. This is called BEM. Generally, there are 3 problems that CSS naming conventions try to solve: 1. To know what a selector does, just by looking at its name 2. To have an idea of where a selector can be used, just by looking at it 3. To know the relationships between class names, just by looking at them You may have already guessed that the B in BEM stands for ‘Block’ in the real world, this ‘block’ could represent a site navigation, header, footer, or any other block of design. Lets use one of the sections on my site to explain this, lets use one of the recommendation blocks. We’re going to look at the text underneath the image… The block that holds the type, name and location has a class of .card-text which would look like this: #### E for Elements The E in ‘BEM’ stands for Elements. Overall blocks of design rarely live in isolation. For instance, the card-text block has a type, name, location. The type, name and location are all elements of the card-text block. They may be seen as child components, i.e. children of the overall parent component. Using the BEM naming convention, element class names are derived by adding two underscores, followed by the element name. For example: ```css .card-text { } ``` #### M for Modifiers The M in ‘BEM’ stands for Modifiers. What if the card-text was modified and we could have a uppercase or a italic card-text block? Using BEM, modifier class names are derived by adding two hyphens followed by the element name. For example: ```css .card-text__type { } .card-text__name { } .card-text__location { } ``` #### Full example Below is an example of the BEM naming convention, it’s super easy to implement and makes debugging and modifying your code a dream. Try refactoring your CSS file with the above methods and see how clean and easy your code is to read. ```html <p class="card-text"> <h4 class="card-text__type">hotel</h4> <h4 class="card-text__name">the ludlow</h4> <h4 class="card-text__location">Lower East Side, New York</h4> </p> <!-- CSS --> .card-text { } .card-text__type { } .card-text__name { } .card-text__location { } ``` #### Why Use Naming Conventions? Naming things is hard. We’re trying to make things easier, and save ourselves time in the future with more maintainable code. Naming things correctly in CSS will make your code easier to read and maintain. If you choose to use the BEM naming convention, it will become easier to see the relationship between your design components/blocks just by looking at the markup.
Published

Adding pagination in Gatsby

A page that displays your posts can get incredibly long as you continue to add posts. Pagination can offer a solution to having a massive list of posts, where it takes an age to scroll to the end of them. Pagination allows you to break up those posts into multiple, smaller pages. This tutorial was inspired by the post by Nicky Meuleman, I, however, had issues with it and couldn't get it to work. So this is my version of that tutorial. But first, why did I choose pagination over Infinite Scroll? I ended up picking pagination solely for the challenge of creating it. After reading the article [UX: Infinite Scrolling vs. Pagination](https://uxplanet.org/ux-infinite-scrolling-vs-pagination-1030d29376f1) I decided I made the right choice. Infinite scroll IMO is better suited to apps like Instagram. ### Create a page for your posts. Create a new file in `src/templates/` this will serve as a blueprint for every page that lists the posts. Make sure to import GraphQL and Link from Gatsby, and I have also imported my Layout and Head components too. ```javascript // src/templates/post-list.js import React, { Component } from 'react' import { Link, graphql } from 'gatsby' import Layout from '../components/layout' import Head from '../components/head' const PostList = () => { return ( <Layout> <Head title="Posts"> // - Posts Here </Layout> ) } export default PostList ``` Then you will need to get the post data inside the template using GraphQL below is the query I used. To learn more about GraphQL, you can watch the following videos. - [ Gatsby Data with GraphQL - Andrew Mead](https://www.youtube.com/watch?v=8t0vNu2fCCM&t=5303s) - [GraphQL Basics - Fun Fun Function](https://youtu.be/lAJWHHUz8_8) ```javascript // src/templates/post-list.js export const query = graphql` query($skip: Int!, $limit: Int!) { allMarkdownRemark( sort: { fields: [frontmatter___date], order: DESC } limit: $limit skip: $skip ) { edges { node { fields { slug } frontmatter { title date(formatString: "MMM DD, YYYY") } excerpt(pruneLength: 280) } } } } ` ``` It's time to populate the data into the template for this I am just going to copy my code. If you want to learn more about how it's filled using GraphQL, carry on watching the video by Andrew Mead. I also added `props` as a parameter to `PostList,` enabling me to get that data into my component. ```javascript // src/templates/post-list.js const PostList = props => { const posts = props.data.allMarkdownRemark.edges return (... ``` Read more about React components and props [here](https://reactjs.org/docs/components-and-props.html) ```javascript // src/templates/post-list.js const PostList = props => { const posts = props.data.allMarkdownRemark.edges return ( <Layout> <Head title="Posts" /> <div className={layoutStyles.pageHeader}> <h2>Posts</h2> <span>Just my ramberlings</span> </div> {posts.map(({ node }) => { const title = node.frontmatter.title || node.fields.slug return ( <div className={postPageStyles.postItem}> <div className={postPageStyles.postItemTitle}> <h2>{title}</h2> <span>Posted on {node.frontmatter.date}</span> </div> <div> <p>{node.excerpt}</p> <Link to={`${node.fields.slug}`}> <span>Continue Reading</span> <span role="img"> 👉🏼</span> </Link> </div> </div> ) })} </Layout> ) } export default PostList ``` ### Get data to those listing pages At this point, I am guessing you already have a collection of posts, and they're displayed as a giant list. I won't go over creating a slug for your `.md` files, if you don't, however, head over [here](https://www.youtube.com/watch?v=8t0vNu2fCCM&t=8340s) to learn more. The code below will create an amount of pages that is based on the total number of posts. . Each page will list `postsPerPage` (3) posts, until there are less than `postsPerPage` (3) posts left. The path for the first page is `/posts`, the following pages will have a path of the form: `/posts/2`, `/posts/3`, etc. This will also create a page for each post, each post will use the `post.js` template which you can find on my [repo](https://github.com/mrpbennett/gatsby-me/blob/master/src/templates/post.js). ```javascript // gatsby-node.js module.exports.createPages = async ({ graphql, actions, reporter }) => { const { createPage } = actions const result = await graphql(` query { allMarkdownRemark { edges { node { fields { slug } } } } } `) // Handle errors if (result.errors) { reporter.panicOnBuild('Error while running GraphQL query.') return } // Create the pages for each markdown file const postTemplate = path.resolve('src/templates/post.js') result.data.allMarkdownRemark.edges.forEach(({ node }) => { createPage({ component: postTemplate, path: `${node.fields.slug}`, context: { slug: node.fields.slug, }, }) }) // PAGINATION FOR BLOG POSTS const postsResult = await graphql( ` { allMarkdownRemark( sort: { fields: [frontmatter___date], order: DESC } limit: 1000 ) { edges { node { fields { slug } } } } } ` ) if (postsResult.errors) { reporter.panicOnBuild('Error while running GraphQL query.') return } // Create blog-list pages const posts = postsResult.data.allMarkdownRemark.edges const postsPerPage = 3 const numPages = Math.ceil(posts.length / postsPerPage) Array.from({ length: numPages }).forEach((_, i) => { createPage({ path: i === 0 ? '/posts' : `/posts/${i + 1}`, component: path.resolve('./src/templates/post-list.js'), context: { limit: postsPerPage, skip: i * postsPerPage, numPages, currentPage: i + 1, }, }) }) } ``` ### Adding previous/next navigation This is where I ran to a dead end when using Nicky's tutorial. I was stuck [here](https://nickymeuleman.netlify.com/blog/gatsby-pagination/#navigate-to-previousnext-page). As this was my first React project, I was unaware you were unable to use [const within a class in React](https://stackoverflow.com/questions/58318868/unable-to-use-const-within-a-class-in-react/58319086?noredirect=1#comment103005995_58319086) I kept on getting an undefined error on `this.props.pageContext` after much headache, lots of dog walks, and two days later. I found the solution!! 🎉 I found it watching [this video](https://youtu.be/qcS1VCGWukU?t=238). It was a simple case of using `const { currentPage, numPages } = props.pageContext`. Now my component looked like the below. With everything working now, you can use `currentPage` and `numPages` to determine the routes to the previous/next page. They also make it possible to only show those links if they exist. ```javascript // src/templates/post-list.js const PostList = props => { const { currentPage, numPages } = props.pageContext const isFirst = currentPage === 1 const isLast = currentPage === numPages const prevPage = currentPage - 1 === 1 ? 'posts/' : 'posts/' + (currentPage - 1).toString() const nextPage = 'posts/' + (currentPage + 1).toString() const posts = props.data.allMarkdownRemark.edges return ( <Layout> {!isFirst && ( <Link to={prevPage} rel="prev"> ← Previous Page </Link> )} {!isLast && ( <Link to={nextPage} rel="next"> Next Page → </Link> )} </Layout> ) } ``` ### Adding numbering Iterate over `numPages` and output a number with the relevant link. ```javascript { Array.from({ length: numPages }, (_, i) => ( <Link key={`pagination-number${i + 1}`} to={`/${i === 0 ? '' : i + 1}`}> {i + 1} </Link> )) } ``` I ended up placing all the pagination into it's own div enabling me to style it. This ended up looking like the below: ```html <div className={paginationStyles.paginationBlock}> <div className={paginationStyles.previous}> {!isFirst && ( <Link to={prevPage} rel="prev"> <span role="img" className={paginationStyles.emojis}> 👈🏼 </span> </Link> )} </div> <div className={paginationStyles.numbers}> {Array.from({ length: numPages }, (_, i) => ( <Link key={`pagination-number${i + 1}`} to={`posts/${i === 0 ? '' : i + 1}`} > {i + 1} </Link> ))} </div> <div className={paginationStyles.next}> {!isLast && ( <Link to={nextPage} rel="next"> <span role="img" className={paginationStyles.emojis}> 👉🏼 </span> </Link> )} </div> </div> ``` If this doesnt make any sense, I mean I am also new to Gatsby and React you can check out the relevant files to get the pagination working below: - [post-list.js](https://github.com/mrpbennett/gatsby-me/blob/master/src/templates/post-list.js) - [gatsby-node.js](https://github.com/mrpbennett/gatsby-me/blob/master/gatsby-node.js) The whole repo is also [here](https://github.com/mrpbennett/gatsby-me). #### Update: I actually got in touch with Nicky over Twitter to advise him of the bug in his tutorial. This has now been resolved. However, i'll leave this blog post as it is, as a leaning curve.
Published

Breaking Jekyll posts into years

I struggled with this for sometime until I stubmled upon a [Stackoverflow post](https://stackoverflow.com/questions/19086284/jekyll-liquid-templating-how-to-group-blog-posts-by-year) that has what I was looking for. Seriously people think developers just type all day, I have learnt that one of the biggest skills is knowing how to google your problem and finding the correct soultion. Even the most seasoned developer has to look something up in the docs or on forums now and then. Although developers are mega clever they're not superman. ### Breaking up my posts into years. This is how I did it. If you're using [Bootstrap](https://getbootstrap.com) and like the layout I have you can just copy and paste the code block. ```html {% raw %} {% for post in site.posts %} {% capture this_year %}{{ post.date | date: "%Y" }}{% endcapture %} {% capture next_year %}{{ post.previous.date | date: "%Y" }}{% endcapture %} {% if forloop.first %} <h2 id="{{ this_year }}-ref">{{this_year}}</h2> <ul class="p-0"> {% endif %} <li style="list-style: none;"> <div class="row"> <div class="col-md-8 post_entry my-1"> <a href="{{ post.url }}"> <h2 class="string">{{ post.title }}</h2> </a> </div> <div class="col my-1"> <p class="comment text-small text-right">{{ post.date | date: "%B %e" }} </p> </div> </div> </li> {% if forloop.last %} </ul classp-0> {% else %} {% if this_year != next_year %} </ul> <h2 id="{{ next_year }}-ref">{{next_year}}</h2> <ul class="p-0"> {% endif %} {% endif %} {% endfor %} </ul> {% endraw %} ``` With thanks to the Stackoverflow answer I can now break everything into years, you can too.
Published

Awesome aliases make everything better

I bloody love using aliases within my terminal. It does make everything better, as I have grown more confident using the terminal. I have continued to add more and more aliases, solely to make my workflow quicker. Below I have put all my aliases that are currently in my `.zshconfig` file. I am sure more will be added over time. I have various ones for Python, Django, Gatsby and Git. The Python ones have made my workflow so much easier, especially when running virtual environments. Just a couple are: #### Running a Python script Before: ``` $ python <python-file> ``` After ```bash $ p <python-file> ``` #### Installing Python packages Before: ```bash $ pipenv install <python-package> ``` After: ```bash $ pi <python-package> ``` #### Switching Git branches Before: ```bash $ git switch <branch-name> ``` After: ```bash $ gbs <branch-name> ``` The above are only small changes, but they will make a tonne of difference especially if you're having to frequently type the same commands. Take a look at the list before hopefully, you can get some inspiration from them. If you're using [ohmyzsh](https://ohmyz.sh) you can check out their aliases [here](https://github.com/ohmyzsh/ohmyzsh/wiki/Cheatsheet). ```bash alias zshconfig="code ~/.zshrc" alias ohmyzsh="code ~/.oh-my-zsh" alias ..="cd .." alias ...="cd ../.." alias ....="cd ../../.." alias .....="cd ../../../.." # Shortcuts alias dt="cd ~/Desktop" alias dev="cd ~/Developer" alias dl="cd ~/Downloads" # Git Shortcuts alias gs="git status" alias gp="git pull" alias gb="git branch $1" alias gbs="git switch $1" alias ga="git add ." alias gc="git commit -m $1" alias gca="git commit -a -m $1" alias gpo="git push origin $1" # Python Shortcuts alias python="/usr/local/bin/python3" alias pip="pip3" alias p="python $1" # saves typing python every time. alias pi="pipenv install $1" # allows you to just type the package alias ps="pipenv shell" # start a env with just "ps" # Flask Shortcuts alias fr="flask run" # start a flask app # Django Shortcuts alias rs="python manage.py runserver" # starts django server alias dmm="python manage.py makemigrations" # makes db migrations in django alias dm="python manage.py migrate" # migrates db in django # Gatsby Shortcuts alias gd="gatsby develop" alias gbd="gatsby build" # opens VSC from the cmd line: code () { VSCODE_CWD="$PWD" open -n -b "com.microsoft.VSCode" --args $* ;} ```
Published

Generating a new SSH key and adding it to the ssh-agent

Most of you seasoned developers are use to SSH keys, this was something new to me. So this is how I went about setting up my dev enviroment to use them. ### First we need to check for existing SSH keys 1. Open Terminal 2. Enter `ls -al ~./ssh` to see if existing SSH keys are present: ```bash $ ls -al ~/.ssh # Lists the files in your .ssh directory, if they exist ``` 3. Check the directory listing to see if you already have a public SSH key. By default, the filenames of the public keys are one of the following: - id_dsa.pub - id_ecdsa.pub - id_ed25519.pub - id_rsa.pub If you don't have an existing public and private key pair. Then we can Generate a new SSH key. ### Generating a new SSH key 1. Open Terminal 2. Paste the text below, substituting in your GitHub email address. `$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"` This creates a new ssh key, using the provided email as a label. `> This creates a new ssh key, using the provided email as a label.` 3. When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location. `Enter a file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter]` 4. At the prompt, type a secure passphrase. For more information, see ["Working with SSH key passphrases"](https://help.github.com/en/articles/working-with-ssh-key-passphrases). ```bash > Enter passphrase (empty for no passphrase): [Type a passphrase] > Enter same passphrase again: [Type passphrase again] ``` ### Now adding the SSH ket to the ssh-agent When adding your SSH key to the agent, use the default macOS `ssh-add` command!! 1. Start the ssh-agent in the background. ```bash $ eval "$(ssh-agent -s)" > Agent pid 59566 ``` 2. If you're using macOS Sierra 10.12.2 or later, you will need to modify your `~/.ssh/config` file to automatically load keys into the ssh-agent and store passphrases in your keychain. ```bash Host * AddKeysToAgent yes UseKeychain yes IdentityFile ~/.ssh/id_rsa ``` 3. Add your SSH private key to the ssh-agent and store your passphrase in the keychain. If you created your key with a different name, or if you are adding an existing key that has a different name, replace id_rsa in the command with the name of your private key file. `$ ssh-add -K ~/.ssh/id_rsa` **Note:** The `-K` option is Apple's standard version of ssh-add, which stores the passphrase in your keychain for you when you add an ssh key to the ssh-agent. Now its time to [Adding a new SSH key to your GitHub account](https://help.github.com/en/articles/adding-a-new-ssh-key-to-your-github-account) I have bascially ripped off the tutorial from [Help GitHub](https://help.github.com/) but this is mainly for my reference so I can have access to quickly. If you're using [BitBucket](https://bitbucket.org/) you can go [here](https://confluence.atlassian.com/bitbucket/set-up-an-ssh-key-728138079.html)
Published

Atlantic Night a VSC theme

After reading an [article](https://css-tricks.com/creating-a-vs-code-theme/ "css tricks article on creating a vac theme") created by Sarah Drasner on creating a theme for Visual Studio Code, I got inspired to create my own. I have used many themes in my time, the likes of [Dracula](https://draculatheme.com "Dracula Syntax theme"), [Material](https://material-theme.site "Material theme"), [Monokai](https://monokai.pro "Monokai Pro syntax theme") and [One Dark](https://binaryify.github.io/OneDark-Pro/#/ "One Dark syntax theme"). These are some popular themes, but there was always something I would want to change one of them. After being inspired by the colours of the Atlantic ocean during a boat cruise around the island of Antigua. I went on to create my theme for VSC and called it Atlantic Night. With coding at night in mind, the colour choices have been taking into consideration with accessibility in mind. All colours have been picked from [Color Safe](http://colorsafe.co "Color Safe website") which empowers designers with beautiful and accessible colour palettes based on [WCAG Guidelines](http://webaim.org/blog/wcag-2-0-and-link-colors/ "WCAG Guidelines") of text and background contrast ratios, these ratios have been tweaked slightly, however, the colour choice originates from Color Safe. With blue as my main base of colour, I then went on to use other colours. Such as green, purple, yellow and a couple more. This theme will ever be evolving I hope to get further input from the developer community to make the theme better. Even if that means rearranging the colour schemes for certain syntaxes. I tried to keep the VSC workspace all the same colour with slight changes to separate each section of the window. This was to keep my eyes concentrated on the code and not the workspace, I am currently typing this up in my garden, it's very sunny and bright. The workspace has almost turned to black but the syntax still pops from the screen, the colours I used and the contrast created helps this. Some people may say this theme is too similar to others, that might be the case. However, this is my theme. Which means I can change it to my needs whenever, but what is more important than that, is that its open-source and I get to share my little project with the developer community and also gather feedback to make it better. Below are some code examples of the languages Atlantic Night currently supports. More will be coming, and even a light version is in the works. The theme has also been listed as trending themes for VS Code that are growing in popularity for 2020 by [DEV](https://dev.to/thegeoffstevens/50-vs-code-themes-for-2020-45cc). Which I am very, very happy about! ### HTML ![HTML](//images.ctfassets.net/53u3hzg2egeu/6MfBzKRjbm7EfL0Mtj5xsA/2f5edae2e86b485caaa0592ac34d9764/HTML.png) ### CSS ![CSS](//images.ctfassets.net/53u3hzg2egeu/74pYAuwbPwcTQFMKHb4P3A/85f0d273f22598058b183ce89bdd02a0/CSS.png) ### JS ![JS](//images.ctfassets.net/53u3hzg2egeu/4lhgoLOxh7Y4CLHgAXaXXj/e538ee6260bc0b2bcd689f0bdb48f09b/JS.png) ### React ![REACT](//images.ctfassets.net/53u3hzg2egeu/6Pwid0eyBq4j93CWMtkjoU/638a01acfc1f0b30535bc53fff5978e2/REACT.png) ### JSON ![JSON](//images.ctfassets.net/53u3hzg2egeu/3FwAQnoXTwLdy7vGZZWKLE/00f254e0bc10ae10e75dc12286290bdc/JSON.png) ### Python ![PYTHON](//images.ctfassets.net/53u3hzg2egeu/5Rl6Aa9F56kSZ9r9DP9GUD/b3c998d3847896e544c5cd88cb76423b/PYTHON.png) ### MarkDown ![MD](//images.ctfassets.net/53u3hzg2egeu/5G40TSoKteMBTzFTooWRD8/aa09845fc4cb51cb3ebfac58c69f560e/MD.png) ### Rust ![RUST](//images.ctfassets.net/53u3hzg2egeu/5flnf7XoZJP8SQ7rRXSLgw/a012688fa320ac670443fd7fa529047b/RUST.png) ### SQL ![SQL](//images.ctfassets.net/53u3hzg2egeu/6dye9xJZWr2mEFcsbuQCMP/f83c1528fc61b39ef6b84ecc6e0ed42a/SQL.png) If you have any suggestions or ideas, please head over to the [repo and log an issue](https://github.com/mrpbennett/atlantic-night-vscode-theme/issues "Github Repo Issue"). Any help is welcome.
Published

Installing mySQL via Homebrew

I have recently started a new role as a Solutions Engineer, and it's time to dig back into SQL and learning my way through databases and how to query them. Therefore It's time to install [mySQL](https://www.mysql.com/) on my machine. I do this by using [Homebrew](https://brew.sh/ "homebrew"), the most awesome package manager for mac. Let's get started. ### Installing Homebrew If you haven't already it's time to get Homebrew installed. Open up your terminal of choice and paste this in and press return. ```bash /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" ``` This will then install homebrew allowing you to install packages. ### Installing mySQL and setup Now we have homebrew installed it is time to install mySQL to do that. Enter the following into the terminal and hit return. ```bash brew install mysql ``` This will go ahead and install mySQL for you. #### General Setup After installing mySQL homebrew will give a few options, one of them is allowing us to set the `root` password. Or simply just log in with a blank password by: ```bash mysql -uroot ``` I would suggest setting your root password, as it's good practice. #### Check that mySQL is installed Before moving on it's best to check that the install was successful. Time to start your mySQL server. ```bash mysql.server start ``` If you see SUCCESS! you know the install has been successful. Now It's time to log in with the root user. ```bash mysql -uroot ``` As the password is blank this should log us in successfully and present us with `mysql>` in the terminal. Let's exit the server by typing in `exit` and hitting return. #### Securing the installation To secure your installation of mySQL you enter the following `mysql_secure_installation` in the terminal. mySQL comes with a validation password plugin, but for this, we won't be using that. So when prompted just hit any key. Then enter your new password, this can be anything you like. Once set you're prompted with a few options, for me personally ill remove: - anonymous users - the ability for remote login - the test database Then I will reload the privileges. #### Creating our first local database Now we have secured our installation, it's time to create our first local database. For this database let's use the following creds: - user: test_user - password: test_password - database: test_database Of course, you're able to use anything you like, but If you're not feeling creative use the above. Let's set up our database, but first log into mySQL using the password your set above. ```bash mysql -uroot -pyourpassword ``` Once logged in, we can create our database ```bash mysql> CREATE DATABASE app_database; ``` Now let's create a user for the database ```bash mysql> CREATE USER 'test_user'@'localhost' IDENTIFIED BY 'test_password'; ``` Give the new user full access to the database ```bash mysql> GRANT ALL PRIVILEGES ON test_database.* TO 'test_user'@'localhost'; ``` Now flush all privileges ```bash mysql> FLUSH PRIVILEGES; ``` Let's exit our session by pressing `ctrl + d` #### Log into your new DB with new user Now everything is created let's try to log into `test_database` with user `test_user` ```bash mysql -utest_user -ptest_password ``` If successful we know everything has worked, and I can actually write a tutorial. Now let's try to use the `test_database` table. ```bash mysql> USE test_database ``` If you don't get an error we're all good, and everything is set up correctly. To exit the database just type in `exit`. #### Using a GUI to access and manage your db. If you want to manage your db via a gui such as [PopSQL](https://popsql.com/ "popsql") you can set it up by using what we have created above like so: ![Screenshot 2020-07-22 at 13.08.26](//images.ctfassets.net/53u3hzg2egeu/l0u7mbdK94rkDTUq3kAvw/974f2fe74319922fae65728fcc6a246c/Screenshot_2020-07-22_at_13.08.26.png) If you have any issues with this little tutorial, hit me up on Twitter.
Published

Using .env files within Python

I have been trying a lot of Node.js tutorials, putting my hands to learning a new skill and all that, especially during COVID-19. Using enviroment variables has always been easy in node especially with packages such as [dotenv](https://www.npmjs.com/package/dotenv) which allows getting your variables from your `.env` file with ease. ```javascript // .env DB_HOST=localhost ``` ```javascript // nodeFile.js require('dotenv').config() const db = host: process.env.DB_HOST, ``` But when using enviroment variables within Python, I have always found that a little tricky, mainly because if you're in an virtual enviroment and you set variables in that enviroment. Once you stop it, they're forgotten. This is of course unless you keep them in your `.zsh` file. But then if you upload your dotfiles to Github they're stored in the repo. Enter `python-dotenv` a sweet package from [Saurabh Kumar](https://github.com/theskumar) that allows you to use `.env` within your Python projects. Whilst I am working through node tutorials, I still like to brush up on my Python. Having a package like this has made my life a tone easier when it comes to managing keys. #### Let me walk you through how to get set up Install the latest version ```bash pip install -U python-dotenv ``` Assuming you have created the `.env` file along-side your settings module. ```bash . ├── .env └── settings.py ``` Add the following code to your `settings.py` ```python # settings.py from dotenv import load_dotenv load_dotenv() # OR, the same with increased verbosity load_dotenv(verbose=True) # OR, explicitly providing path to '.env' from pathlib import Path # python3 only env_path = Path('.') / '.env' load_dotenv(dotenv_path=env_path) ``` At this point, parsed key/value from the `.env` file is now present as system environment variable and they can be conveniently accessed via `os.getenv()`: ```python # app.py import os SECRET_KEY = os.getenv("EMAIL") DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD") ``` You can find more information about `python-dotenv` via the [repo](https://github.com/theskumar/python-dotenv/). I think this is a great package, and i'll be using it in all my Python development.
Published

Firing a tracking pixel on button click without GTM

As a solutions engineer in Adtech, sometimes we have to create ways to bend the will of the browser for our clients. One of our clients wanted to fire a tracking pixel on a button click. With [Google Tag Manager](https://marketingplatform.google.com/intl/en_uk/about/tag-manager/) this is very easy, you just use a custom trigger using a CSS selector. The client wasn't using Google Tag Manager (GTM) so I put this together. ```html <button id="unique_id">click me</button> <script type="text/javascript"> (() => { const b = document.getElementById('unique_id'); b.onclick = () => { const i = document.createElement('img'); i.src = '< your pixel src goes here >'; i.setAttribute('width', 0); i.setAttribute('height', 0); b.parentElement.appendChild(i); }; })(); </script> ``` ### Breakdown The first bit is pretty simple, this is out button. ```html <button id="unique_id">click me</button> ``` The next part is where the pixel gets appended to the body on button click where it's then fired. ```html <script type="text/javascript"> (() => { const b = document.getElementById('unique_id'); b.onclick = () => { const i = document.createElement('img'); i.src = '< your pixel src goes here >'; i.setAttribute('width', 0); i.setAttribute('height', 0); b.parentElement.appendChild(i); }; })(); </script> ``` I have set up anonymous function, which gets the button by its ID and stores it into the variable `b`. Then a click handler is added to `b` which allows us to do what we need to when the button is clicked. Once the button is clicked we create our `img` HTML element, we then set its height and width via `setAttribute` we set this to `0`. Then we append it to the parent element which is the HTML body. This is when the pixel is fired. All in all pretty simple, but a cool little way of achieving what the client wants when they were using GTM.
Published

A new journey into modern webdev

I am typing this currently unemployed. COVID-19 hasn't been the kindest of things for me and my partner, it's turned our life kinda upside down. Things are getting better but the job market sucks right now. Therefore I am going back to basics to solidify my skill set. Web development for me is currently only a hobby, this site shows that. I want to make it a full-time career though, at 35 I have it in my head that it's going to be hard. I suffer from imposter syndrome a lot and feel I currently don't have what it takes to become a junior frontend dev. So from today (Thursday 18th June), I am going to take myself on a journey through Udemy, and going through JS, React, Node and GraphQL. I built this site using a Youtube tutorial by [Andrew Mead](https://twitter.com/andrew_j_mead "tutor") on Gatsby who I found to be a great teacher, I am going through his courses which are: 1. [The Modern JavaScript Bootcamp](https://www.udemy.com/course/modern-javascript/ "modern js") 2. [The Complete React Developer Course (w/ Hooks and Redux)](https://www.udemy.com/course/react-2nd-edition/ "react course") 3. [The Complete Node.js Developer Course (3rd Edition)](https://www.udemy.com/course/the-complete-nodejs-developer-course-2/ "node js") 4. [The Modern GraphQL Bootcamp (with Node.js and Apollo) ](https://www.udemy.com/course/graphql-bootcamp/ "graphql") During these courses, I will continue to keep my [TIL repo](https://github.com/mrpbennett/TIL "til repo") updated with new learnings. I will also more than likely write some posts on my experiences also. I hope by the time I have finished these courses, I will have the skillset to become a web developer. Watch this space.
Published

Positioning an element to the bottom of its Parent

I have never remembered how to stick something to the bottom of its parent element. Just like the pinned repos on your Github profile page, how do you you keep the language of the repo fixed to the bottom? Well below ill tell you, I am using Tailwind 3.0 here but ill place examples below in plain css too. For me when I was trying to figure it out, I knew it was about positioning with CSS but what I always forgot was the Parents height. This needs to be 100% or in Tailwind terms `h-full`. You can see the main div is just an outer container with a border style, but the inner one is where I have gave a class of `h-full relative` to it, this gives the inner containiner (which holds everything) a height of 100% and a position of relative. Which in terms of MDN means *"The element is positioned according to the normal flow of the document, and then offset relative to itself based on the values of `top`, `right`, `bottom`, and `left`. The offset does not affect the position of any other elements; thus, the space given for the element in the page layout is the same as if position were static.*" More information [here](https://developer.mozilla.org/en-US/docs/Web/CSS/position "position") ```javascript <div id="project-outer-container" class="text-sm border-solid border border-slate-300 dark:border-slate-700 rounded shadow mb-4 md:mb-0 pb-4"> <div id="project-inner-container" class="p-4 h-full relative"> <div id="project-title" class="flex items-center"> <span class="mr-3 text-xl"> <GoRepo /> </span> <a href={r.node.url}>{r.node.name}</a> </div> <div id="project-about" class=""> <p class="">{r.node.description}</p> </div> <div id="project-lang" class="flex items-center absolute bottom-0"> <div style={langColor} class="rounded-full h-3 w-3"></div> <span class="ml-3">{r.node.primaryLanguage.name}</span> </div> </div> </div> ``` Now our inner container needs to be stuck to the bottom, so it doesn't float up if there is a less text within the project description. I have done this by adding a class of `absolute bottom-0` which will give it a position of absolute and fix it to the bottom of it's parent. ```javascript <div id="project-lang" class="flex items-center absolute bottom-0"> <div style={langColor} class="rounded-full h-3 w-3"></div> <span class="ml-3">{r.node.primaryLanguage.name}</span> </div> ``` That's pretty much it really, simple when you can remember it for sure. In it's simplest term the CSS would be like: ```css .parent { position: relative; height: 100%; } .child { position: absolute; bottom: 0; } ```
Published

Escaping e-mail hell

Can i say that this was first written by [Julie Zhuo](https://twitter.com/joulee) this is all her work...I just love the article and want to share it. ## How can I write more effective e-mails in less time? *Q: I spend way too much time writing e-mails. I tend to be very detail oriented, sharing tons of context and thoughts to my team and managers. But as a result, every night I stay up way too late writing long, multi-paragraph e-mails. It’s affecting my sleep and upsetting my wife. What’s worse, my long e-mails aren’t even effective. Sometimes I’ll run into someone days later and they’ll say “I saw your e-mail, but I haven’t had a chance to read it yet.” Can you help me escape e-mail hell?* Love it or hate it, e-mail is likely a major part of your job. Even with modern replacements like Workplace or Quip, asynchronous written communication is essential for sharing plans, providing feedback, and aligning teams. And as your team grows, you’ll have more people to connect with, and more and more e-mails to write and respond to. So, how can you stop having inbox-zero FOMO, get your points across succinctly, and save your marriage? Read on. ### Make sure you’re actually focused Before you can attempt to cut down your e-mails, let’s be sure the problem is actually that you’re spending too much time writing, and not that other distractions are holding you back. My recent article about staying motivated addresses this topic. All good there? Let’s continue. ### Build the skill of brevity Simply beating yourself up by saying “I WILL write shorter e-mails next time” isn’t going to work. You can’t just will yourself to change an ingrained habit. Like any skill, communication requires practice. So, acknowledge that this will take time and effort, but this investment in self-improvement will pay off in the future. Here’s a technique to practice. Take an e-mail you’ve already written in your normal fashion. Now, edit the e-mail down to half the words. This may involve removing some words or sentences, or you may just try re-writing the whole thing from scratch. Pay attention to what’s different between the longer and shorter versions. Which parts were actually essential, and which parts were extra cruft? I’ve practiced this with a speaking coach before, where she gave me 60 seconds to answer a question (e.g. “What is your team working on and why”). Then, I’d answer it again in only 30 seconds. I was stunned how each time, the 30 second answer was more focused and clear. ### Avoid squishy words Squishy words are your greatest enemy. Self-defeating phrases like “I feel”, “I’m not sure”, “perhaps”, using the passive voice, or pretty much any adverbs waste time for both you and your recipient, and muddle your point. Studies show that this “softer language” is more prevalent with women, so ladies be on the lookout. Learn to recognize the signs of squish. Here are examples for how to transform your text. *Example #1: Giving recommendations* - Before: “I’m not sure if I’ve fully considered all the pros and cons of each option, but based on what I currently know, I think option 2 is perhaps a good choice. That said, I’m willing to be convinced otherwise if you have a different perspective on this that you’d like to discuss.” - After: “My recommendation is option 2. How does that sound?” What changed: - This is an e-mail, not a stone tablet. It doesn’t matter how much soul-searching you’ve done to arrive at your recommendation. If you favor option 2, own it. - If you have a question, include a question mark. Asking a question as a long-winded phrase won’t make it clear that you want a response. - Before: 52 words. After: 9 words. *Example #2: Coordinating meetings* - Before: “I feel like it would be good meet again to discuss this topic further. I’m not sure if we’ll be able to meet up again while you are in town this week, but if we can’t make our schedules line up for us to be able to get together in-person before you head back to Seattle, then doing a video conference next week would be really appreciated.” - After: “Can we meet again? I’d prefer this week in-person, or a VC next week when you’re in Seattle.” *What changed:* - Unless you’re talking to venture capitalists, VC is a well understood acronym in the context of meetings. - The recipient knows in-person meetings happen when two people are in the same physical location. You don’t need to spell it out for them. - Before: 68 words. After: 18 words. *Example #3: Managing people.* - Before: “Regarding your observation that Jimmy seems to be a bit disconnected from the rest of the team, I think you might be right, and I really appreciate you pointing this out to me. After looking into this and thinking about for it a bit, I’m getting the impression that Jimmy might not have been receiving a lot of directive management, and instead he has been afforded quite a lot of freedom and flexibility in choosing his own projects and self-managing his own priorities. While this is working OK for both him and the team, Jimmy is still relatively junior, so he probably doesn’t always focus on the highest priority work. Moreover, I think he’s not actually getting a lot of input about whether or not his work could be improved. This probably isn’t an urgent problem, as he’s generally being pretty helpful, but I do think there’s an opportunity for Jimmy to be doing more valuable work that’s more tightly connected with the team. I think it would be helpful for him to receive more frequent direction on the things he should be focusing his time on, and also for him to start getting more feedback and follow-up on a regular basis on what seems to be working well, and what are some of the areas where things might be improved. So, that’s what I’m thinking about trying out with him. I’m going to give this a try in the coming weeks, and I will plan to let you know if this appears to be effective or not in improving Jimmy’s work moving forward in terms of being more well-connected with the rest of the team’s priorities, as well as his work being higher quality overall.” - After: “I agree Jimmy needs more direction and feedback. I’ll do this next week.” *What changed:* - Everything. And yet nothing. The exact same meaning was conveyed, except this time people will actually read it. - Before: 289 words. After: 13 words. ### Know your end before you start Rushing straight into typing without a clear idea of what you’re trying to say is risky. Start with your hands off the keyboard. Think about your intended outcome. What questions do you want to ask? What points do you want to make? Quickly type this outline in plainspoken language. Sentence fragments are fine. You can then flesh out these points if needed, but don’t wordsmith any more than is necessary. With practice, this outline actually IS your e-mail, and you can hit send in mere minutes without getting stuck down a rabbit-hole of endless revisions. ### Call out Action Items and names in bold If you’re buried in a pile of e-mail, odds are everyone else is as well. Make it easy for recipients to know exactly what you want them to do. If there are multiple people on the thread, and there’s someone in particular you want to reply, put their name in bold with it clearly spelled out what you expect from them, and by when. ### The almighty TL;DR Ideally, the whole e-mail is just the TL;DR (“too long; didn’t read”) section. But if more context will help frame what your e-mail is all about, or what the reader should take away from it or do next, start with your conclusions in a “tl;dr” section at the beginning, either as a single straightforward sentence (in bold) or some brief bullet points. ### Forwarding etiquette and the dreaded FYI As a corollary to the above, never forward along a massive e-mail chain without a TL;DR of why you’re sending this and what you want the recipient to get out of it, such as a quick summary of the parts to focus on, or an action item. - Before: “FYI. See below. Thoughts?” (Followed by a 20 message deep email involving a dozen people debating a contentious product decision. - After: “FYI. See below. Our design team wants to support B2C and C2C in a combined flow, but the sales team wants to prioritize B2C. Please help recommend how we can resolve this.” __Forwarding a huge thread with nothing more than “Thoughts?” at the start is the e-mail equivalent of a flaming bag of dog poop on your doorstep__ ### Apologize to your wife and get more sleep A happy marriage and plenty of rest is worth more than any e-mail, and will certainly help you be more effective at your job. Thanks for your question, and I wish you the best of luck changing your squishy ways as you venture forth towards the pearly gates of inbox-zero heaven. --- You can find the original article [here](https://medium.com/the-year-of-the-looking-glass/escaping-e-mail-hell-f55905f3862f) on Medium
Published
2022 No rights reserved.
3D Heart