diff --git a/aleksis/core/assets/App.vue b/aleksis/core/assets/App.vue
index 4ee81730c0772d05f83e62e78e114611e16c50a5..8e525db874be43819793c96d7d16654712e329cc 100644
--- a/aleksis/core/assets/App.vue
+++ b/aleksis/core/assets/App.vue
@@ -1,6 +1,6 @@
 <template>
   <v-app v-cloak>
-    <loading v-if="$apollo.loading && !systemProperties"> </loading>
+    <loading v-if="$apollo.loading && !systemProperties" splash> </loading>
     <div v-else>
       <v-navigation-drawer app v-model="drawer">
         <v-list nav dense shaped>
diff --git a/aleksis/core/assets/components/Loading.vue b/aleksis/core/assets/components/Loading.vue
index 379d336756739e3f1700cca8c2a18a54aee7c193..e0493da6c620a602bc3a45530824c51c96f06988 100644
--- a/aleksis/core/assets/components/Loading.vue
+++ b/aleksis/core/assets/components/Loading.vue
@@ -1,9 +1,29 @@
 <template>
-  <div class="d-flex justify-center align-center progress-container">
+  <div
+      v-if="splash"
+      id="logo-container"
+  >
+    <img
+        src="/logo"
+        alt="Logo"
+        id="logo"
+        width="600"
+    >
+    <div class="lds-ellipsis">
+      <div></div>
+      <div></div>
+      <div></div>
+      <div></div>
+    </div>
+  </div>
+  <div
+      v-else
+      class="d-flex justify-center align-center progress-container"
+  >
     <v-progress-circular
-      indeterminate
-      color="primary"
-      :size="60"
+        indeterminate
+        color="primary"
+        :size="60"
     ></v-progress-circular>
   </div>
 </template>
@@ -29,4 +49,81 @@ export default {
   bottom: 10%;
   z-index: 1000;
 }
+
+#logo {
+  width: 100%;
+}
+
+#logo-container {
+  width: min(80vw, 600px);
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  text-align: center;
+}
+
+.lds-ellipsis {
+  display: inline-block;
+  position: relative;
+  width: 80px;
+  height: 80px;
+}
+
+.lds-ellipsis div {
+  position: absolute;
+  top: 33px;
+  width: 13px;
+  height: 13px;
+  border-radius: 50%;
+  background: v-bind("$vuetify.theme.currentTheme.primary");
+  animation-timing-function: cubic-bezier(0, 1, 1, 0);
+}
+
+.lds-ellipsis div:nth-child(1) {
+  left: 8px;
+  animation: lds-ellipsis1 0.6s infinite;
+}
+
+.lds-ellipsis div:nth-child(2) {
+  left: 8px;
+  animation: lds-ellipsis2 0.6s infinite;
+}
+
+.lds-ellipsis div:nth-child(3) {
+  left: 32px;
+  animation: lds-ellipsis2 0.6s infinite;
+}
+
+.lds-ellipsis div:nth-child(4) {
+  left: 56px;
+  animation: lds-ellipsis3 0.6s infinite;
+}
+
+@keyframes lds-ellipsis1 {
+  0% {
+    transform: scale(0);
+  }
+  100% {
+    transform: scale(1);
+  }
+}
+
+@keyframes lds-ellipsis3 {
+  0% {
+    transform: scale(1);
+  }
+  100% {
+    transform: scale(0);
+  }
+}
+
+@keyframes lds-ellipsis2 {
+  0% {
+    transform: translate(0, 0);
+  }
+  100% {
+    transform: translate(24px, 0);
+  }
+}
 </style>
diff --git a/aleksis/core/assets/components/generic/AvatarClickbox.vue b/aleksis/core/assets/components/generic/AvatarClickbox.vue
new file mode 100644
index 0000000000000000000000000000000000000000..faa2ba173e5e9c4dc08d6048c133adf1cad30300
--- /dev/null
+++ b/aleksis/core/assets/components/generic/AvatarClickbox.vue
@@ -0,0 +1,32 @@
+<template>
+    <v-dialog v-model="overlay" max-width="fit-content" max-height="fit-content">
+      <template #activator="{ on, attrs }">
+        <v-card class="rounded-circle">
+          <v-responsive :aspect-ratio="1" v-bind="attrs" v-on="on">
+            <slot name="activator" />
+          </v-responsive>
+        </v-card>
+      </template>
+      <div class="inDialog">
+        <slot class="inDialog" />
+      </div>
+    </v-dialog>
+  </template>
+  
+  <script>  
+  export default {
+    name: "AvatarClickBox",
+    data: () => ({
+      overlay: false,
+    }),
+  };
+  </script>
+  
+  <style scoped>
+  .inDialog {
+    /* FIXME: find a way to enlarge image */
+    max-height: 80vmin;
+    width: 80vmin;
+  }
+  </style>
+  
\ No newline at end of file
diff --git a/aleksis/core/assets/components/generic/DetailView.vue b/aleksis/core/assets/components/generic/DetailView.vue
index 843dacefab4c49f2c2f827c42b4b56f7aa49b9b5..dfe6e482bf7694b53ffdf27d9241867e4c543b9f 100644
--- a/aleksis/core/assets/components/generic/DetailView.vue
+++ b/aleksis/core/assets/components/generic/DetailView.vue
@@ -25,11 +25,10 @@
       </v-col>
 
       <v-col order="1" order-sm="last" class="ms-5">
-        <div
-          class="d-flex gap justify-md-end flex-column-reverse flex-md-row align-end align-md-center"
-        >
-          <slot name="actions" />
-        </div>
+        <slot
+          name="actions"
+          v-bind:classes="'d-flex gap justify-md-end flex-column-reverse flex-md-row align-end align-md-center'"
+        />
       </v-col>
     </v-row>
     <slot />
diff --git a/aleksis/core/assets/components/person/AvatarClickBox.vue b/aleksis/core/assets/components/person/AvatarClickBox.vue
deleted file mode 100644
index fc60bd301db1d8df5b23639cafa2708f46550033..0000000000000000000000000000000000000000
--- a/aleksis/core/assets/components/person/AvatarClickBox.vue
+++ /dev/null
@@ -1,39 +0,0 @@
-<template>
-  <v-dialog v-model="overlay" max-width="fit-content" max-height="fit-content">
-    <template #activator="{ on, attrs }">
-      <v-card class="rounded-circle">
-        <v-responsive :aspect-ratio="1" v-bind="attrs" v-on="on">
-          <avatar-content :id="id" class="rounded-circle" />
-        </v-responsive>
-      </v-card>
-    </template>
-    <avatar-content :id="id" contain class="inDialog" />
-  </v-dialog>
-</template>
-
-<script>
-import AvatarContent from "./AvatarContent.vue";
-
-export default {
-  name: "AvatarClickBox",
-  components: { AvatarContent },
-  data: () => ({
-    overlay: false,
-  }),
-  props: {
-    id: {
-      type: String,
-      required: false,
-      default: "",
-    },
-  },
-};
-</script>
-
-<style scoped>
-.inDialog {
-  /* FIXME: find a way to enlarge image */
-  max-height: 80vmin;
-  width: 80vmin;
-}
-</style>
diff --git a/aleksis/core/assets/components/person/PersonAvatarClickbox.vue b/aleksis/core/assets/components/person/PersonAvatarClickbox.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1a5de2a12879d37716305e85d50304b2fa75f4c1
--- /dev/null
+++ b/aleksis/core/assets/components/person/PersonAvatarClickbox.vue
@@ -0,0 +1,33 @@
+<template>
+  <avatar-clickbox>
+    <template #activator>
+      <avatar-content :id="id" class="rounded-circle" />
+    </template>
+    <avatar-content :id="id" contain />
+  </avatar-clickbox>
+</template>
+
+<script>
+import AvatarClickbox from "../generic/AvatarClickbox.vue";
+import AvatarContent from "./AvatarContent.vue";
+
+export default {
+  name: "PersonAvatarClickBox",
+  components: {
+    AvatarClickbox,
+    AvatarContent,
+  },
+  data: () => ({
+    overlay: false,
+  }),
+  props: {
+    id: {
+      type: String,
+      required: false,
+      default: "",
+    },
+  },
+};
+</script>
+
+<style scoped></style>
diff --git a/aleksis/core/assets/components/person/PersonOverview.vue b/aleksis/core/assets/components/person/PersonOverview.vue
index 43fe4620f2414bbecb6abf7207aed01cf04311a5..62b4962fe9385a8dbea2803ad459a3b786408e17 100644
--- a/aleksis/core/assets/components/person/PersonOverview.vue
+++ b/aleksis/core/assets/components/person/PersonOverview.vue
@@ -13,7 +13,7 @@
       <template v-else-if="data && data.person">
         <detail-view>
           <template #avatarContent>
-            <avatar-click-box :id="id" />
+            <person-avatar-clickbox :id="id" />
           </template>
 
           <template #title>
@@ -24,8 +24,8 @@
             {{ data.person.username }}
           </template>
 
-          <template #actions>
-            <person-actions :id="data.person.id" />
+          <template #actions="{ classes }">
+            <person-actions :class="classes" :id="data.person.id" />
           </template>
 
           <div class="text-center my-5" v-text="data.person.description"></div>
@@ -204,20 +204,20 @@
 
 <script>
 import AdditionalImage from "./AdditionalImage.vue";
-import AvatarClickBox from "./AvatarClickBox.vue";
 import DetailView from "../generic/DetailView.vue";
 import GroupCollection from "../group/GroupCollection.vue";
 import PersonActions from "./PersonActions.vue";
+import PersonAvatarClickbox from "./PersonAvatarClickbox.vue";
 import PersonCollection from "./PersonCollection.vue";
 
 export default {
   name: "PersonOverview",
   components: {
     AdditionalImage,
-    AvatarClickBox,
     DetailView,
     GroupCollection,
     PersonActions,
+    PersonAvatarClickbox,
     PersonCollection,
   },
   props: {
diff --git a/aleksis/core/templates/core/partials/splash_screen.html b/aleksis/core/templates/core/partials/splash_screen.html
index 00e01649894a2a83c7d0129580034350fed77c3a..243468a47b5634b9797512ff7aa1887378f9f588 100644
--- a/aleksis/core/templates/core/partials/splash_screen.html
+++ b/aleksis/core/templates/core/partials/splash_screen.html
@@ -1,5 +1,5 @@
 {% load static any_js i18n %}
-{% include_css "Roboto300" %}
+{% include_css "Roboto400" %}
 {% static "img/aleksis-banner.svg" as aleksis_banner %}
 <div id="logo-container">
   <img
@@ -8,14 +8,11 @@
     id="logo"
     width="600"
   >
-  <div id="text">
-    <h1>{{ request.site.preferences.general__title }}</h1>
-    <div class="lds-ellipsis">
-      <div></div>
-      <div></div>
-      <div></div>
-      <div></div>
-    </div>
+  <div class="lds-ellipsis">
+    <div></div>
+    <div></div>
+    <div></div>
+    <div></div>
   </div>
   <noscript>
     {% blocktrans %}
@@ -35,17 +32,12 @@
     top: 50%;
     left: 50%;
     transform: translate(-50%, -50%);
+    text-align: center;
   }
 
-  #text {
-    display: flex;
-    justify-content: space-around;
-    flex-wrap: wrap;
-  }
-
-  h1 {
+  noscript {
     font-family: Roboto, sans-serif;
-    font-weight: 300;
+    font-weight: 400;
   }
 
   .lds-ellipsis {
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index a8cee0d037d7b06540a224a7a69c93f781fd74d2..c25861a504419b364b6f96d72769d658ed1e01c2 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -24,6 +24,7 @@ urlpatterns = [
     path(settings.MEDIA_URL.removeprefix("/"), include("titofisto.urls")),
     path("__icons__/", include("dj_iconify.urls")),
     path("graphql/", csrf_exempt(GraphQLView.as_view(graphiql=True)), name="graphql"),
+    path("logo", views.LogoView.as_view(), name="logo"),
     path(
         "django/",
         include(
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 29d5ca002594e9ea9a989330c66d239e3eb856b3..ede1cd0e41bbc0d84104eaefdbf3e7dbd13f1b1a 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -23,6 +23,7 @@ from django.http import (
 )
 from django.shortcuts import get_object_or_404, redirect, render
 from django.template import loader
+from django.templatetags.static import static
 from django.urls import reverse, reverse_lazy
 from django.utils import timezone
 from django.utils.decorators import method_decorator
@@ -137,6 +138,16 @@ from .util.forms import PreferenceLayout
 from .util.pdf import render_pdf
 
 
+class LogoView(View):
+    def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
+        image = request.site.preferences["theme__logo"]
+        if image:
+            image = image.url
+        else:
+            image = static("img/aleksis-banner.svg")
+        return redirect(image)
+
+
 class RenderPDFView(TemplateView):
     """View to render a PDF file from a template.