Introduction

Hi there! As you might have noticed, my gitlab isn’t exactly looking like the original gitlab. You might be familiar with the design, it looks like this page or any other page of me (main, tools, imprint). I wrote that design about 3 years ago (in 2020) and launched the 5th version of bixilon.de (codenamed nojs). In 2024 I created tools.bixilon.de and needed a design for it. Being way to lazy brought me to the idea “Just reuse it…” . The theme (now called bx5) was quickly extracted and I started thinking what else I could do with it. Other pages followed, porting them is really easy, because I absolutely don’t belive in tailwind (What is the whole purpose if css then? Pretty stupid. Design should be independant of code) and it is just including the css…Except for gitlab. How can you even customize gitlab? Kind of (usable) 0 google results…

How I deploy gitlab

Gitlab itself is just a gitlab repository for me with a dockerfile. Without wanting to go away from the topic, it basically looks like this:

1FROM debian:bookworm-slim
2
3RUN apt-get install gitlab-ce
4
5COPY configs...
6
7ENTRYPOINT ...

(It is actually way larger, around 50 lines. A lot of scripts, monitoring, configs, healthchecking and stuff is missing there.)

I could easly add some patching to it. Not that bad.

Patching

Patching html

Gitlab uses haml for their templates, Adding html can look pretty much like this:

 1#!/bin/bash
 2
 3FOOTER_FILE="/opt/gitlab/embedded/service/gitlab-rails/app/views/layouts/_page.html.haml"
 4FOOTER=$(cat $FOOTER_FILE)
 5
 6> $FOOTER_FILE # clear
 7
 8while IFS= read -r line; do
 9    if [[ $line == *"= yield :after_content"* ]]; then
10        echo "      = raw File.read \"$SOURCE/html/footer.html\"" >> $FOOTER_FILE
11
12        echo "Injected footer before line $line"
13    fi
14
15    echo "$line" >> $FOOTER_FILE
16done <<< "$FOOTER"

CSS

CSS is a bit tricker on the other hand, the files are stored gzipped. You could just unpack a css file, append you css and then repack, but that is not really good because of caching. Your browser just requests a (css) file once and everytime the html requires it, it sees that it already requested it and uses the localy cached version of it. Pretty bad if you add your css and want to change it afterwards.

The solution is simple: Add it as a separate css and identify it by its content. My script looks pretty much like this (including cache bypass, gzipping and injecting):

 1#!/bin/bash
 2SOURCE=/opt/gitlab/patches
 3ASSETS=/opt/gitlab/embedded/service/gitlab-rails/public/assets
 4
 5echo "Gathering hash of bx5 theme"
 6BX5_HASH=$(sha1sum $SOURCE/css/bx5.css | cut -d' ' -f1 | cut -c1-8)
 7BX5_FILE="${ASSETS}/bx5.$BX5_HASH.css"
 8
 9echo "Copying bx5 to $BX5_FILE"
10cp $SOURCE/css/bx5.css $BX5_FILE
11
12echo "Creating gzip..."
13cat $BX5_FILE | gzip > $BX5_FILE.gz
14
15echo "Adding css file to gitlab..."
16HEAD_FILE="/opt/gitlab/embedded/service/gitlab-rails/app/views/layouts/_head.html.haml"
17HEAD=$(cat $HEAD_FILE)
18
19> $HEAD_FILE # clear
20
21while IFS= read -r line; do
22    echo "$line" >> $HEAD_FILE
23    if [[ $line == *"universal_stylesheet_link_tag 'performance_bar'"* ]]; then
24        echo "  = universal_stylesheet_link_tag \"/assets/bx5.$BX5_HASH.css\"" >> $HEAD_FILE
25        echo "Injected css after line $line"
26    fi
27done <<< "$HEAD"

Not too bad. Keep in mind that that works for most gitlab releases, but can break at any time. Write tests!

Writing scss

That is really simple, just press [F12] in your browser, get the class and then write it. It can look like this (the original bx5 gitlab theme has ~4000 lines more than the bx5 theme):

1.gl-bg-default {
2  background-color: theme.$color-dark;
3}
4
5.gl-bg-subtle {
6  background-color: theme.$color-dark;
7}

Should not be that complicated. Keep in mind that it might break with every update. You should test your design carefully after every update and write automated tests.

Reducing the deployment time

While developing a theme, you don’t want to wait 10 minutes (stop, build again, run) for every change. For that I wrote a tampermonkey script, feel free to use it for such projects:

 1// ==UserScript==
 2// @name         Gitlab Local design inject
 3// @match        https://gitlab.bixilon.de/*
 4// @version      1.0
 5// @description  try to take over the world!
 6// @author       Moritz Zwerger <bixilon@bixilon.de>
 7// @resource     css file:///home/moritz/git/gitlab.bixilon.de/bixilon/html-themes/public/themes/bx5/gitlab.css
 8// @grant        GM_getResourceText
 9// @grant        GM_addStyle
10// @grant        GM_log
11// ==/UserScript==
12
13
14function inject_css() {
15    GM_log("Injected local css");
16    const css = GM_getResourceText("css");
17    GM_addStyle(css);
18    GM_log("Injected!");
19}
20
21(function() {
22    'use strict';
23
24    window.setTimeout(inject_css, 100);
25})()

Now you can use sass watch feature (sass --watch) and then just press F5 to reload. Not that hard, is it?

Additional notes

  • gitlab has a dark/light theme, to make my thing work, I completely patched out the light theme in gitlab
  • same for the syntax highlighting
  • minifying improves performance
  • it is really a lot of work to make almost all parts working, I am sure you can spot some errors I made. But 90% is enough for me in such projects
  • gitlab is fucking massive and bloated

Final result

See it yourself:

gitlab.bixilon.de (my “bx5” themed gitlab)

gitlab.com (original gitlab)

I think my variant looks far better, but maybe that is just my personal preference.

Tags: Gitlab