I´m finally proud to release this upgraded blog and restyled elements to my wishes. More will come. To be continued...

The Challenges

Looking for Docker solution

I love the idea not to having to install a mess and ruining my system files only for running an app or apps for that matter. Luckily Ghost Blog has docker versions, which it was a good time to finally upgrade my old blog that’s running on version 1.

How to attach independent docker-compose.yml to custom Docker Shared Network

Since I created this amazing Docker Nginx Stack and I was wrapping my head around how to deal with apps that are running same ports, but with docker it’s okay without exposing ports to your system and let Nginx proxy configuration deal with them all with ease.

I even know how to create another docker-compose.yml running with multi ghost versions, connect them with Docker Nginx Stack network and access them trough browser for example:

I’ll make a guide how to configure your Nginx Docker Stack with other networks.

Upgrading from Ghost v1 to v3

My previous blog was so old it was running version 1.0. At that time whenever there is an upgrade, I felt there was a lot of work for upgrading it. Download new version, running correct Node version, deal with node warnings, and then found out I'm running too new Node version, that I should downgrade again to see which version should run this app. Then I have to search their website or forums which versions it was correct one. It was tedious. So I was holding off and I should be content with it, which I was not. Also I've edit Ghost backend and create my own features and that upgrading was never an option.

When I do things, I tend to do shortcut first and see I could spare some time. So I exported my old one, and import to the latest. What a surprise; I’ve been greeted with error message, that I should do the upgrades on version 1 first. The longcut then it is.

So I imported my data to the latest of version 1 first. Check if all are there, and got little worried with all those warning messages. But everything seems to be all right. Let’s export from this version then, and so I did.

I’m still curious to do the imports in v3 instead of v2. And to my surprise, it worked!

I finally have all my posts and migrate all my images and files.

Remember another long password

The first registration needed to make much longer password than I’m used to. So I needed some time to create a new one and remember it by heart. Practise my muscles to remember them.

Getting to know the whole file structure of Casper theme

Before theming it’s great to know what tech stack it use, and hopefully learn something new.

Migrate Mizu v1 own custom theme to v3

Migrating things is without issues. Trying to migrate on v3 I get bunch of errors, which mean I have to start from v1 and work way up from there.

Luckily after dealing some warnings by changing some variables, I can skip v2 and made my old theme work for the latest version.

Migrate Injected Codes to the latest and update libraries

Refactored CSS and Javascript, updating libraries and hope for the best.

Migrate old Posts to v3

By accident my posts got doubled or tripled by exporting them, and I was expecting that the posts have an id and only updates the ones that already exist. So I had to reset the blog by Delete all content in the Settings / Lab / Delete All Content.

I don’t like my old Mizu theme anymore and start Casper default theme as base

While it’s a comfort to see your old theme back, I don’t feel like it aged well. It’s doesn't meet today’s tech and what you expect in a blog and its features.

I don’t like how gulpfile was written, so I reformat and refactor into ES6

Since this is going to be my own theme, I might make it all mine with all my coding rules.

The horrendous one big pile of CSS file

When you want to style any component, you look up for the class name and check if it’s within the CSS. The CSS is horrendous big and very long to scroll and why isn’t this in small chunks of components written LESS or SCSS? Good thing there are commented titles, so I could start splitting in beautiful chunks and organise a bit and make sure Gulp compile SCSS for me.

Gulp nanocss is bugged and replace with gulp-clean-css

Who’d’ve tought creating numbered headers in CSS can be so hard, and had to found out that nanocss is bugged and replaces variables into single letters. Which is good in first glance, but it converts same variables in other places too such as line-numbers for hightlight/prism js, that doesn't make sense at all. Which made me look for an alternative.

Restyled components into BEM methodology

This methodology helps with legacy projects and it’s a good way to write CSS.

Replace highlight.js with prism.js

I wanted line-numbers for code highlighting, but highlight.js isn't the right tool for this.

Finalised styling and publish work-in-progress to GitHub

Something that I’m proud of and finally pushed to GitHub, because working on any project I never had to worry about colours and feelings, but I’m used to translate designs to HTML/CSS. Designing stuff is too emotional for me.



Code injection

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" />
<link rel="stylesheet" href="/assets/built/prism.min.css" />
    :root {
        scroll-behavior: smooth;
    pre {
        font-size: 1rem;
        line-height: 2rem;

    /* CUSTOM */
    pre[class*='language-'] {
        font-size: 1.4rem;
        line-height: 2rem;
        text-shadow: 0 1px #111;
    pre {
        min-height: 50px;
    	max-height: calc(100vh - 160px);
    .post-card-content-link.post-card-content-link--comment {
        position: relative;
        left: 4px;
        top: 5px;
        background-color: rgba(255,255,255,.8);
        background-image: linear-gradient(141deg,#0fb8ad 0,#1fc8db 51%,#2cb5e8 75%);
        color: white;
        border-radius: 8px;
        padding: 2px 4px !important;
        font-height: 1em;
        font-size: 12px;
<style lang="less">
    .visitorData {
        z-index: 101;
        position: fixed;
        bottom: 100px;
        right: 0;
    	background-color: black;
        color: #0fb8ad;
        padding: 4px 8px;
        border-radius: 8px 0 0 8px;
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.7.1/less.min.js"></script>
Site Header
<script src="https://unpkg.com/vue"></script>
<script src="/content/images/files/js/clicktoposteditor/index.js"></script>
    <style rel="stylesheet/less" type="text/less">
        wa-me {
            z-index: 5000;
            position: fixed;
            right: 0;
            bottom: 40px;
            .button {
                display: flex;
                align-items: center;
                justify-content: center;
                width: 40px;
                height: 40px;
                border-radius: 50% 0 0 4px;
                background-color: white;
                color: #4FCE5D;
                text-align: center;
                cursor: pointer;
                font-size: 36px;
                &:hover {
                	text-decoration: inherit; }
    <script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.7.1/less.min.js"></script>
    <a href="//wa.me/31624678838?text=Hello%20I%E2%80%99m%20texting%20you%20from%20blog.sylo.space%21%0A%0AMy%20name%20is%3A%20%0A-%20&source=blog.sylo.space&data&app_absent" class="button"><i class="fab fa-whatsapp"></i></a>
<ghost-search url="//blog.sylo.space" ghost-key="00000000000000000000123456"></ghost-search>

<script id="page-index" type="application/javascript" src="/content/images/files/js/pageindex/index.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/prism.min.js" data-manual></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/autoloader/prism-autoloader.min.js"></script>
    ;(() => {
	const d = document
	const onDOMContentLoaded = () => {
		// Add class .line-numbers to pre/code for Line Numbers
		const codeElememts = document.querySelectorAll(
		codeElememts.forEach((el, i) => el.classList.add('line-numbers'))
		if (Prism.plugins && Prism.plugins.autoloader)
			Prism.plugins.autoloader.languages_path =

		// Rerun Prism syntax highlighting on the current page
		Prism && Prism.highlightAll()

	// Init
	if (
		d.readyState === 'complete' ||
		d.readyState === 'loaded' ||
		d.readyState === 'interactive'
	) {
		// document has at least been parsed
	} else {
		window.addEventListener('DOMContentLoaded', onDOMContentLoaded)

<script src="/content/images/files/js/loadScripts.js"></script>
const scripts = Object.entries({
	PageIndex: '/content/images/files/js/PageIndex.js',
    //pageindex: '/content/images/files/js/pageindex.js',
    //addLineNumbersOnPrismCodeHighlighting: '/content/images/files/js/addLineNumbersOnPrismCodeHighlighting.js',
    createDisqusThreadPostFooter: '/content/images/files/js/createDisqusThreadPostFooter.js',
}).map(([key, value]) => value)

    .then(loadScript.bind(null, '/assets/built/vue/ghost-search.min.js'))
    //.then(loadScript.bind(null, '/content/images/files/js/appendDomGhostSearch.js'))

    <!-- Facebook Likes -->
        .iframeButton {
         	width: 100%;
            height: 100%;
    <script id="fb-root" async defer 
    <script src="/content/images/files/js/fb-like/index.js"></script>

    <form action="https://www.paypal.com/donate" method="post" target="_blank">
        <h1>Keep my Blog ad free or<br/>buy me a coffee ☕</h1>
        <input type="hidden" name="cmd" value="_donations" />
        <input type="hidden" name="business" value="RDA6JFLJYKGEW" />
        <input type="hidden" name="item_name" value="Keep this Blog free of ads" />
        <input type="hidden" name="currency_code" value="EUR" />
        <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" />
        <img alt="" border="0" src="https://www.paypal.com/en_NL/i/scr/pixel.gif" width="1" height="1" />
    <i slot="button-open" class="fas fa-thumbs-up"></i>
    <i slot="button-close" class="fas fa-window-close"></i>
<script type="module" src="/content/images/files/js/panel-widget/index.js"></script>
<!-- Disqus Counts -->
<script id="dsq-count-scr" src="//hariantoatworkblog.disqus.com/count.js" async></script>
document.getElementById('dsq-count-scr').onload = () => {
	let d = document,
		postFeed = d.querySelector('#site-main .post-feed'),
		postFeedObserver = new MutationObserver(
			mutations =>
				mutations && DISQUSWIDGETS && DISQUSWIDGETS.getCount({ reset: true })

	postFeed && postFeedObserver.observe(postFeed, {
		childList: true
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-35815336-4"></script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-35815336-4');
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js" integrity="sha256-bQmrZe4yPnQrLTY+1gYylfNMBuGfnT/HKsCGX+9Xuqo=" crossorigin="anonymous"></script>
<script type="text/html" id="visitor-tpl">
<footer class="visitorData u-box u-box--column u-box--align-center" title="Active Visitors">
<div v-text="activeUsers" />
<i class="fas fa-users" />
<script src="https://js.sylo.space/js/visitorData.js"></script>
(() => {
const socket = io.connect('chat.sylo.space'),
      el = document.currentScript

const app = new Vue({
	name: 'visitorData',
    template: '#visitor-tpl',
    data: () => ({
        activeUsers: 0
    beforeMount() {
    	socket.on('updated-stats', data => {
            Object.assign(this, data)
Site Footer