Hammerill Light Logo

How I Created this Site

How I Created this Site

May 19, 2024

Hello! My name is Kirill.

You might know me by my nickname - Hammerill. Or by my official name, Kyrylo.

Anyways, you are interested in how this site was made. Let me commence.

So what is Hammerill? Well, let's just say it's a direct link to me.

Imagine me being 11 years old at some English class:

"H-A-M-M-E-R" - "Hammer". It is a hand tool, kids.

...

Kirill! What does "hammer" mean?

And this was the moment when I thought to merge the two words: "hammer" and my own name "Kirill" into one: Hammerill. I didn't really care about the meaning or anything, it just sounded cool to me. Since then I've been putting it as my nickname everywhere.

And I came up with a logo, literally a hammer with diamond corners:

Old Hammerill Logo

The old logo of Hammerill made by me in Paint in 2016.

Interestingly, only these diamond corners are still there. You can see them as two blue squares in light mode or two white squares in dark mode as the logo of this site.

I will talk about history of this nickname at the main page when I finish the parallax effect.

If you don't know what parallax is, take a quick look to this example:

For now, let's go on.

After a long time of being inactive online after the war, I decided to revive my old nickname. Just one little thing came to my mind - create a personal website.

I registered the hammerill.com domain name and took a VPS. I won't talk too much about the services I used, let's just say the domain name cost me ~10€ / year and the VPS ~100€ / year.

Now, being an apprentice at IT, I have to configure it.

Now let's talk about the technical part.

The only thing I knew at the time as the web server was Apache. I've tried installing it directly on the VPS and letting it serve some plain HTML. Also I've learned such a thing as HTTPS, SSL certs and how to get them, Let's Encrypt, in order to get rid of that annoying security error.

But Apache installed directly on the VPS was not the most comfortable thing to work with. I couldn't really configure it for my subdomains (like sand-box2d.hammerill.com), maybe that's just my problem, maybe it's the Apache which is hard to configure, idk.

Also where I'm supposed to save my config? Do I have to copy it manually everytime? It was the time to revise and automate everything. I've thought about my personal Git server.

As I've seen at my apprenticeship, anyone in fact could have their own Git server, such as GitLab. So I've tried installing it on my VPS. It was... hard. It required me to learn such things as Docker, its Compose and the Nginx web server. And finally, I've got it working.

But GitLab seemed too heavy for me. Luckily, I've just started working on a project in my Lycée where professors used the ultra tiny Gitea instead, so I've switched over. It also required me to use more Docker.

After being forced to learn Docker, I've started seeing it as an interesting thing. If I can store my configs at the Git server, leaving myself to up the Docker is way easier than forcing to install dependencies manually. Now, every service can be put inside the portable space not touching the server config itself.

The first thing I wanted to put inside the Docker was the web server. And it wasn't Apache. It was Nginx. And it worked very well! Now it's serving all my subdomains easily configurable at any time.

Now the most important thing. My site itself.

It's a Next.js application running on port 3000 inside a Docker. Another Docker (Nginx) is doing the proxy on port 443 while providing the SSL certs. I will explain later how I coded it.

This VPS is a great place to host the other stuff. For example:

  • Mail Server. VPS is running another Docker which is letting me setup my own mail server.
  • Ackee. Ackee is an open source alternative to the Google Analytics or similar.
  • code-server. A cool solution to have your own online VSCode accessible from every computer being able to connect to the Internet. You only have to secure it well. For example, this text you are reading is written on 4 different PCs (my powerful laptop at home, my slow portable laptop, a PC in the Lycée and a PC at internship). Text and terminal state is perfectly synced with all the machines connected to the code-server.
  • And even more, like SMS reporting...

And finally, the programming aspect of the site you're visiting.

  • First of all, this site is based on Next.js.
  • Next.js is a framework based on the React library.
  • React lets you create reusable components (sort of HTML element templates) and use them in your JavaScript code (actually TSX).

Interest of Next.js is that it executes your JavaScript code on the server and sends the ready HTML to the client. In general, plain React code without any framework is executed on the client, which could be difficult for low-end devices. And even more, you can ask Next.js to execute your code only once at the build time and yield the static HTML/CSS/JS assets. That's how the static.hammerill.com/en (you can even access it on plain HTTP from a VERY old machine) and hammerill.github.io work.

I've chosen Next.js when I was thinking: "Well. I have to create my site. I didn't practice web dev for a while. What is the newest tech stack I can learn now to create my website with?".

And that's how I chose Next.js, TailwindCSS and shadcn/ui.

Tailwind gives you another way of styling elements. Idea is that you apply the class name like flex flex-wrap instead of creating a class in CSS like this:

.wrapper {
  display: flex;
  flex-wrap: wrap;
}

It really simplifies everything! Just go ahead and try it.

shadcn/ui supplies you with the ready components in a modern look for some most used things like dropdown menu. Like this, I don't have to lose time on coding such things as "programming the closed/open state of the menu", I just fill my site with the content I want to see instead.

Look at these buttons it provides (I remind you, the only thing styled by me is their positioning):

DO NOT. CLICK. ME.

Of course it provides a lot of more stuff, like the dropdown menu button at the left top corner.

Component <VisibleWrapper> is the particularity of my site. Maybe the most problematic one.

It acts as a <div> but whenever element is not on the screen, it becomes transparent. When it appears on the screen, it becomes visible again. And we can see that effect slightly fading in as transition CSS effect was applied.

This thing creates 4 problems:

  1. Screen readers
    Some screen readers (like the mobile Chrome one) might have hard time parsing yet invisible elements. Since May 26, I offer the grand solution: VisibleWrapper will apply opacity-5 instead of opacity-0.
  2. JS required
    As all the elements are set to not visible by default, a script in JS has to activate them. That can lead to the longer load time. Since May 26, the grand solution also helps out: you'll be able to see the content slightly.
  3. Creating new layout
    Every time I add a new item I have to ensure it's inside a <VisibleWrapper> or, simpler, swap <div> to <VisibleWrapper> if it's indeed a <div> by chance.
  4. DOM
    Looking at the produced HTML isn't really comfortable. For example, all the paragraphs in the blog are wrapped inside these <div>s.

The first two problems are fixable. And actually I offered the grand solution since May 26. You can solve the 1st problem by setting graphics to low (telling JS to make all the elements visible) or permanently solve the both by going to the static version of the site (disabling <VisibleWrapper> completely and turning it to a regular <div>). I remind you that in the case with my site, using the static version is well encouraged. It gets the latest updates and has the same support.

The other two, well, are inevitable. But end user doesn't care. 🤷

Also, how did I put all the paragraphs in the blog inside special <div>s and made it work in static?

The text you are reading right now is written in MDX, a type of Markdown which lets me insert some special JSX code into it. Then, it gets compiled to a normal JSX. After that, in static it gets compiled to HTML.

Did you notice the possibility of changing the language? If you were to click Dropdown Menu > Language > Français you would see the French version of this article. These two (English version and French version) are different MDX files located in the same article folder.

So how does that work? First, language switching is provided by the next-intl library. Then, as it's required by the Next.js App Router, I import all these MDX files in the page.tsx file located in the particular article folder (I copy-paste it everytime -_-) and return the corresponding MDX file to the component which wraps it up as the article.

To get the vision of how does that work, take a look at this screenshot:

Structure of the Hammerill blog

Note: that's not the full structure. I also sectorize it and there are more files. I give you this example only to make you understand the principle.

Let's break it up. From bottom to up (get ready, it'll fill your whole screen):

  • layout.tsx and page.tsx inside the blog folder
    Main page of the blog. It shows you all the articles.
  • layout.tsx inside the (articles) folder
    Shared layout of all the articles. See more here.
  • article-utils.tsx
    File containing all the stuff needed to put the article imported from MDX in place. For example, put the blurred article image at the background and add the "Back to blog" button. This file is separated from "layout.tsx" as it declares that it exports the utils used inside the article routes. The main component exported is "ArticleHolder" taking some metadata and MDX import with corresponding locale as the children.
  • Folders inside the (articles) folder
    Every particular article. Considering Next.js App Router, it means that you can access both articles at URLs "/blog/example" and "/blog/second-example".
  • img.png
    Some image used in the article. Maybe it's used as the article preview image. Maybe it's used inside the article as a figure. It could be put inside the "img" folder.
  • get-article-meta.ts
    A file exporting the article metadata. It imports all the local MDX files, gathers their exports (localized title and description) and puts all them together. It can contain the general meta not depending on the locale (like date of publish in ISO format, e.g. "2024-05-19").
  • page.tsx
    File handling the route as required by the Next.js App Router. It simply imports all the local MDX files, looks which locale is asked and returns the "ArticleHolder" passing all the needed data to it (metadata from "get-article-meta.ts" and MDX with current locale).
  • en.mdx and fr.mdx
    Actual article content. It's simply the Markdown, but I have to export localized title and description and I'm able to import some of my own components if needed. They could be put inside the "content" folder.

Woah! That's a lot. And still, I've took my time to compose all of that and it actually does work.

But why overcomplicate everything? Well, it makes my site accessible for everyone I know as all the languages are well supported.

And more, this is static-friendly. Meaning that every change I make could create a new static export. This static export could work even if I've stopped caring about my site, as the hosting by GitHub is free and forever (would GitHub delete my account and all of its contents if I would be offline for so long? 🤔).

We didn't yet discuss the cool thing about this approach: MDX plugins and custom components.

First of all, I've created the typography component containing various customized typography elements as recommended by shadcn. Then I wrap all the elements inside the <VisibleWrapper>.

For example, instead of regular <p> there is <P> which basically is this:

<VisibleWrapper className={visibleWrapperClassName}>
  <p {...props} className={cn("mt-6 leading-7 break-words", className)}>
    {children}
  </p>
</VisibleWrapper>

Meaning that I have to represent a paragraph with my component, and simply applying CSS is not enough.

Happily, MDX allows me to do that. Take a look at mdx-components.tsx:

import { ---, P, --- } from "@/components/typography";

export function useMDXComponents(---): MDXComponents {
---
p: ({children, ...props}: {children?: React.ReactNode}) => (
  <P {...props}>{children}</P>
),
---
}

Now, module turning MDX paragraph to JSX paragraph understands that it has to produce my component <P>, not the default HTML tag <p>. That's very nice! I did that for all the elements.

After that, I can connect custom Remark and Rehype plugins such as autolink-headings which adds those buttons at the right of headings when you hover on them. My task is to ensure it works and style it.

By the way, those buttons are added to the left by default. I have to configure it. Look at next.config.mjs:

export const withMDX = createMDX({
  options: {
    rehypePlugins: [rehypeHighlight, rehypeSlug, [rehypeAutolinkHeading, {behavior: "append"}]],
    remarkPlugins: [remarkGfm, [remarkToc, {heading: tocHeading}]],
  },
  extension: /\.mdx?$/,
});

Did you see it? It's behavior: "append".

What I wanted to say that there were a LOT of things like this that required me to apply some little config to make it work. But, in order to find that little config I have to spend hours of understanding who's responsible for the problem and how to fix it. After all, it makes me an experienced developer.

Another example: while printing a page, text will be fuzzy on paper if you apply blur-none Tailwind class name somewhere. To really reset the blur effect, you have to apply filter-none instead. 🫠

In general, creating this site was a pleasure, a good way to spend some time.

If you're reading this, half of the work is done. But parallax effect isn't done yet. I should start working!

If you're willing to subscribe or to say something other, email me and I'll see further about implementing this feature.

Thank you for staying with me, see you soon!