Canonical Voices

Posts tagged with 'frontend'

Max

Upgrading my blog theme with the Vanilla CSS Framework

Recently I have spent more and more time thinking about the usability of my website and the cleanliness of its code.

This has brought me to another iteration in its design process.

Of course also related to my employment at Canonical I have decided to give our Vanilla Framework a try.

The first thing I had to update is my gulp task to include node_modules during the build.

//  CSS
gulp.task("css", function () {
  return gulp
    .src(`${source}/assets/css/styles.scss`)
    .pipe(sourcemaps.init())
    .pipe(sass({
      includePaths: ['node_modules']
    }).on('error', sass.logError))
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest("assets/built/"));
});

Otherwise it wouldnt pick up on the imports. This is also described on the framework page.

Then I have included the basic settings from Vanilla in my styles file with

@import "vanilla-framework/scss/settings";
@import "vanilla-framework/scss/base";
@include vf-base;

The @include is necessary because vanilla provides mixins via the imported files. This way I can control exactly what I want on my blog, thus reducing bloat and data usage.

Just by doing this, the website, given its semantic HTML already looks pretty nice:

Upgrading my blog theme with the Vanilla CSS Framework

One thing that I would still like to highlight though is the main content vs the head of the webpage. After browsing through the patterns at https://docs.vanillaframework.io/en/ for a bit I decided that I am only going to need the `p-card` pattern to achieve this.

After including this with @import "vanilla-framework/scss/patterns_card"; and using the needed mixins for the default card, the highlighted one and the content inside the card with

@include vf-p-card-default;
@include vf-p-card-highlighted;
@include vf-p-card-specific-content;

I just had to add classes to my articles on the index page and the content on the post page to achieve the following:

One thing that still bothers me here is the focus of the content on the left side. So I went ahead and adjusted some of the margins to the sides and on the header.

An additional thing I added (or actually removed) afterwards is the Font. If you set the $font-base-family variable before including the Vanilla base mixin, it will replace the default Ubuntu Font. As my goal is a very data saving, content focused website, I have replace it with serif, sans-serif. This will use the Fonts that are installed on the system in that order.

So, if for any reason, there is no Font found that has [serifs](https://en.wikipedia.org/wiki/Serif), a Font without serifs will be used instead.

Resume

In a few hours I was able to reach a design that is both clear and allows the user to focus on the content, which is written text. The Vanilla Framework, and the way it is set up, made it possible for me to focus on the things that are important to me and disregard the rest.

This way I can leverage all the design decisions around readability etc. that the Vanilla team has put into the framework and still have a bloat-free, brutalisitc website design to my liking, that represents me on the Internet.

This is a great speed improvement and asset for someone like me who appreciates great design, but spends a lot of time in the backend.

Read more
Max

Upgrading my blog theme with the Vanilla CSS Framework

Recently I have spent more and more time thinking about the usability of my website and the cleanliness of its code.

This has brought me to another iteration in its design process.

Of course also related to my employment at Canonical I have decided to give our Vanilla Framework a try.

The first thing I had to update is my gulp task to include node_modules during the build.

//  CSS
gulp.task("css", function () {
  return gulp
    .src(`${source}/assets/css/styles.scss`)
    .pipe(sourcemaps.init())
    .pipe(sass({
      includePaths: ['node_modules']
    }).on('error', sass.logError))
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest("assets/built/"));
});

Otherwise it wouldnt pick up on the imports. This is also described on the framework page.

Then I have included the basic settings from Vanilla in my styles file with

@import "vanilla-framework/scss/settings";
@import "vanilla-framework/scss/base";
@include vf-base;

The @include is necessary because vanilla provides mixins via the imported files. This way I can control exactly what I want on my blog, thus reducing bloat and data usage.

Just by doing this, the website, given its semantic HTML already looks pretty nice:

Upgrading my blog theme with the Vanilla CSS Framework

One thing that I would still like to highlight though is the main content vs the head of the webpage. After browsing through the patterns at https://docs.vanillaframework.io/en/ for a bit I decided that I am only going to need the `p-card` pattern to achieve this.

After including this with @import "vanilla-framework/scss/patterns_card"; and using the needed mixins for the default card, the highlighted one and the content inside the card with

@include vf-p-card-default;
@include vf-p-card-highlighted;
@include vf-p-card-specific-content;

I just had to add classes to my articles on the index page and the content on the post page to achieve the following:

One thing that still bothers me here is the focus of the content on the left side. So I went ahead and adjusted some of the margins to the sides and on the header.

An additional thing I added (or actually removed) afterwards is the Font. If you set the $font-base-family variable before including the Vanilla base mixin, it will replace the default Ubuntu Font. As my goal is a very data saving, content focused website, I have replace it with serif, sans-serif. This will use the Fonts that are installed on the system in that order.

So, if for any reason, there is no Font found that has [serifs](https://en.wikipedia.org/wiki/Serif), a Font without serifs will be used instead.

Resume

In a few hours I was able to reach a design that is both clear and allows the user to focus on the content, which is written text. The Vanilla Framework, and the way it is set up, made it possible for me to focus on the things that are important to me and disregard the rest.

This way I can leverage all the design decisions around readability etc. that the Vanilla team has put into the framework and still have a bloat-free, brutalisitc website design to my liking, that represents me on the Internet.

This is a great speed improvement and asset for someone like me who appreciates great design, but spends a lot of time in the backend.

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Ghost theme - Part 4: Finishing the Sidebar

On mobile Layouts < 360px width the Sidebar still overflows vertically. Lets fix it and implement the features decided on in Part 3.

First a small screenshot of the problem:

Ghost theme - Part 4: Finishing the Sidebar

As you can see the Sidebar flows into the other content.
To fix this lets remove some margins and change the nav to list its items horizontally.

All done with this bit of CSS in the styles.css file since we are changing the default layout.

.sidebar {
  grid-area: Sidebar;
  display: flex;
  flex-wrap: wrap;
  max-height: var(--sidebar-height);

  & .page-title {
      margin: 0px;
  }

  & .page-description {
      margin: 0px;
  }

  & .navigation {
    & .nav {
      display: flex;
      list-style: none;
      padding: 0px;
      margin: 0px;
    }
  }
}
@media screen and (min-width: 961px) {
    .sidebar {
        position: relative;
    }
}

This has actually become quite a lot to keep in the styles.css file.
Lets extract it into components/sidebar.css and import it.

All should look good now. Except when the content doesnt wrap!
Here is a screenshot of what I mean:

Ghost theme - Part 4: Finishing the Sidebar

Everything is in a row. The fix is quite easy with flexbox. Lets just set the flex-direction to column. That should fix it. Right?

Almost. The flex-wrap: wrap from earlier now messes things up. So lets just get rid of it on the sidebar. It worked great when the direction was row but now it is wrapping at the vertical ends, which is not what we want.

Now the sidebar looks pretty nice and our layout is almost done!

The features

Lets go on to the features:

  1. when scrolling the Title disappears
  2. when scrolling the Description disappears
  3. Only the Navigation is shown and the sidebar should shrink to its size but stay at the top

With position: sticky; the last one is pretty easy to do these days.
But to hide the other two we would need to restructure the HTML of our Sidebar since the position is relative to its parent. Which means it would still go out of the screen together with it.

So the last resort here is JavaScript together with position: fixed;. In general we should not use JavaScript for styling, but for this weird case lets make an exception and add a new class to the sidebar when it scrolls out of view.
Using that class we can then use the correct styles to achieve our goal and maybe add some nice CSS Animation.

Lets code

To use JavaScript create the file assets/js/helpers/styling.js.
The gotede build tools will take care of doing all the hard work of getting it onto the page, such as using babel and concatenating the JavaScript files you create.
Just take note that imports wont work. Since this is a Theme for purely Server-side rendered pages, that is totally fine. The JavaScript should be kept to a minimum here.

It should contain the following code, which will attach a new class to the Sidebar when we scroll down and then remove this class again once we scroll to the top:

function initScrollingListener() {
  const sidebar = document.getElementsByClassName("sidebar")[0];

  function addScrollClass() {
    if (window.pageYOffset >= 0) {
      sidebar.classList.add("scrolled");
      window.removeEventListener("scroll", addScrollClass);
      window.addEventListener("scroll", removeScrollClass);
    }
  }

  function removeScrollClass() {
    if (window.pageYOffset == 0) {
      sidebar.classList.remove("scrolled");
      window.removeEventListener("scroll", removeScrollClass);
      window.addEventListener("scroll", addScrollClass);
    }
  }

  if (window.pageYOffset == 0) {
    window.addEventListener("scroll", addScrollClass);
  } else {
    addScrollClass();
  }
}

initScrollingListener();

You should have some basic understanding of programming to really understand what is happening here but here is a small breakdown:

1.  If pageYOffset is 0 we are at the top, so we add a Listener that will call our addScrollClass function once we scroll. Otherwise we are not at the top and can call that function directly.
2. addScrollClass adds the class to the Sidebar element when we are not at the top of the page and the removes the Listener for calling itself on scrolling. Then it adds the Listener that calls removeScrollClass when we scroll.
3. removeScrollClass does exactly the opposite of addScrollClass.

Here is a list of all the DOM functionalities that are used in this piece of code: window, document, getElementsByClassName, window.pageYOffset, Element.classList, addEventListener, removeEventListener

Maybe you notice that we will call one of these functions every time that we scroll. In this case it is a very small amount of code, but it still has to run on the CPU, which is why we should generally avoid these kind of hacks.

Using the new class

So lets go and implement the things that should happen when we scroll in CSS.

.sidebar {
    /* ... */
    position: fixed;
    /* ... */
    &.scrolled{
        height: calc(var(--sidebar-height) / 1.5);
        width: var(--sidebar-width);

        & .page-title {
           display: none; 
        }

        & .page-description {
           display: none; 
        }
      }

This is all we need for now.
It should be inside the .sidebar block and hides both the description and the title when the scrolled class is added.

While it is not an optimal solution, since we use JavaScript for styling, it works in this case and does not put much load on the CPU since we only add two Listeners to our Window.

An Animation

To add some spiciness to our Sidebar suddenly reducing in size, we can add a small Animation when this happens.
Lets just make a dummy one for now where the Background transitions through colors. We can fine-tune this later when we got to the real colors of the theme.

  &.scrolled {
    /*...*/
    animation: background-transition 3s;
    background-color: yellow;
    z-index: 1;
  }
  /*...*/
  
  @keyframes background-transition {
    0% {
        background-color: blue;
    }
    100% {
        background-color: yellow;
    }
  }

One more thing is left to do.
While we have a nice Sidebar for a mobile layout these rules will break the Desktop design. Since we would have to overwrite all these rules in a MediaQuery lets make it easy on us and instead wrap these inside one themselves to keep the default on the other layouts.
To do this put all the CSS we just created for the scrolled class inside @media screen and (max-width: 960px) and dont forget to wrap them inside the .sidebar class as well.

The final sidebar.css should look like this:

.sidebar {
  grid-area: Sidebar;
  display: flex;
  flex-direction: column;
  max-height: var(--sidebar-height);

  position: fixed;
  top: 0;

  & .page-title {
      margin: 0px;
  }

  & .page-description {
      margin: 0px;
  }

  & .navigation {
    & .nav {
      display: flex;
      list-style: none;
      padding: 0px;
      margin: 0px;
    }
  }
}

@media screen and (max-width: 960px) {
    .sidebar {
        &.scrolled {
            height: calc(var(--sidebar-height) / 1.5);
            width: var(--sidebar-width);
            animation: background-transition 3s;
            background-color: yellow;
            z-index: 1;

        & .page-title {
            display: none; 
        }

        & .page-description {
            display: none; 
        }
        }
    }
}

@media screen and (min-width: 961px) {
    .sidebar {
        position: relative;
    }
}

@keyframes background-transition {
    0% {
        background-color: blue;
    }
    100% {
        background-color: yellow;
    }
}

The end

Here is a GIF of the new behaviour:

Ghost theme - Part 4: Finishing the Sidebar

Caught that Image that overflows our page? A Bug, how nice. I will fix it by next time. If you somehow followed along and want to do this as well, check out the class of such and element and try to find a setting that restricts its width ;)

Otherwise it looks like we are done here from a Basic Layouting point of view.
Of course there is still much needed on the visual front, which is what we will start in Part 6, where it is going to be all about Typography and choosing a Font.

Cover: https://unsplash.com/photos/SXihyA4oEJs

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Ghost theme - Part 4: Finishing the Sidebar

On mobile Layouts < 360px width the Sidebar still overflows vertically. Lets fix it and implement the features decided on in Part 3.

First a small screenshot of the problem:

Ghost theme - Part 4: Finishing the Sidebar

As you can see the Sidebar flows into the other content.
To fix this lets remove some margins and change the nav to list its items horizontally.

All done with this bit of CSS in the styles.css file since we are changing the default layout.

.sidebar {
  grid-area: Sidebar;
  display: flex;
  flex-wrap: wrap;
  max-height: var(--sidebar-height);

  & .page-title {
      margin: 0px;
  }

  & .page-description {
      margin: 0px;
  }

  & .navigation {
    & .nav {
      display: flex;
      list-style: none;
      padding: 0px;
      margin: 0px;
    }
  }
}
@media screen and (min-width: 961px) {
    .sidebar {
        position: relative;
    }
}

This has actually become quite a lot to keep in the styles.css file.
Lets extract it into components/sidebar.css and import it.

All should look good now. Except when the content doesnt wrap!
Here is a screenshot of what I mean:

Ghost theme - Part 4: Finishing the Sidebar

Everything is in a row. The fix is quite easy with flexbox. Lets just set the flex-direction to column. That should fix it. Right?

Almost. The flex-wrap: wrap from earlier now messes things up. So lets just get rid of it on the sidebar. It worked great when the direction was row but now it is wrapping at the vertical ends, which is not what we want.

Now the sidebar looks pretty nice and our layout is almost done!

The features

Lets go on to the features:

  1. when scrolling the Title disappears
  2. when scrolling the Description disappears
  3. Only the Navigation is shown and the sidebar should shrink to its size but stay at the top

With position: sticky; the last one is pretty easy to do these days.
But to hide the other two we would need to restructure the HTML of our Sidebar since the position is relative to its parent. Which means it would still go out of the screen together with it.

So the last resort here is JavaScript together with position: fixed;. In general we should not use JavaScript for styling, but for this weird case lets make an exception and add a new class to the sidebar when it scrolls out of view.
Using that class we can then use the correct styles to achieve our goal and maybe add some nice CSS Animation.

Lets code

To use JavaScript create the file assets/js/helpers/styling.js.
The gotede build tools will take care of doing all the hard work of getting it onto the page, such as using babel and concatenating the JavaScript files you create.
Just take note that imports wont work. Since this is a Theme for purely Server-side rendered pages, that is totally fine. The JavaScript should be kept to a minimum here.

It should contain the following code, which will attach a new class to the Sidebar when we scroll down and then remove this class again once we scroll to the top:

function initScrollingListener() {
  const sidebar = document.getElementsByClassName("sidebar")[0];

  function addScrollClass() {
    if (window.pageYOffset >= 0) {
      sidebar.classList.add("scrolled");
      window.removeEventListener("scroll", addScrollClass);
      window.addEventListener("scroll", removeScrollClass);
    }
  }

  function removeScrollClass() {
    if (window.pageYOffset == 0) {
      sidebar.classList.remove("scrolled");
      window.removeEventListener("scroll", removeScrollClass);
      window.addEventListener("scroll", addScrollClass);
    }
  }

  if (window.pageYOffset == 0) {
    window.addEventListener("scroll", addScrollClass);
  } else {
    addScrollClass();
  }
}

initScrollingListener();

You should have some basic understanding of programming to really understand what is happening here but here is a small breakdown:

1.  If pageYOffset is 0 we are at the top, so we add a Listener that will call our addScrollClass function once we scroll. Otherwise we are not at the top and can call that function directly.
2. addScrollClass adds the class to the Sidebar element when we are not at the top of the page and the removes the Listener for calling itself on scrolling. Then it adds the Listener that calls removeScrollClass when we scroll.
3. removeScrollClass does exactly the opposite of addScrollClass.

Here is a list of all the DOM functionalities that are used in this piece of code: window, document, getElementsByClassName, window.pageYOffset, Element.classList, addEventListener, removeEventListener

Maybe you notice that we will call one of these functions every time that we scroll. In this case it is a very small amount of code, but it still has to run on the CPU, which is why we should generally avoid these kind of hacks.

Using the new class

So lets go and implement the things that should happen when we scroll in CSS.

.sidebar {
    /* ... */
    position: fixed;
    /* ... */
    &.scrolled{
        height: calc(var(--sidebar-height) / 1.5);
        width: var(--sidebar-width);

        & .page-title {
           display: none; 
        }

        & .page-description {
           display: none; 
        }
      }

This is all we need for now.
It should be inside the .sidebar block and hides both the description and the title when the scrolled class is added.

While it is not an optimal solution, since we use JavaScript for styling, it works in this case and does not put much load on the CPU since we only add two Listeners to our Window.

An Animation

To add some spiciness to our Sidebar suddenly reducing in size, we can add a small Animation when this happens.
Lets just make a dummy one for now where the Background transitions through colors. We can fine-tune this later when we got to the real colors of the theme.

  &.scrolled {
    /*...*/
    animation: background-transition 3s;
    background-color: yellow;
    z-index: 1;
  }
  /*...*/
  
  @keyframes background-transition {
    0% {
        background-color: blue;
    }
    100% {
        background-color: yellow;
    }
  }

One more thing is left to do.
While we have a nice Sidebar for a mobile layout these rules will break the Desktop design. Since we would have to overwrite all these rules in a MediaQuery lets make it easy on us and instead wrap these inside one themselves to keep the default on the other layouts.
To do this put all the CSS we just created for the scrolled class inside @media screen and (max-width: 960px) and dont forget to wrap them inside the .sidebar class as well.

The final sidebar.css should look like this:

.sidebar {
  grid-area: Sidebar;
  display: flex;
  flex-direction: column;
  max-height: var(--sidebar-height);

  position: fixed;
  top: 0;

  & .page-title {
      margin: 0px;
  }

  & .page-description {
      margin: 0px;
  }

  & .navigation {
    & .nav {
      display: flex;
      list-style: none;
      padding: 0px;
      margin: 0px;
    }
  }
}

@media screen and (max-width: 960px) {
    .sidebar {
        &.scrolled {
            height: calc(var(--sidebar-height) / 1.5);
            width: var(--sidebar-width);
            animation: background-transition 3s;
            background-color: yellow;
            z-index: 1;

        & .page-title {
            display: none; 
        }

        & .page-description {
            display: none; 
        }
        }
    }
}

@media screen and (min-width: 961px) {
    .sidebar {
        position: relative;
    }
}

@keyframes background-transition {
    0% {
        background-color: blue;
    }
    100% {
        background-color: yellow;
    }
}

The end

Here is a GIF of the new behaviour:

Ghost theme - Part 4: Finishing the Sidebar

Caught that Image that overflows our page? A Bug, how nice. I will fix it by next time. If you somehow followed along and want to do this as well, check out the class of such and element and try to find a setting that restricts its width ;)

Otherwise it looks like we are done here from a Basic Layouting point of view.
Of course there is still much needed on the visual front, which is what we will start in Part 6, where it is going to be all about Typography and choosing a Font.

Cover: https://unsplash.com/photos/SXihyA4oEJs

Read more
Max

CSS Modules: the correct code split

There is a lot of talk going on in the JavaScript Frontend Community about using CSSinJS. One example for this are styled-components.
A use of them is explained over at https://www.toptal.com/javascript/styled-components-library .
In this Article I want to name an alternative and talk about some benefits.

What are CSS modules?

According to the css-modules github repo:

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.

For use in React or Vue f.e. this would mean:
All of the styles inside of a CSS Module will not affect anything else than the Component on which they are imported in.

Using CSS modules

Lets try this out with a a bit of React.
First we write a Box Component like this:

import React from 'react';
import styles from './Box.css';

const Box = () => <div />;

export default Box;

And apply some styles to it:

import React from 'react';
import styles from './Box.css';

const Box = () => <div className={styles.box} />;

export default Box;

Notice how we just import some CSS file into our JavaScript. This is where the magic happens.
In this case the CSS modularization happens through the webpack css-loader.

This is the Box.css file that gets imported.

.box {
    width: 400px;
    height: 300px;
    border-bottom: solid brown 4px;
    border-right: solid brown 4px;
    border-left: solid brown 4px;
}

we can check out the browser to see what the result looks like.

CSS Modules: the correct code split

Notice the class name? This is how CSS Modules achieve the local scoping. The imported class will be added on the correct Components and in the process their names will be changed and a unique hash added.
This will prevent any collisions with rules of other components.

No collision

Lets try this out in the browser.
Here are 2 components that use the same class name in their CSS:

Banana Component

import React from 'react';
import styles from './Banana.css';

const Banana = () => (
  <div className={styles.container}>
    <p className={styles.text}>Banana</p>
  </div>
);

export default Banana;
.container {
    display: flex;
    justify-content: center;
    align-items: center;
    border: solid 1px yellow;
}

.text {
    color: yellow;
}

Apple Component

import React from 'react';
import styles from './Apple.css';

const Apple = () => (
  <div className={styles.container}>
    <p className={styles.text}>Apple</p>
  </div>
);

export default Apple;
.container {
    display: flex;
    justify-content: center;
    align-items: center;
    border: solid 1px green;
}

.text {
    color: green;
}

Both of the components use the same classes inside of them. If we would write normal CSS this should lead to collisions, meaning that one set of rules would overwrite the other.
But checking the website again we can see that everything is normal.
Take note of the hashed class names again.

CSS Modules: the correct code split

All collisions are avoided by making the class names unique. Similar to techniques such as BEM, just in an automated way.

Conclusion

This was a very brief introduction to CSS Modules but should give a good understanding of what they do and how they work.
Some of the features are similar to techniques such as styled-components, but by being just CSS files we can benefit from some advantages.
Lets dive into the Pros and Cons to wrap it up:

Pros

  • Designers do not need to learn new ways of writing CSS in JS. Instead they can easily make changes to the correct CSS files. This way of styling, combined with mental models such a atomic design, can increase communication between Developers and Designers and development speed.
  • Separation of Concerns: We hear this all the time in Web Development and it was regarded as best practice for a long time. Given JSXs popularity one might argue that this is not true anymore, but we should still aim for it.
  • Tools and Preprocessors such as PostCss can be easily integrated to allow CSS Imports, Custom Properties etc.

Cons

  • The build task might be complicated to set up at first
  • Global styles have to be wrapped in a special :global block. Where would such a block belong?
  • The power of JavaScript is not available inside the CSS files

Cover Image is from https://unsplash.com/photos/8EzNkvLQosk

Read more
Max

CSS Modules: the correct code split

There is a lot of talk going on in the JavaScript Frontend Community about using CSSinJS. One example for this are styled-components.
A use of them is explained over at https://www.toptal.com/javascript/styled-components-library .
In this Article I want to name an alternative and talk about some benefits.

What are CSS modules?

According to the css-modules github repo:

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.

For use in React or Vue f.e. this would mean:
All of the styles inside of a CSS Module will not affect anything else than the Component on which they are imported in.

Using CSS modules

Lets try this out with a a bit of React.
First we write a Box Component like this:

import React from 'react';
import styles from './Box.css';

const Box = () => <div />;

export default Box;

And apply some styles to it:

import React from 'react';
import styles from './Box.css';

const Box = () => <div className={styles.box} />;

export default Box;

Notice how we just import some CSS file into our JavaScript. This is where the magic happens.
In this case the CSS modularization happens through the webpack css-loader.

This is the Box.css file that gets imported.

.box {
    width: 400px;
    height: 300px;
    border-bottom: solid brown 4px;
    border-right: solid brown 4px;
    border-left: solid brown 4px;
}

we can check out the browser to see what the result looks like.

CSS Modules: the correct code split

Notice the class name? This is how CSS Modules achieve the local scoping. The imported class will be added on the correct Components and in the process their names will be changed and a unique hash added.
This will prevent any collisions with rules of other components.

No collision

Lets try this out in the browser.
Here are 2 components that use the same class name in their CSS:

Banana Component

import React from 'react';
import styles from './Banana.css';

const Banana = () => (
  <div className={styles.container}>
    <p className={styles.text}>Banana</p>
  </div>
);

export default Banana;
.container {
    display: flex;
    justify-content: center;
    align-items: center;
    border: solid 1px yellow;
}

.text {
    color: yellow;
}

Apple Component

import React from 'react';
import styles from './Apple.css';

const Apple = () => (
  <div className={styles.container}>
    <p className={styles.text}>Apple</p>
  </div>
);

export default Apple;
.container {
    display: flex;
    justify-content: center;
    align-items: center;
    border: solid 1px green;
}

.text {
    color: green;
}

Both of the components use the same classes inside of them. If we would write normal CSS this should lead to collisions, meaning that one set of rules would overwrite the other.
But checking the website again we can see that everything is normal.
Take note of the hashed class names again.

CSS Modules: the correct code split

All collisions are avoided by making the class names unique. Similar to techniques such as BEM, just in an automated way.

Conclusion

This was a very brief introduction to CSS Modules but should give a good understanding of what they do and how they work.
Some of the features are similar to techniques such as styled-components, but by being just CSS files we can benefit from some advantages.
Lets dive into the Pros and Cons to wrap it up:

Pros

  • Designers do not need to learn new ways of writing CSS in JS. Instead they can easily make changes to the correct CSS files. This way of styling, combined with mental models such a atomic design, can increase communication between Developers and Designers and development speed.
  • Separation of Concerns: We hear this all the time in Web Development and it was regarded as best practice for a long time. Given JSXs popularity one might argue that this is not true anymore, but we should still aim for it.
  • Tools and Preprocessors such as PostCss can be easily integrated to allow CSS Imports, Custom Properties etc.

Cons

  • The build task might be complicated to set up at first
  • Global styles have to be wrapped in a special :global block. Where would such a block belong?
  • The power of JavaScript is not available inside the CSS files

Cover Image is from https://unsplash.com/photos/8EzNkvLQosk

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Ghost theme - Part 3: Mobile Layout

Here is the third part of this series where we will dive into creating the proper layout for the posts on the front page and figure out a way to display our layout on mobilephones.

Just in case lets bring up the inital sketch again

Ghost theme - Part 3: Mobile Layout

But before we can jump in and take care of the posts we have a small problem.
Using the mobile page preview of the browser we can easily spot it.

Ghost theme - Part 3: Mobile Layout

The content inside the sidebar does not fit inside the container anymore. Unfortunately max-width does not help here, as the words are actually too long and the browser cant wrap the text to the next line.

So its time to come up with a new sketch:

Ghost theme - Part 3: Mobile Layout

You have probably encountered this technique before. Here is a break down:

  1. We will make the sidebar go to the top on mobile devices.
  2. The Title and subtitle will scroll out of the screen
  3. The navigation elements will stay on top

Implementation

Responsiveness

The first thing I am going to do is to convert the whole layout to this idea.
This 'old' design will be wrapped via media-queries that activate when a certain screen-size is reached.
This way of doing it is called mobile first.
Since this theme is in an early stage of development it is still easily possible to switch to this approach.

This is the updated CSS:

variables.css

:root {
  /* Container sizes */
  --main-width: 100vw;
  --main-height: 80vh;

  --sidebar-width: 100vw;
  --sidebar-height: 20vh;
}

@media screen and (min-width: 961px) {
  :root {
    /* Container sizes */
    --main-width: 80vw;
    --main-height: 100%;

    --sidebar-width: 20vw;
    --sidebar-height: 100vh;
  }
}

styles.css

@import "./helpers/variables.css";

/* Mobile */
body {
  margin: 0px;
}

.grid-container {
  display: grid;
  height: 100%;
  grid-template-columns: 1fr;
  grid-template-rows: var(--sidebar-height) var(--main-height);
  grid-gap: 0px;
  grid-template-areas: "Sidebar" "Content";
}

.main {
  grid-area: Content;
  max-width: var(--main-width);
}

.sidebar {
  grid-area: Sidebar;
}


/* Desktop */
@media screen and (min-width: 961px) {
  .grid-container {
    display: grid;
    height: 100%;
    grid-template-columns: var(--sidebar-width) var(--main-width);
    grid-template-rows: 1fr;
    grid-gap: 0px;
    grid-template-areas: "Sidebar Content";
  }
  .main {
    grid-area: Content;
    max-width: var(--main-width);
  }
}

Et voila, we have a responsive page.

Mobile:
Ghost theme - Part 3: Mobile Layout

Desktop:
Ghost theme - Part 3: Mobile Layout

The Sidebar

As you can see the content of the Sidebar seems to escape into regions it shouldnt.

To fix this lets first change the default.hbs and remove the <div class="sidebar> and attach that class directly to the <header>.

Back in the styles.css we can now add some rules.
I am going to go for a flexbox solution.

 .sidebar {
  grid-area: Sidebar;
  display: flex;
  flex-wrap: wrap;
  max-height: var(--sidebar-height);
}
  1. Tell the component that its display type is flexbox.
  2. Make sure that the content will wrap when it reaches the bounds of this container
  3. Set a maximum height on the sidebar. To make sure it doesnt grow beyond what we would like to have in our design. This is espacially useful when the sidebar is actually at the side.

Lets leave the additional requirements of hiding the title and subtitle for the next post.

The posts

Lastly its time to implement the post layout.
On the mobile front everything looks good already. One post per row.

For the desktop we need to find a way to display 2 posts next to each other though.

First I will create a new file components/index.css that will hold all the styles for the Index page. All the styling until now was for the default.hbs.
Also do not forget to import this file in the styles.css via @import "./components/index.css";.

Here is the CSS that will accomplish the task:

.index__posts {
  display: flex;
  flex-wrap: wrap;

  & .post {
    width: var(--post-width);
  }
}

You can see that the .post class is nested inside the .index__posts class. This again is taken care of during the build step via postcss-preset-env.

The --post-width variable is inside the variables.css file and looks like this:

/* .... */
:root {
  --post-width: var(--main-width);
}

@media screen and (min-width: 961px) {
  :root {
    /* .... */
    --post-width: calc(var(--main-width) / 2);
  }

This works in the following way:

  1. When the Screen is 961px wide or bigger every post will have the width of half the --main-width.
  2. Posts are inside a flexbox container
  3. Since the flexbox container does not wrap by default and instead rezises our posts to fit inside of it, we tell it to wrap the content.
  4. 2 posts will be shown per 'row' :)

The one thing I have not mentioned until now is the CSS calc function, which allows us to dynamically calculate some values.

Wrap up

Phew, this post had quite a lot of content and advanced techniques. Nevertheless the theme is now in a good state with relatively little CSS and should be pretty maintainable and extendable.

The neat little trick with the custom-properties is something that I have added to postcss-custom-properties while writing this, so it might not yet be released. I will update this post once it is.

Here is a final image of the Desktop page, looks pretty good already :

Ghost theme - Part 3: Mobile Layout

Next post

Whats going to happen in part 4?
Lets fix the Sidebar for mobile layouts. Afterwards in part 5 the Basics should be done to start with Typography and Colours :)

Cover: https://unsplash.com/photos/-1_RZL8BGBM

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Here is the third part of this series where we will dive into creating the proper layout for the posts on the front page and figure out a way to display our layout on mobilephones.

Just in case lets bring up the inital sketch again

Sketch_theme-2

But before we can jump in and take care of the posts we have a small problem.
Using the mobile page preview of the browser we can easily spot it.

mobile_view_broken

The content inside the sidebar does not fit inside the container anymore. Unfortunately max-width does not help here, as the words are actually too long and the browser cant wrap the text to the next line.

So its time to come up with a new sketch:

mobile_sketch-1

You have probably encountered this technique before. Here is a break down:

  1. We will make the sidebar go to the top on mobile devices.
  2. The Title and subtitle will scroll out of the screen
  3. The navigation elements will stay on top

Implementation

Responsiveness

The first thing I am going to do is to convert the whole layout to this idea.
This 'old' design will be wrapped via media-queries that activate when a certain screen-size is reached.
This way of doing it is called mobile first.
Since this theme is in an early stage of development it is still easily possible to switch to this approach.

This is the updated CSS:

variables.css

:root {
  /* Container sizes */
  --main-width: 100vw;
  --main-height: 80vh;

  --sidebar-width: 100vw;
  --sidebar-height: 20vh;
}

@media screen and (min-width: 961px) {
  :root {
    /* Container sizes */
    --main-width: 80vw;
    --main-height: 100%;

    --sidebar-width: 20vw;
    --sidebar-height: 100vh;
  }
}

styles.css

@import "./helpers/variables.css";

/* Mobile */
body {
  margin: 0px;
}

.grid-container {
  display: grid;
  height: 100%;
  grid-template-columns: 1fr;
  grid-template-rows: var(--sidebar-height) var(--main-height);
  grid-gap: 0px;
  grid-template-areas: "Sidebar" "Content";
}

.main {
  grid-area: Content;
  max-width: var(--main-width);
}

.sidebar {
  grid-area: Sidebar;
}


/* Desktop */
@media screen and (min-width: 961px) {
  .grid-container {
    display: grid;
    height: 100%;
    grid-template-columns: var(--sidebar-width) var(--main-width);
    grid-template-rows: 1fr;
    grid-gap: 0px;
    grid-template-areas: "Sidebar Content";
  }
  .main {
    grid-area: Content;
    max-width: var(--main-width);
  }
}

Et voila, we  have a responsive page.

mobile_resp
desktop_resp

The Sidebar

As you can see the content of the Sidebar seems to escape into regions it shouldnt.

To fix this lets first change the default.hbs and remove the <div class="sidebar> and attach that class directly to the <header>.

Back in the styles.css we can now add some rules.
I am going to go for a flexbox solution.

 .sidebar {
  grid-area: Sidebar;
  display: flex;
  flex-wrap: wrap;
  max-height: var(--sidebar-height);
}
  1. Tell the component that its display type is flexbox.
  2. Make sure that the content will wrap when it reaches the bounds of this container
  3. Set a maximum height on the sidebar. To make sure it doesnt grow beyond what we would like to have in our design. This is espacially useful when the sidebar is actually at the side.

Lets leave the additional requirements of hiding the title and subtitle for the next post.

The posts

Lastly its time to implement the post layout.
On the mobile front everything looks good already. One post per row.

For the desktop we need to find a way to display 2 posts next to each other though.

First I will create a new file  components/index.css that will hold all the styles for the Index page. All the styling until now was for the default.hbs.
Also do not forget to import this file in the styles.css via @import "./components/index.css";.

Here is the CSS that will accomplish the task:

.index__posts {
  display: flex;
  flex-wrap: wrap;

  & .post {
    width: var(--post-width);
  }
}

You can see that the .post class is nested inside the .index__posts class. This again is taken care of during the build step via postcss-preset-env.

The --post-width variable is inside the variables.css file and looks like this:

/* .... */
:root {
  --post-width: var(--main-width);
}

@media screen and (min-width: 961px) {
  :root {
    /* .... */
    --post-width: calc(var(--main-width) / 2);
  }

This works in the following way:

  1. When the Screen is 961px wide or bigger every post will have the width of half the --main-width.
  2. Posts are inside a flexbox container
  3. Since the flexbox container does not wrap by default and instead rezises our posts to fit inside of it, we tell it to wrap the content.
  4. 2 posts will be shown per 'row' :)

The one thing I have not mentioned until now is the CSS calc function, which allows us to dynamically calculate some values.

Wrap up

Phew, this post had quite a lot of content and advanced techniques. Nevertheless the theme is now in a good state with relatively little CSS and should be pretty maintainable and extendable.

The neat little trick with the custom-properties is something that I have added to postcss-custom-properties while writing this, so it might not yet be released. I will update this post once it is.

Here is a final image of the Desktop page, looks pretty good already :

desktop_done

Next post

Whats going to happen in part 4?
Lets fix the Sidebar for mobile layouts. Afterwards in part 5 the Basics should be done to start with Typography and Colours :)

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Ghost theme - Part 2: a Grid

In this post I want to start with the styling.
the first thing here is to consult the sketches again.

Ghost theme - Part 2: a Grid

Since we split out the sidebar into the default layout, we should create a Grid for this first.
The technology I am going to use here is CSS-Grid.

The only to be aware of is the browser support. Check caniuse.com to see if it supports the browsers you want.
Since this is my personal blog and the audience is probably using modern browsers (this is an assumption, as I have no tracking yet) I am totally fine with ignoring older and more exotic browsers for now.

Grid

With tools like https://www.layoutit.com/grid we can quickly generate a basic layout.

The generated code for the grid I want on the blog according to the sketches looks like this

.grid-container {
  display: grid;
  height: 100%;
  grid-template-columns: 20vw 80vw;
  grid-template-rows: 1fr;
  grid-gap: 0px;
  grid-template-areas: "Sidebar Content";
}

.main{ grid-area: Content; }

.sidebar { grid-area: Sidebar; }

This will have to go into a css file at assets/css/styles.css in the theme folder.

Make sure to restart the `npm run start` task, so that gulp can pick up on the newly created `.css` file. Unfortunately this is not detected automatically.

In the default.hbs this stylesheet now needs to be imported and the correct classes need to be added to the HTML in the like following:


<head>
...
<link rel="stylesheet" type="text/css" href="{{asset "built/styles.css"}}" />

</head>

<body class="{{body_class}} grid-container">
    <div class="sidebar">
        <header>
            <h1 class="page-title">{{@blog.title}}</h1>
            <h2 class="page-description">{{@blog.description}}</h2>
            <nav class="navigation">
                {{navigation}}
            </nav>
        </header>
    </div>
    <div class="main">
        {{{body}}}
    </div>
    {{ghost_foot}}
</body>

The <body> is acting as the Grid container, and the sidebar and main where already equipped with the correct classes.

With this simple addition the page already looks like this:

Ghost theme - Part 2: a Grid

Variables

Now we have a good base to start introducing variables in CSS.
We can use these due to the postcss-custom-properties that are included in the default gotede gulp task.

What this will allow us is to define certain variables that should be applied throughout our app and then reuse them whenever needed.

By doing this we introduce 2 benefits.

  1. We can easily make a change in 1 place that will be applied everywhere that value was used
  2. There is 1 place to check in order to find out what specific values are

The place to define those is f.e. assets/css/helpers/variables.css, so lets create some, that we can use for our grid.

:root {
  /* Container sizes */
  --main-width: 80vw;
  --sidebar-width: 20vw;

  /* Gutter sizes */
  --gutter-horizontal--small: 2vw;
}

Notice that they are wrapped in the :root{} block.

Once this is done the gulp task will automatically make those 'available' in all our CSS files (This is actually done in the build step, but thats semantics I guess).

Lets import them and use them for the grid like this:


@import "./helpers/variables.css";


.grid-container {
  display: grid;
  height: 100%;
  grid-template-columns: var(--sidebar-width) var(--main-width);
  grid-template-rows: 1fr;
  grid-gap: 0px;
  grid-template-areas: "Sidebar Content";
}

.main {
  grid-area: Content;
}

.sidebar {
  grid-area: Sidebar;
}

You can see that the variables are used with var(VARIABLE_NAME). While this might not seem super important or useful right now, be sure that this will save a lot of headaches down the line.

A break in the layout

There is a little weirdness going on when we head over to the Writing posts with Ghost for example.
You can see that something is breaking on mobile devices. The content seems to escape the website.
Ghost theme - Part 2: a Grid

To fix this lets set a max-width on the main container. This does exactly what the name implies, telling it that its maximum width should be of some value.
Which one is the correct value? Given our Layout it should be the --main-width variable. Already comming in handy :).

Lets add it to the .main block

.main {
  grid-area: Content;
  max-width: var(--main-width);
}

Check out the post again. All good?
Nope, looks like the picture is still escaping to the right.

If you inspect the HTML you can find the picture class .kg-image. For this class we will add a rule of max-width: 100%; for now, until we encounter the styling of the pictures again, later on.

Now it looks good again:
Ghost theme - Part 2: a Grid

By the way, you can ignore the purple lines.
These are added by the Firefox developer tools. You can read about these here if you would like to use them too.

A small task for you

Check out the Publishing options article. On this page you should encounter another problem of content escaping.
I will leave this as a small task for you to fix right now. Should you need help or a hint feel free to drop me a short email or contact me on my new twitter.

What next?

The theme already looks quite a bit like the sketch. One thing we need to ensure though, is that there are always only 2 posts next to each other (and maybe alter this idea for mobile devices?).

So the next post will be focussing on those 2 topics including media-queries and flexbox.

Cover: https://unsplash.com/photos/Vwf8q3RzBRE

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Ghost theme - Part 2: a Grid

In this post I want to start with the styling.
the first thing here is to consult the sketches again.

Ghost theme - Part 2: a Grid

Since we split out the sidebar into the default layout, we should create a Grid for this first.
The technology I am going to use here is CSS-Grid.

The only to be aware of is the browser support. Check caniuse.com to see if it supports the browsers you want.
Since this is my personal blog and the audience is probably using modern browsers (this is an assumption, as I have no tracking yet) I am totally fine with ignoring older and more exotic browsers for now.

Grid

With tools like https://www.layoutit.com/grid we can quickly generate a basic layout.

The generated code for the grid I want on the blog according to the sketches looks like this

.grid-container {
  display: grid;
  height: 100%;
  grid-template-columns: 20vw 80vw;
  grid-template-rows: 1fr;
  grid-gap: 0px;
  grid-template-areas: "Sidebar Content";
}

.main{ grid-area: Content; }

.sidebar { grid-area: Sidebar; }

This will have to go into a css file at assets/css/styles.css in the theme folder.

Make sure to restart the `npm run start` task, so that gulp can pick up on the newly created `.css` file. Unfortunately this is not detected automatically.

In the default.hbs this stylesheet now needs to be imported and the correct classes need to be added to the HTML in the  like following:


<head>
...
<link rel="stylesheet" type="text/css" href="{{asset "built/styles.css"}}" />

</head>

<body class="{{body_class}} grid-container">
    <div class="sidebar">
        <header>
            <h1 class="page-title">{{@blog.title}}</h1>
            <h2 class="page-description">{{@blog.description}}</h2>
            <nav class="navigation">
                {{navigation}}
            </nav>
        </header>
    </div>
    <div class="main">
        {{{body}}}
    </div>
    {{ghost_foot}}
</body>

The <body> is acting as the Grid container, and the sidebar and main where already equipped with the correct classes.

With this simple addition the page already looks like this:

Ghost theme - Part 2: a Grid

Variables

Now we have a good base to start introducing variables in CSS.
We can use these due to the postcss-custom-properties that are included in the default gotede gulp task.

What this will allow us is to define certain variables that should be applied throughout our app and then reuse them whenever needed.

By doing this we introduce 2 benefits.

  1. We can easily make a change in 1 place that will be applied everywhere that value was used
  2. There is 1 place to check in order to find out what specific values are

The place to define those is f.e. assets/css/helpers/variables.css, so lets create some, that we can use for our grid.

:root {
  /* Container sizes */
  --main-width: 80vw;
  --sidebar-width: 20vw;

  /* Gutter sizes */
  --gutter-horizontal--small: 2vw;
}

Notice that they are wrapped in the :root{} block.

Once this is done the gulp task will automatically make those 'available' in all our CSS files (This is actually done in the build step, but thats semantics I guess).

Lets import them and use them for the grid like this:


@import "./helpers/variables.css";


.grid-container {
  display: grid;
  height: 100%;
  grid-template-columns: var(--sidebar-width) var(--main-width);
  grid-template-rows: 1fr;
  grid-gap: 0px;
  grid-template-areas: "Sidebar Content";
}

.main {
  grid-area: Content;
}

.sidebar {
  grid-area: Sidebar;
}

You can see that the variables are used with var(VARIABLE_NAME). While this might not seem super important or useful right now, be sure that this will save a lot of headaches down the line.

A break in the layout

Ghost theme - Part 2: a Grid

To fix this lets set a max-width on the main container. This does exactly what the name implies, telling it that its maximum width should be of some value.
Which one is the correct value? Given our Layout it should be the --main-width variable. Already comming in handy :).

Lets add it to the .main block

.main {
  grid-area: Content;
  max-width: var(--main-width);
}

Check out the post again. All good?
Nope, looks like the picture is still escaping to the right.

If you inspect the HTML you can find the picture class .kg-image. For this class we will add a rule of max-width: 100%; for now, until we encounter the styling of the pictures again, later on.

Ghost theme - Part 2: a Grid

By the way, you can ignore the purple lines.
These are added by the Firefox developer tools. You can read about these here if you would like to use them too.

A small task for you

Check out the Publishing options article. On this page you should encounter another problem of content escaping.
I will leave this as a small task for you to fix right now. Should you need help or a hint feel free to drop me a short email or contact me on my new twitter.

What next?

The theme already looks quite a bit like the sketch. One thing we need to ensure though, is that there are always only 2 posts next to each other (and maybe alter this idea for mobile devices?).

So the next post will be focussing on those 2 topics including media-queries and flexbox.

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Creating a new ghost theme - Part 1

I want to make my blog more unique and fitting to my style both in code and in looks.
Writing my own theme seems like the right thing to do, given that I am currently looking for a job and having both more free time and wanting to look good to possible employers.

So I want to accompany this with a few blog posts on how I am going about this.

All of this will be done incrementally and I will set the theme live, so you can give me some feedback if you can/want. The best way to do this is on Twitter or at max [at] ehlers . berlin.

The development environment

The first thing that I need is a development environment. I wrote this article about a small script I wrote to help with this.

The homepage/index

When you come to this blog via its basic URL the first thing you are going to be greeted with is the index.

So this is also the first thing that I want to work on.

Here is a small sketch I made to get started:

Creating a new ghost theme - Part 1

Now I implemented all the things needed according to the Documentation in the index.hbs of my theme.

It now looks like this

<header>
  <h1 class="page-title">{{@blog.title}}</h1>
  <h2 class="page-description">{{@blog.description}}</h2>
  <nav class="navigation">
    {{navigation}}
  </nav>
</header>

<main role="main">
  {{#foreach posts}}
	<article class="{{post_class}}">
 		<header class="post-header">
   		<h2><a href="{{url}}">{{title}}</a></h2>
    </header>
    <section class="post-excerpt">
 			<p>{{excerpt words="26"}} <a class="read-more" href="{{url}}">...</a></p>
    </section>
    <footer class="post-meta">
      {{authors}}
      {{tags prefix=" on "}}
      <time class="post-date" datetime="{{date format='YYYY-MM-DD'}}">{{date format="DD MMMM YYYY"}}</time>
    </footer>
  </article>
  {{/foreach}}
</main>

{{pagination}}

When you made the changes, make sure to restart Ghost or reenable your theme.
Since my instance in running inside a container I can do a quick docker restart THEME_NAME and reload the page.

This code is only adopted very slightly from the example given in the documentation. All that is removed is the author image for each post, and a navigation is added in the <header>.

I am not worrying about the design yet, this post is only about the structure. Having the sketch just helps me in figuring out what is needed on the page.

Posts

Next up are the posts.
Again a quick sketch:
Creating a new ghost theme - Part 1

We can again consult the Documentation and can copy the example code, so that the post.hbs looks like this:

{{#post}}
              
<article class="{{post_class}}">
  <header class="post-header">
    <h1 class="post-title">{{title}}</h1>
    <section class="post-meta">
      <time class="post-date" datetime="{{date format='YYYY-MM-DD'}}">
        {{date format="DD MMMM YYYY"}}
      </time> 
      {{tags prefix=" on "}}
    </section>  
  </header>
  <section class="post-content">
    {{content}}
  </section>     
</article>

{{/post}} 

Now we will have to somehow add the navigation sidebar here and since it is not supposed to change on any pages on the blog we can use a default.hbs file in our theme.
This file we be applied to all pages that Ghost will display.

First we remove the navigation from our index.hbs file and then create the default.hbs with the following content.
It will include a few helpers from Ghost so that different things can be injected here from the Admin panel.

<!DOCTYPE html>
<html lang="{{lang}}">

<head>
    {{! Document settings and metadata }}
    <title>{{meta_title}}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta charset="utf-8" /> 
    {{!-- This tag outputs SEO meta+structured data and other important settings --}}
    {{ghost_head}}
</head>

<body class="{{body_class}}">
    <div class="sidebar">
        <header>
            <h1 class="page-title">{{@blog.title}}</h1>
            <h2 class="page-description">{{@blog.description}}</h2>
            <nav class="navigation">
                {{navigation}}
            </nav>
        </header>
    </div>
    <div class="main">
        {{{body}}}
    </div>
    {{ghost_foot}}
</body>

</html>

Once we have this file we need to tell both the index.hbs and post.hbs file that they should use this default file by adding

{{!< default}}

to the top of both files.

We can now restart our Ghost instance and should see the navigation on both pages.

Here is a screenshot of the posts page:

Creating a new ghost theme - Part 1

Wrap up

This should be enough for a start.
The theme now has the basic structure to start with a bit of styling.

Ill cover that in the next article :)

Read more
Max

Please Note that this theme is not in use anymore. See this post for more info.

Creating a new ghost theme - Part 1

I want to make my blog more unique and fitting to my style both in code and in looks.
Writing my own theme seems like the right thing to do, given that I am currently looking for a job and having both more free time and wanting to look good to possible employers.

So I want to accompany this with a few blog posts on how I am going about this.

All of this will be done incrementally and I will set the theme live, so you can give me some feedback if you can/want. The best way to do this is on Twitter or at max [at] ehlers . berlin.

The development environment

The first thing that I need is a development environment. I wrote this article about a small script I wrote to help with this.

The homepage/index

When you come to this blog via its basic URL the first thing you are going to be greeted with is the index.

So this is also the first thing that I want to work on.

Here is a small sketch I made to get started:

Creating a new ghost theme - Part 1

Now I implemented all the things needed according to the Documentation in the index.hbs of my theme.

It now looks like this

<header>
  <h1 class="page-title">{{@blog.title}}</h1>
  <h2 class="page-description">{{@blog.description}}</h2>
  <nav class="navigation">
    {{navigation}}
  </nav>
</header>

<main role="main">
  {{#foreach posts}}
	<article class="{{post_class}}">
 		<header class="post-header">
   		<h2><a href="{{url}}">{{title}}</a></h2>
    </header>
    <section class="post-excerpt">
 			<p>{{excerpt words="26"}} <a class="read-more" href="{{url}}">...</a></p>
    </section>
    <footer class="post-meta">
      {{authors}}
      {{tags prefix=" on "}}
      <time class="post-date" datetime="{{date format='YYYY-MM-DD'}}">{{date format="DD MMMM YYYY"}}</time>
    </footer>
  </article>
  {{/foreach}}
</main>

{{pagination}}

When you made the changes, make sure to restart Ghost or reenable your theme.
Since my instance in running inside a container I can do a quick docker restart THEME_NAME and reload the page.

This code is only adopted very slightly from the example given in the documentation. All that is removed is the author image for each post, and a navigation is added in the <header>.

I am not worrying about the design yet, this post is only about the structure. Having the sketch just helps me in figuring out what is needed on the page.

Posts

Creating a new ghost theme - Part 1

We can again consult the Documentation and can copy the example code, so that the post.hbs looks like this:

{{#post}}
              
<article class="{{post_class}}">
  <header class="post-header">
    <h1 class="post-title">{{title}}</h1>
    <section class="post-meta">
      <time class="post-date" datetime="{{date format='YYYY-MM-DD'}}">
        {{date format="DD MMMM YYYY"}}
      </time> 
      {{tags prefix=" on "}}
    </section>  
  </header>
  <section class="post-content">
    {{content}}
  </section>     
</article>

{{/post}} 

Now we will have to somehow add the navigation sidebar here and since it is not supposed to change on any pages on the blog we can use a default.hbs file in our theme.
This file we be applied to all pages that Ghost will display.

First we remove the navigation from our index.hbs file and then create the default.hbs with the following content.
It will include a few helpers from Ghost so that different things can be injected here from the Admin panel.

<!DOCTYPE html>
<html lang="{{lang}}">

<head>
    {{! Document settings and metadata }}
    <title>{{meta_title}}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta charset="utf-8" /> 
    {{!-- This tag outputs SEO meta+structured data and other important settings --}}
    {{ghost_head}}
</head>

<body class="{{body_class}}">
    <div class="sidebar">
        <header>
            <h1 class="page-title">{{@blog.title}}</h1>
            <h2 class="page-description">{{@blog.description}}</h2>
            <nav class="navigation">
                {{navigation}}
            </nav>
        </header>
    </div>
    <div class="main">
        {{{body}}}
    </div>
    {{ghost_foot}}
</body>

</html>

Once we have this file we need to tell both the index.hbs and post.hbs file that they should use this default file by adding

{{!< default}}

to the top of both files.

We can now restart our Ghost instance and should see the navigation on both pages.

Here is a screenshot of the posts page:

Creating a new ghost theme - Part 1

Wrap up

This should be enough for a start.
The theme now has the basic structure to start with a bit of styling.

Ill cover that in the next article :)

Read more
Max

When setting up a new theme for this blog, I ran into the issue of quickly setting up a development environment on my local machine to test out ideas.

So I went on to duckduckgo.com and quickly found this blog article.
Taking it as an inspiration I wrote a small script to publish on npm to make it easier for future themes and other people as well. You can find it at https://github.com/b-m-f/gotede.

This blog post will summarize how to get started with it.

Installing gotede

The first thing you will need to do is to install the script with npm install -g gotede.

You also have to make sure to install both docker and docker-compose.

Now all you need to do is to switch to a folder in which the theme folder should be created. For example your home directory with cd ~.

Running gotede will ask you for the name of your new theme and which port the Ghost developemt instance should run under on your localhost (4000 f.e.).

Once youve entered your answers a new folder will be in that directory, with the name of the theme you entered. Go into that folder with cd THEME_NAME_YOU_ENTERED and start up the instance with docker-compose up -d.

For this example I am going to continue with a supplied port number of 4000.

To make sure that everthing worked correctly open your browser and go to http://localhost:PORT_YOU_PROVIDED, so in my case http://localhost:4000.

You should be greeted with the familiar skeleton instance of ghost, looking like this:

Screenshot_2018-09-11-Ghost

Setting up the Ghost instance

If everything worked, you should head over to http://localhost:4000/ghost to set up your admin account. Just follow the steps shown on the website. You can just use a test account here, since it will only be running locally.

Creating the theme and activating it

For this step make sure that you are still in the folder that gotede created for you and where all your files for the theme are located in.
This will be where you do the actual work.

To get everything running you will first need to install all the required npm dependencies. Do this with npm install.

Once it is completed we can start the local development server that will take care of compiling our css and supplying the ghost instance with our theme with npm run start.

Since the theme is newly added to the instance, we will have to restart it with either docker restart THEME_NAME_YOU_PROVIDED or docker-compose restart so that it becomes available.

As a last step head over to http://localhost:4000/ghost/#/settings/design and activate your theme at the bottom of the page:

Screenshot_2018-09-11-Settings---Design---test

Developing the theme

With the npm run start running in the terminal you can now start editing your theme according to the official docs.

Have fun, and feel free to improve the script and the skeleton theme from gotede by submitting a Pull Request to the Repo.

Read more
Max

When setting up a new theme for this blog, I ran into the issue of quickly setting up a development environment on my local machine to test out ideas.

So I went on to duckduckgo.com and quickly found this blog article.
Taking it as an inspiration I wrote a small script to publish on npm to make it easier for future themes and other people as well. You can find it at https://github.com/b-m-f/gotede.

This blog post will summarize how to get started with it.

Installing gotede

The first thing you will need to do is to install the script with npm install -g gotede.

You also have to make sure to install both docker and docker-compose.

Now all you need to do is to switch to a folder in which the theme folder should be created. For example your home directory with cd ~.

Running gotede will ask you for the name of your new theme and which port the Ghost developemt instance should run under on your localhost (4000 f.e.).

Once youve entered your answers a new folder will be in that directory, with the name of the theme you entered. Go into that folder with cd THEME_NAME_YOU_ENTERED and start up the instance with docker-compose up -d.

For this example I am going to continue with a supplied port number of 4000.

To make sure that everthing worked correctly open your browser and go to http://localhost:PORT_YOU_PROVIDED, so in my case http://localhost:4000.

You should be greeted with the familiar skeleton instance of ghost, looking like this:

Screenshot_2018-09-11-Ghost

Setting up the Ghost instance

If everything worked, you should head over to http://localhost:4000/ghost to set up your admin account. Just follow the steps shown on the website. You can just use a test account here, since it will only be running locally.

Creating the theme and activating it

For this step make sure that you are still in the folder that gotede created for you and where all your files for the theme are located in.
This will be where you do the actual work.

To get everything running you will first need to install all the required npm dependencies. Do this with npm install.

Once it is completed we can start the local development server that will take care of compiling our css and supplying the ghost instance with our theme with npm run start.

Since the theme is newly added to the instance, we will have to restart it with either docker restart THEME_NAME_YOU_PROVIDED or docker-compose restart so that it becomes available.

As a last step head over to http://localhost:4000/ghost/#/settings/design and activate your theme at the bottom of the page:

Screenshot_2018-09-11-Settings---Design---test

Developing the theme

With the npm run start running in the terminal you can now start editing your theme according to the official docs.

Have fun, and feel free to improve the script and the skeleton theme from gotede by submitting a Pull Request to the Repo.

Read more
Max

Alot of Webpack introdutions focus on how to set up your first webpack configuration.
What I want to do in this article is taking a step back and look at what webpack is actually doing before going into the setup and all its complications.

The problem

In the frontend we have a variety of targets that we want our applications to run in, and they are all browsers. Some old, some new.

While the Javascript ecosystem is evolving at an extreme pace and SPAs + more complex web applications become the norm, the browsers evolve slowly.
In addition alot of people still use outdated ones, which might be due to workplace restrictions or the proper longterm use of hardware.

Now we have a dilemma. We want to write complex applications in Javascript, but in the browser we usually load an app via a single file.
React is a good example.
You would usually load it with sth like:

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>

But the developers definitely did not develop this complex piece of software in just one file! The complexity of doing that with a team of developers and even alone would have too many downsides.

Looking at the repository we can see a lot of different folders and files, that all host their specific part of the application logic.

Screen-Shot-2018-07-05-at-16.59.22-1

In order for the different parts to use each other and share functonalities they are using the import syntax in Javascript. If you do not know how this works you can read about it here or just do a quick duckduckgo search.

The downside of this approach is that most of the aforementioned browsers do not yet, or never will support it. In addition to that all of those would have to be loaded over the web individually. While this is one thing that HTTP/2 might make less of a problem, it still is one to date.

Enter webpack

This exact situation is where webpack fits in perfectly!

In order to get all your different components and Javascript files that import each other to work in the browser you will need to bundle them together somehow and in essence webpack will do just that.
It take multiple files and figures out how they import each other and then bundles them together into 1 big file that provides wrapper functions to mimic the process of importing.

Lets make this a bit clearer with the following example:

// mathHelpers.js

export function add(a, b) {
  return a + b;
}
// main.js
import { add } from "./mathHelpers.js";

function main() {
  const a = 5;
  const b = 37;
  const result = add(a, b);
  console.log(`The result of ${a} + ${b} = ${result}`);
}

We have the main file which imports the mathHelpers to do some difficult calculations. This helps us to split out different parts of the application into different files. The benefit being a good structure and reusability of f.e. the math functions.

Now we are going to have to bundle those 2 together into 1 file.

The first step is installing webpack. To use it on the command line we need to install it on the computer with npm i -g webpack-cli.

Afterwards we can just run webpack main.js --mode=none in our application folder and the bundling is done! Its really that easy.

The mode option just says that no optimizations should be done, and all we want is a bundled file to check out its contents.

After the operation has completed we can inspect the bundle in the newly generated dist folder.

The content should be similar to this:

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _mathHelpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);


function main() {
  const a = 5;
  const b = 37;
  const result = Object(_mathHelpers_js__WEBPACK_IMPORTED_MODULE_0__["add"])(1 + 2);
  console.log(`The result of ${a} + ${b} = ${result}`);
}


/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
function add(a, b) {
  return a + b;
}


/***/ })
/******/ ]);

Dont worry too much about all of this machine generated code.

Whats important to understand is that the top of the file is the functionality that webpack provides to fake the importing that we are using in our files.

If you inspect the bottom of the file though, you can see that we still have our main and add function.
The difference here is though, that the main function now calls Object(_mathHelpers_js__WEBPACK_IMPORTED_MODULE_0__["add"]) instead of the add function directly.

We have now reached a basic understanding of webpack and can say that the different modules are bundled together under an object and named in a way that all other modules know where to find the functions that they need. All the calls to other modules are then also automatically replaced with the correct call to the Object+Name.

What now?

With this knowledge as a basis you can now start with understanding how the basic webpack main.js --mode=none can be used with configurations to apply some transformations to your codebase.
This will allow things like using the latest EcmaScript features, code splitting and many more, some of which I will cover in later posts.

Read more
Max

Alot of Webpack introdutions focus on how to set up your first webpack configuration.
What I want to do in this article is taking a step back and look at what webpack is actually doing before going into the setup and all its complications.

The problem

In the frontend we have a variety of targets that we want our applications to run in, and they are all browsers. Some old, some new.

While the Javascript ecosystem is evolving at an extreme pace and SPAs + more complex web applications become the norm, the browsers evolve slowly.
In addition alot of people still use outdated ones, which might be due to workplace restrictions or the proper longterm use of hardware.

Now we have a dilemma. We want to write complex applications in Javascript, but in the browser we usually load an app via a single file.
React is a good example.
You would usually load it with sth like:

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>

But the developers definitely did not develop this complex piece of software in just one file! The complexity of doing that with a team of developers and even alone would have too many downsides.

Looking at the repository we can see a lot of different folders and files, that all host their specific part of the application logic.

Screen-Shot-2018-07-05-at-16.59.22-1

In order for the different parts to use each other and share functonalities they are using the import syntax in Javascript. If you do not know how this works you can read about it here or just do a quick duckduckgo search.

The downside of this approach is that most of the aforementioned browsers do not yet, or never will support it. In addition to that all of those would have to be loaded over the web individually. While this is one thing that HTTP/2 might make less of a problem, it still is one to date.

Enter webpack

This exact situation is where webpack fits in perfectly!

In order to get all your different components and Javascript files that import each other to work in the browser you will need to bundle them together somehow and in essence webpack will do just that.
It take multiple files and figures out how they import each other and then bundles them together into 1 big file that provides wrapper functions to mimic the process of importing.

Lets make this a bit clearer with the following example:

// mathHelpers.js

export function add(a, b) {
  return a + b;
}
// main.js
import { add } from "./mathHelpers.js";

function main() {
  const a = 5;
  const b = 37;
  const result = add(a, b);
  console.log(`The result of ${a} + ${b} = ${result}`);
}

We have the main file which imports the mathHelpers to do some difficult calculations. This helps us to split out different parts of the application into different files. The benefit being a good structure and reusability of f.e. the math functions.

Now we are going to have to bundle those 2 together into 1 file.

The first step is installing webpack. To use it on the command line we need to install it on the computer with npm i -g webpack-cli.

Afterwards we can just run webpack main.js --mode=none in our application folder and the bundling is done! Its really that easy.

The mode option just says that no optimizations should be done, and all we want is a bundled file to check out its contents.

After the operation has completed we can inspect the bundle in the newly generated dist folder.

The content should be similar to this:

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _mathHelpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);


function main() {
  const a = 5;
  const b = 37;
  const result = Object(_mathHelpers_js__WEBPACK_IMPORTED_MODULE_0__["add"])(1 + 2);
  console.log(`The result of ${a} + ${b} = ${result}`);
}


/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
function add(a, b) {
  return a + b;
}


/***/ })
/******/ ]);

Dont worry too much about all of this machine generated code.

Whats important to understand is that the top of the file is the functionality that webpack provides to fake the importing that we are using in our files.

If you inspect the bottom of the file though, you can see that we still have our main and add function.
The difference here is though, that the main function now calls Object(_mathHelpers_js__WEBPACK_IMPORTED_MODULE_0__["add"]) instead of the add function directly.

We have now reached a basic understanding of webpack and can say that the different modules are bundled together under an object and named in a way that all other modules know where to find the functions that they need. All the calls to other modules are then also automatically replaced with the correct call to the Object+Name.

What now?

With this knowledge as a basis you can now start with understanding how the basic webpack main.js --mode=none can be used with configurations to apply some transformations to your codebase.
This will allow things like using the latest EcmaScript features, code splitting and many more, some of which I will cover in later posts.

Read more