diff --git a/app/controllers/course/basicdata.php b/app/controllers/course/basicdata.php
index f4f50feef18298ce7ea99f803a15547cbaedde0c..97ec0531cbbabff27a16c40aa9ff6ec4d212dfd9 100644
--- a/app/controllers/course/basicdata.php
+++ b/app/controllers/course/basicdata.php
@@ -445,6 +445,14 @@ class Course_BasicdataController extends AuthenticatedController
             $widget = new CourseManagementSelectWidget();
             $sidebar->addWidget($widget);
         }
+
+        foreach ($this->flash['msg'] ?? [] as $msg) {
+            match ($msg[0]) {
+                'msg'   => PageLayout::postSuccess($msg[1]),
+                'error' => PageLayout::postError($msg[1]),
+                'info'  => PageLayout::postInfo($msg[1]),
+            };
+        }
     }
 
     /**
diff --git a/app/controllers/settings/general.php b/app/controllers/settings/general.php
index 734cca1a796cf3661db783cb2aa4cb0022957ba5..0e8ec70a19db88a0d43b587a8b695a6810489455 100644
--- a/app/controllers/settings/general.php
+++ b/app/controllers/settings/general.php
@@ -44,6 +44,7 @@ class Settings_GeneralController extends Settings_SettingsController
     public function index_action()
     {
         $this->user_language = getUserLanguage($this->user->id);
+        $this->notifications_placement = User::findCurrent()->getConfiguration()->SYSTEM_NOTIFICATIONS_PLACEMENT;
     }
 
     /**
@@ -80,6 +81,7 @@ class Settings_GeneralController extends Settings_SettingsController
         } else {
             PersonalNotifications::deactivateAudioFeedback($this->user->id);
         }
+        $this->config->store('SYSTEM_NOTIFICATIONS_PLACEMENT', Request::get('system_notifications_placement'));
 
         PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.'));
         $this->redirect('settings/general');
diff --git a/app/views/course/basicdata/view.php b/app/views/course/basicdata/view.php
index 380fdb48ecfdae75b7f0c1ffa888b5d998b55653..9438aeb7ca37d1f0178dcd6f8ed7d617d0854989 100644
--- a/app/views/course/basicdata/view.php
+++ b/app/views/course/basicdata/view.php
@@ -12,14 +12,8 @@ use Studip\Button, Studip\LinkButton;
  */
 
 $dialog_attr = Request::isXhr() ? ' data-dialog="size=50%"' : '';
-
-$message_types = ['msg' => "success", 'error' => "error", 'info' => "info"];
 ?>
 
-<? if (is_array($flash['msg'])) foreach ($flash['msg'] as $msg) : ?>
-    <?= MessageBox::{$message_types[$msg[0]]}($msg[1]) ?>
-<? endforeach ?>
-
 <form name="course-details" name="details" method="post" action="<?= $controller->link_for('course/basicdata/set', $course_id) ?>" <?= $dialog_attr ?> class="default collapsable">
     <?= CSRFProtection::tokenTag() ?>
     <input id="open_variable" type="hidden" name="open" value="<?= $flash['open'] ?>">
diff --git a/app/views/settings/general.php b/app/views/settings/general.php
index 4bd543388973171c5cbced5bd4df62508a5fa35b..32b1a6f98b851ba05eee7ecf9ef4ae2fa5a0c9c7 100644
--- a/app/views/settings/general.php
+++ b/app/views/settings/general.php
@@ -93,6 +93,21 @@ $start_pages = [
                 '- auch wenn Sie gerade einen anderen Browsertab anschauen. Der Plopp ist ' .
                 'nur zu hören, wenn Sie die Benachrichtigungen über Javascript aktiviert haben.')) ?>
         </label>
+
+        <label>
+            <?= _('Platzierung von Systembenachrichtigungen im Browserfenster') ?>
+            <?= tooltipIcon(_('Sie können entscheiden, an welcher Stelle Ihres Browserfensters ' .
+                'Systembenachrichtigungen erscheinen sollen: mittig am oberen Rand oder rechts unten.')) ?>
+            <select name="system_notifications_placement"
+                   aria-describedby="system_notifications_notifications_placement_description">
+                <option value="topcenter"<?= $notifications_placement === 'topcenter' ? ' selected' : '' ?>>
+                    <?= _('zentriert am oberen Rand') ?>
+                </option>
+                <option value="bottomright"<?= $notifications_placement === 'bottomright' ? ' selected' : '' ?>>
+                    <?= _('am rechten unteren Rand') ?>
+                </option>
+            </select>
+        </label>
     </fieldset>
 
     <fieldset>
diff --git a/db/migrations/6.0.9_notification_positioning.php b/db/migrations/6.0.9_notification_positioning.php
new file mode 100644
index 0000000000000000000000000000000000000000..d6693f3e56be1e9de6e1528a4c5949a62546cbbf
--- /dev/null
+++ b/db/migrations/6.0.9_notification_positioning.php
@@ -0,0 +1,35 @@
+<?php
+class NotificationPositioning extends Migration
+{
+    public function description()
+    {
+        return 'Adds a personal config option to select the on-screen position of system notifications';
+    }
+
+    protected function up()
+    {
+        DBManager::get()->execute(
+            "INSERT IGNORE INTO `config` VALUES (:field, :value, :type, :range, :section, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), :desc)",
+            [
+                'field' => 'SYSTEM_NOTIFICATIONS_PLACEMENT',
+                'value' => 'topcenter',
+                'type' => 'string',
+                'range' => 'user',
+                'section' => '',
+                'desc' => 'Wo sollen Systembenachrichtigungen im Fenster angezeigt werden? Gültige Werte sind "topcenter" und "bottomright"'
+            ]
+        );
+    }
+
+    protected function down()
+    {
+        DBManager::get()->execute(
+            "DELETE FROM `config_values` WHERE `field` = :field",
+            ['field' => 'SYSTEM_NOTIFICATIONS_PLACEMENT']
+        );
+        DBManager::get()->execute(
+            "DELETE FROM `config` WHERE `field` = :field",
+            ['field' => 'SYSTEM_NOTIFICATIONS_PLACEMENT']
+        );
+    }
+};
diff --git a/lib/classes/MessageBox.class.php b/lib/classes/MessageBox.class.php
index c0d94b297edd31ad68150f273b9707f88f1254ff..8936ebec08790e152a1ce836c378a203e0386134 100644
--- a/lib/classes/MessageBox.class.php
+++ b/lib/classes/MessageBox.class.php
@@ -141,6 +141,15 @@ class MessageBox implements LayoutMessage
         return $this;
     }
 
+    /**
+     * Return whether this messagebox can be closed or not.
+     * @return bool
+     */
+    public function isCloseable(): bool
+    {
+        return $this->hide_close;
+    }
+
     /**
      * This method renders a MessageBox object to a string.
      *
diff --git a/lib/classes/PageLayout.php b/lib/classes/PageLayout.php
index d6b28ec0a0782acb838a8dd7972db274391d21b3..7f95b7aaa1d028d78d6f6dbd06638820f9ad3298 100644
--- a/lib/classes/PageLayout.php
+++ b/lib/classes/PageLayout.php
@@ -599,10 +599,18 @@ class PageLayout
         if (!isset($_SESSION['messages'])) {
             $_SESSION['messages'] = [];
         }
+
+        $structure = [
+            'type' => $message->class,
+            'message' => $message->message,
+            'details' => $message->details,
+            'closeable' => $message->isCloseable()
+        ];
+
         if ($id === null ) {
-            $_SESSION['messages'][] = $message;
+            $_SESSION['messages'][] = $structure;
         } else {
-            $_SESSION['messages'][$id] = $message;
+            $_SESSION['messages'][$id] = $structure;
         }
     }
 
diff --git a/lib/phplib/Seminar_Auth.class.php b/lib/phplib/Seminar_Auth.class.php
index 4849ece82f6442ffcbe3bf05bae223bfe4d34096..2c1c60c3b92df68c1fa03519fcdd8bb8b5877d77 100644
--- a/lib/phplib/Seminar_Auth.class.php
+++ b/lib/phplib/Seminar_Auth.class.php
@@ -336,7 +336,9 @@ class Seminar_Auth
         } else {
             unset($_SESSION['semi_logged_in']); // used by email activation
             $login_template = $GLOBALS['template_factory']->open('loginform');
-            $login_template->set_attribute('loginerror', (isset($this->auth["uname"]) && $this->error_msg));
+            if (isset($this->auth['uname']) && $this->error_msg) {
+                PageLayout::postException(_('Bei der Anmeldung trat ein Fehler auf!'), $this->error_msg);
+            }
             $login_template->set_attribute('error_msg', $this->error_msg);
             $login_template->set_attribute('uname', (isset($this->auth["uname"]) ? $this->auth["uname"] : Request::username('loginname')));
             $login_template->set_attribute('self_registration_activated', Config::get()->ENABLE_SELF_REGISTRATION);
diff --git a/resources/assets/javascripts/bootstrap/system-notifications.js b/resources/assets/javascripts/bootstrap/system-notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..7a85fcdbfbce669b45a63051e5016ca3464edfab
--- /dev/null
+++ b/resources/assets/javascripts/bootstrap/system-notifications.js
@@ -0,0 +1,11 @@
+import SystemNotificationManager from '../../../vue/components/SystemNotificationManager.vue';
+
+STUDIP.domReady(() => {
+    document.getElementById('system-notifications')?.classList.add('vueified');
+    STUDIP.Vue.load().then(({ createApp }) => {
+        createApp({
+            el: '#system-notifications',
+            components: { SystemNotificationManager }
+        });
+    });
+});
diff --git a/resources/assets/javascripts/entry-base.js b/resources/assets/javascripts/entry-base.js
index 26834395e6d69b63c80b801f7f8346b20e59a847..5f88c3069ba8f098cad208ad8221efd838bd01ce 100644
--- a/resources/assets/javascripts/entry-base.js
+++ b/resources/assets/javascripts/entry-base.js
@@ -16,6 +16,8 @@ import "./init.js"
 import "./bootstrap/responsive.js"
 import "./bootstrap/vue.js"
 
+import "./bootstrap/system-notifications.js"
+
 import "./bootstrap/my-courses.js";
 
 import "./studip-ui.js"
diff --git a/resources/assets/stylesheets/scss/responsive.scss b/resources/assets/stylesheets/scss/responsive.scss
index c4317e933e7ee67c0716e1475d2c16f8cbf5b1d2..e84a8a800521436c64fd8f77efafc4f4e1069802 100644
--- a/resources/assets/stylesheets/scss/responsive.scss
+++ b/resources/assets/stylesheets/scss/responsive.scss
@@ -537,6 +537,26 @@ $sidebarOut: -330px;
             }
         }
     }
+
+    #system-notifications {
+        top: 0;
+        position: fixed;
+        left: 0;
+        width: 100%;
+        z-index: 1001;
+    }
+
+    .system-notification {
+        &.system-notification-slide-enter,
+        &.system-notification-slide-leave-to {
+            transform: translateY(-100%);
+        }
+
+        &.system-notification-slide-leave,
+        &.system-notification-slide-enter-to {
+            transform: translateY(0);
+        }
+    }
 }
 
 /* Settings especially for fullscreen mode */
diff --git a/resources/assets/stylesheets/scss/system-notifications.scss b/resources/assets/stylesheets/scss/system-notifications.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1da1ec249037bcd129646d24d69cb9071b2ca963
--- /dev/null
+++ b/resources/assets/stylesheets/scss/system-notifications.scss
@@ -0,0 +1,174 @@
+#system-notifications {
+    &.bottom-right {
+        bottom: 50px;
+        right: 15px;
+
+        .system-notification {
+            box-shadow: 5px 5px var(--dark-gray-color-10);
+
+            &.system-notification-slide-enter,
+            &.system-notification-slide-leave-to {
+                opacity: 0;
+                transform: translateX(100%);
+            }
+
+            &.system-notification-slide-leave,
+            &.system-notification-slide-enter-to {
+                opacity: 1;
+                transform: translateX(0);
+            }
+        }
+    }
+
+    &.top-center {
+        left: calc(50% - 300px);
+        top: 0;
+
+        .system-notification {
+            box-shadow: 0 0 0 3px var(--dark-gray-color-10);
+
+            &.system-notification-slide-enter,
+            &.system-notification-slide-leave-to {
+                opacity: 0;
+                transform: translateY(-100%);
+            }
+
+            &.system-notification-slide-leave,
+            &.system-notification-slide-enter-to {
+                opacity: 1;
+                transform: translateY(0);
+            }
+        }
+    }
+
+    &:not(.system-notifications-login) {
+        position: fixed;
+        width: 600px;
+    }
+
+    &.system-notifications-login {
+        margin-bottom: 15px;
+    }
+
+    overflow: hidden;
+    z-index: 1001;
+}
+
+.system-notification {
+    background-color: var(--content-color-20);
+    border: thin solid var(--content-color-40);
+    color: var(--black);
+    cursor: pointer;
+    display: flex;
+    padding: 10px;
+    position: relative;
+
+    &.system-notification-slide-enter-active,
+    &.system-notification-slide-leave-active {
+        transition: all var(--transition-duration-slow) ease-in-out;
+    }
+
+    .system-notification-icon {
+        flex: 0;
+        padding: 10px;
+    }
+
+    .system-notification-content {
+        flex: 1;
+        height: auto;
+        margin: auto 25px auto 10px;
+        word-wrap: break-word;
+
+        .system-notification-details {
+            .system-notification-details-content {
+                padding-left: 10px;
+            }
+        }
+    }
+
+    .system-notification-close {
+        align-self: normal;
+        flex: 0;
+        height: 20px;
+        width: 20px;
+
+        img, svg {
+            cursor: pointer;
+            position: absolute;
+            right: 10px;
+            top: 10px;
+        }
+    }
+
+    .system-notification-timeout {
+        &.system-notification-timeout-enter-active,
+        &.system-notification-timeout-leave-active {
+            transition: width 5s linear;
+        }
+
+        &.system-notification-timeout-enter {
+            width: 100%;
+        }
+
+        &.system-notification-timeout-leave {
+            width: 0;
+        }
+
+        background-color: var(--base-color-40);
+        bottom: 0;
+        height: 5px;
+        left: 0;
+        position: absolute;
+        width: 0;
+    }
+
+    a:not(.system-notification-message) {
+        color: var(--black);
+        text-decoration-line: underline;
+    }
+
+    a.system-notification-message {
+        color: var(--text-color);
+        text-decoration: unset;
+    }
+}
+
+.system-notification-exception {
+    background-color: var(--red-40);
+
+    .system-notification-timeout {
+        background-color: var(--red);
+    }
+}
+
+.system-notification-error {
+    background-color: var(--red-20);
+
+    .system-notification-timeout {
+        background-color: var(--red-80);
+    }
+}
+
+.system-notification-warning {
+    background-color: var(--yellow-20);
+
+    .system-notification-timeout {
+        background-color: var(--yellow-80);
+    }
+}
+
+.system-notification-success {
+    background-color: var(--green-20);
+
+    .system-notification-timeout {
+        background-color: var(--green-80);
+    }
+}
+
+.system-notification-info {
+    background-color: var(--dark-violet-20);
+
+    .system-notification-timeout {
+        background-color: var(--dark-violet-60);
+    }
+}
diff --git a/resources/assets/stylesheets/studip.scss b/resources/assets/stylesheets/studip.scss
index 0f329a497e3899a276910387be1d7d6537ee5800..a322400ecb67c4317a39b6096a2c733f9bde38e0 100644
--- a/resources/assets/stylesheets/studip.scss
+++ b/resources/assets/stylesheets/studip.scss
@@ -96,6 +96,7 @@
 @import "scss/studygroup";
 @import "scss/studip-overlay";
 @import "scss/studip-selection";
+@import "scss/system-notifications";
 @import "scss/table_of_contents";
 @import "scss/tables";
 @import "scss/tabs";
diff --git a/resources/vue/components/SystemNotification.vue b/resources/vue/components/SystemNotification.vue
new file mode 100644
index 0000000000000000000000000000000000000000..bf804ae3f214b8203764bc849dd7fad28923cfec
--- /dev/null
+++ b/resources/vue/components/SystemNotification.vue
@@ -0,0 +1,156 @@
+<template>
+    <transition name="system-notification-slide" appear>
+        <div v-if="showMe"
+            :class="'system-notification system-notification-' + notification.type"
+            @mouseover="disruptTimeout"
+            @mouseout="initTimeout"
+            @focus="disruptTimeout"
+            @blur="initTimeout">
+            <div class="system-notification-icon">
+                <studip-icon :shape="icon.shape"
+                             :size="48"
+                             :role="icon.color"
+                             alt=""
+                             title=""></studip-icon>
+            </div>
+            <div class="system-notification-content">
+                <p v-html="notification.message"></p>
+                <p class="sr-only" v-if="hasTimeout">
+                    {{ $gettext('Strg+Alt+T hält das automatische Ausblenden der Meldung an bzw. setzt es wieder fort.') }}
+                </p>
+                <details v-if="notification.details?.length > 0"
+                     class="system-notification-details">
+                    <summary>
+                        {{ $gettext('Details') }}
+                    </summary>
+                    <template v-if="Array.isArray(notification.details)">
+                        <p v-for="(detail, index) in notification.details"
+                           :key="index"
+                           v-html="detail"></p>
+                    </template>
+                    <p v-else v-html="notification.details"></p>
+                </details>
+            </div>
+            <button v-if="allowClosing"
+                    class="system-notification-close undecorated"
+                    :title="$gettext('Diese Meldung schließen')"
+                    @click.prevent="destroyMe"
+                    @keydown.space="destroyMe"
+                    tabindex="0">
+                <studip-icon shape="decline"
+                             :size="20"
+                             class="close-system-notification"/>
+            </button>
+            <transition v-if="hasTimeout"
+                        name="system-notification-timeout"
+                        appear>
+                <div v-if="!stopTimeout"
+                     class="system-notification-timeout"
+                     ref="timeout-counter"></div>
+            </transition>
+        </div>
+    </transition>
+</template>
+
+<script>
+export default {
+    name: 'SystemNotification',
+    props: {
+        notification: {
+            type: Object,
+            required: true
+        },
+        visibleFor: {
+            type: Number,
+            default: 5000
+        },
+        appendTo: {
+            type: String,
+            default: null
+        },
+        allowClosing: {
+            type: Boolean,
+            default: true
+        }
+    },
+    data() {
+        return {
+            showMe: false,
+            stopTimeout: false
+        }
+    },
+    computed: {
+        icon() {
+            let iconShape = 'info-circle';
+            let iconColor = 'info';
+            switch (this.type) {
+                case 'exception':
+                    iconShape = 'exclaim-circle';
+                    iconColor = 'info_alt';
+                    break;
+                case 'error':
+                    iconShape = 'exclaim-circle';
+                    iconColor = 'status-red';
+                    break;
+                case 'warning':
+                    iconShape = 'exclaim-circle';
+                    iconColor = 'status-yellow';
+                    break;
+                case 'success':
+                    iconShape = 'check-circle';
+                    iconColor = 'status-green';
+                    break;
+            }
+            return { shape: iconShape, color: iconColor };
+        },
+        hasTimeout() {
+            return !['exception', 'error'].includes(this.notification.type);
+        }
+    },
+    methods: {
+        initTimeout() {
+            if (this.hasTimeout && this.visibleFor > 0) {
+                this.stopTimeout = false;
+                setTimeout(() => {
+                    if (!this.stopTimeout) {
+                        this.destroyMe();
+                    }
+                }, this.visibleFor);
+            }
+        },
+        disruptTimeout() {
+            this.stopTimeout = true;
+        },
+        destroyMe() {
+            this.showMe = false;
+            this.$emit('destroyMe');
+        }
+    },
+    mounted() {
+        if (this.appendTo !== null) {
+            const target = document.querySelector(this.appendTo);
+
+            // Create a live area for screen reader compatibility.
+            const div = document.createElement('div');
+            div.setAttribute('role', 'alert');
+            div.appendChild(this.$el);
+            if (target) {
+                target.prepend(div);
+            }
+        }
+
+        if (!STUDIP.config?.PERSONAL_NOTIFICATIONS_AUDIO_DEACTIVATED) {
+            const audio = new Audio(STUDIP.ASSETS_URL + '/sounds/blubb.mp3');
+            audio.play();
+        }
+        this.showMe = true;
+
+        this.initTimeout();
+        window.addEventListener('keydown', evt => {
+            if (evt.altKey && evt.ctrlKey && evt.code === 'KeyT') {
+                this.stopTimeout ? this.initTimeout() : this.disruptTimeout();
+            }
+        })
+    }
+}
+</script>
diff --git a/resources/vue/components/SystemNotificationManager.vue b/resources/vue/components/SystemNotificationManager.vue
new file mode 100644
index 0000000000000000000000000000000000000000..72c4dbfdaae400e241e85d5ac7e3b5fc104cf1a8
--- /dev/null
+++ b/resources/vue/components/SystemNotificationManager.vue
@@ -0,0 +1,52 @@
+<template>
+    <div role="alert"
+         :class="'system-notifications ' + (placement === 'topcenter' ? 'top-center' : 'bottom-right')">
+        <system-notification v-for="(notification, index) in allNotifications"
+                             :key="'message-' + index"
+                             :notification="notification"></system-notification>
+    </div>
+</template>
+
+<script>
+import SystemNotification from './SystemNotification.vue';
+
+export default {
+    name: 'SystemNotificationManager',
+    components: { SystemNotification },
+    props: {
+        notifications: {
+            type: Array,
+            default: () => []
+        },
+        placement: {
+            type: String,
+            default: 'topcenter',
+            validator: value => {
+                return ['topcenter', 'bottomright'].includes(value);
+            }
+        },
+        appendAllTo: {
+            type: String,
+            default: null
+        }
+    },
+    data() {
+        return {
+            allNotifications: this.notifications
+        }
+    },
+    methods: {
+        getNotifications(type) {
+            return this.allNotifications.filter((n) => n.type === type);
+        },
+        destroyNotification(type, index) {
+
+        }
+    },
+    mounted() {
+        this.globalOn('push-system-notification', notification => {
+            this.allNotifications.push(notification);
+        });
+    }
+}
+</script>
diff --git a/resources/vue/components/courseware/layouts/CoursewareCompanionBox.vue b/resources/vue/components/courseware/layouts/CoursewareCompanionBox.vue
index f617e5ad2575b5945503bf0b0921b09ff753f5b0..c40edd1e678def7ba43336ec435b28aba62a64c4 100644
--- a/resources/vue/components/courseware/layouts/CoursewareCompanionBox.vue
+++ b/resources/vue/components/courseware/layouts/CoursewareCompanionBox.vue
@@ -1,12 +1,3 @@
-<template>
-    <div class="cw-companion-box" :class="[mood]">
-        <div>
-            <p v-html="msgCompanion"></p>
-            <slot name="companionActions"></slot>
-        </div>
-    </div>
-</template>
-
 <script>
 export default {
     name: 'courseware-companion-box',
@@ -20,5 +11,40 @@ export default {
             }
         }
     },
+    computed: {
+        msgType() {
+            let type = 'info';
+            switch (this.mood) {
+                case 'special':
+                case 'unsure':
+                    type = 'warning';
+                    break;
+                case 'sad':
+                    type = 'error';
+                    break;
+                case 'happy':
+                    type = 'success';
+                    break
+                case 'pointing':
+                case 'curious':
+            }
+            return type;
+        }
+    },
+    watch: {
+        msgCompanion: {
+            handler(current) {
+                if (current.trim().length === 0) {
+                    return;
+                }
+                const notification = {
+                    type: this.msgType,
+                    message: current
+                };
+                this.globalEmit('push-system-notification', notification);
+            },
+            immediate: true
+        }
+    }
 };
-</script>
\ No newline at end of file
+</script>
diff --git a/resources/vue/components/courseware/layouts/CoursewareCompanionOverlay.vue b/resources/vue/components/courseware/layouts/CoursewareCompanionOverlay.vue
index ff177f7e177d2c8961eca3113a28d6066864ffd9..a45da116d836ad87fb2e949a6efc5e056e7d05d9 100644
--- a/resources/vue/components/courseware/layouts/CoursewareCompanionOverlay.vue
+++ b/resources/vue/components/courseware/layouts/CoursewareCompanionOverlay.vue
@@ -1,23 +1,3 @@
-<template>
-    <div class="cw-companion-overlay-wrapper">
-        <div
-            class="cw-companion-overlay"
-            :class="[showCompanion ? 'cw-companion-overlay-in' : '', showCompanion ? '' : 'cw-companion-overlay-out', styleCompanion]"
-            aria-hidden="true"
-        >
-            <div class="cw-companion-overlay-content" v-html="msgCompanion"></div>
-            <button class="cw-compantion-overlay-close" @click="hideCompanion"></button>
-        </div>
-        <div
-            class="sr-only"
-            aria-live="polite"
-            role="log"
-        >
-            <p>{{ msgCompanion }}</p>
-        </div>
-    </div>
-</template>
-
 <script>
 import { mapActions, mapGetters } from 'vuex';
 
@@ -30,6 +10,24 @@ export default {
             styleCompanion: 'styleCompanionOverlay',
             showToolbar: 'showToolbar',
         }),
+        msgType() {
+            let type = 'info';
+            switch (this.styleCompanion) {
+                case 'special':
+                case 'unsure':
+                    type = 'warning';
+                    break;
+                case 'sad':
+                    type = 'error';
+                    break;
+                case 'happy':
+                    type = 'success';
+                    break
+                case 'pointing':
+                case 'curious':
+            }
+            return type;
+        }
     },
     methods: {
         ...mapActions({
@@ -49,11 +47,24 @@ export default {
             }
         },
         showToolbar(newValue, oldValue) {
-            // hide companion when toolbar is closed 
+            // hide companion when toolbar is closed
             if (oldValue === true && newValue === false) {
                 this.hideCompanion();
             }
+        },
+        msgCompanion: {
+            handler(current) {
+                if (current.trim().length === 0) {
+                    return;
+                }
+                const notification = {
+                    type: this.msgType,
+                    message: current
+                };
+                this.globalEmit('push-system-notification', notification);
+            },
+            immediate: true
         }
-    },
+    }
 };
 </script>
diff --git a/resources/vue/store/courseware/courseware-shelf.module.js b/resources/vue/store/courseware/courseware-shelf.module.js
index dc92a23ae4601a15a48452ec86aec2fd2ee3af89..d907ee421c4215a5032a51dc1415ac1e75283a85 100644
--- a/resources/vue/store/courseware/courseware-shelf.module.js
+++ b/resources/vue/store/courseware/courseware-shelf.module.js
@@ -249,33 +249,23 @@ export const actions = {
         }
     },
     async companionInfo({ dispatch }, { info }) {
-        await dispatch('setStyleCompanionOverlay', 'default');
-        await dispatch('setMsgCompanionOverlay', info);
-        return dispatch('setShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'info', message: info});
     },
 
     async companionSuccess({ dispatch }, { info }) {
-        await dispatch('setStyleCompanionOverlay', 'happy');
-        await dispatch('setMsgCompanionOverlay', info);
-        return dispatch('setShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'success', message: info});
     },
 
     async companionError({ dispatch }, { info }) {
-        await dispatch('setStyleCompanionOverlay', 'sad');
-        await dispatch('setMsgCompanionOverlay', info);
-        return dispatch('setShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'error', message: info});
     },
 
     async companionWarning({ dispatch }, { info }) {
-        await dispatch('setStyleCompanionOverlay', 'alert');
-        await dispatch('setMsgCompanionOverlay', info);
-        return dispatch('setShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'warning', message: info});
     },
 
     async companionSpecial({ dispatch }, { info }) {
-        await dispatch('setStyleCompanionOverlay', 'special');
-        await dispatch('setMsgCompanionOverlay', info);
-        return dispatch('setShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'info', message: info});
     },
     coursewareShowCompanionOverlay({dispatch}, { data }) {
         return dispatch('setShowCompanionOverlay', data);
@@ -310,7 +300,7 @@ export const actions = {
 
         return dispatch(loadUnits, state.context.id);
     },
-    
+
     async sortUnits({ dispatch, state }, data) {
         let loadUnits = null;
         if (state.context.type === 'courses') {
@@ -321,7 +311,7 @@ export const actions = {
         }
 
         await state.httpClient.post(`${state.context.type}/${state.context.id}/courseware-units/sort`, {data: data});
-        
+
         return dispatch(loadUnits, state.context.id);
     },
 
diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js
index c44bba17ce5f467232a169a790bcb885cccadee4..784b75090dff4b5be7bcfd5610c590b3dd4fb1a7 100644
--- a/resources/vue/store/courseware/courseware.module.js
+++ b/resources/vue/store/courseware/courseware.module.js
@@ -874,33 +874,23 @@ export const actions = {
     },
 
     async companionInfo({ dispatch }, { info }) {
-        await dispatch('coursewareStyleCompanionOverlay', 'default');
-        await dispatch('coursewareMsgCompanionOverlay', info);
-        return dispatch('coursewareShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'info', message: info});
     },
 
     async companionSuccess({ dispatch }, { info }) {
-        await dispatch('coursewareStyleCompanionOverlay', 'happy');
-        await dispatch('coursewareMsgCompanionOverlay', info);
-        return dispatch('coursewareShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'success', message: info});
     },
 
     async companionError({ dispatch }, { info }) {
-        await dispatch('coursewareStyleCompanionOverlay', 'sad');
-        await dispatch('coursewareMsgCompanionOverlay', info);
-        return dispatch('coursewareShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'error', message: info});
     },
 
     async companionWarning({ dispatch }, { info }) {
-        await dispatch('coursewareStyleCompanionOverlay', 'alert');
-        await dispatch('coursewareMsgCompanionOverlay', info);
-        return dispatch('coursewareShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'exception', message: info});
     },
 
     async companionSpecial({ dispatch }, { info }) {
-        await dispatch('coursewareStyleCompanionOverlay', 'special');
-        await dispatch('coursewareMsgCompanionOverlay', info);
-        return dispatch('coursewareShowCompanionOverlay', true);
+        STUDIP.eventBus.emit('push-system-notification', { type: 'warning', message: info});
     },
 
     // adds a favorite block type using the `type` of the BlockType
diff --git a/templates/layouts/base.php b/templates/layouts/base.php
index 6597fd8e124d6cc89bc0b5075c1330f2ae3a9870..d7fc1f10d2bd7644c86e496e8462de0979bf0dc6 100644
--- a/templates/layouts/base.php
+++ b/templates/layouts/base.php
@@ -53,7 +53,9 @@ $lang_attr = str_replace('_', '-', $_SESSION['_language']);
                 'ACTIONMENU_THRESHOLD' => Config::get()->ACTION_MENU_THRESHOLD,
                 'ENTRIES_PER_PAGE'     => Config::get()->ENTRIES_PER_PAGE,
                 'OPENGRAPH_ENABLE'     => Config::get()->OPENGRAPH_ENABLE,
-                'COURSEWARE_CERTIFICATES_ENABLE' => Config::get()->COURSEWARE_CERTIFICATES_ENABLE
+                'COURSEWARE_CERTIFICATES_ENABLE' => Config::get()->COURSEWARE_CERTIFICATES_ENABLE,
+                'PERSONAL_NOTIFICATIONS_AUDIO_DEACTIVATED' =>
+                    (bool) User::findCurrent()->getConfiguration()->PERSONAL_NOTIFICATIONS_AUDIO_DEACTIVATED,
             ]) ?>,
         }
     </script>
@@ -90,9 +92,12 @@ $lang_attr = str_replace('_', '-', $_SESSION['_language']);
                     <?= Icon::create('zoom-out2')->asImg(24) ?>
                 </button>
             <? endif; ?>
-            <?= implode(PageLayout::getMessages()) ?>
             <?= $content_for_layout ?>
         </div>
+        <system-notification-manager
+            id="system-notifications"
+            :notifications='<?= htmlReady(json_encode(PageLayout::getMessages())) ?>'
+            placement="<?= User::findCurrent()->getConfiguration()->SYSTEM_NOTIFICATIONS_PLACEMENT ?>"></system-notification-manager>
     </main>
     <!-- End main content -->
 
diff --git a/templates/loginform.php b/templates/loginform.php
index 69fc1f06ab65df91b7740e656c7785e7ac5378ee..da717eb8e5ed595ca62d8ebe3a9ba485995978ea 100644
--- a/templates/loginform.php
+++ b/templates/loginform.php
@@ -32,20 +32,14 @@ $show_hidden_login = !$show_login && StudipAuthAbstract::isLoginEnabled();
 
     <div id="login_flex">
         <div>
-            <? if ($loginerror): ?>
-                <!-- failed login code -->
-                <?= MessageBox::error(_('Bei der Anmeldung trat ein Fehler auf!'), [
-                    $error_msg,
-                    sprintf(
-                        _('Bitte wenden Sie sich bei Problemen an: <a href="mailto:%1$s">%1$s</a>'),
-                        $GLOBALS['UNI_CONTACT']
-                    )
-                ]) ?>
-            <? endif ?>
-
-            <?= implode('', PageLayout::getMessages()); ?>
             <div id="loginbox">
                 <header>
+                    <system-notification-manager
+                        id="system-notifications"
+                        class="system-notifications-login"
+                        :notifications='<?= htmlReady(json_encode(PageLayout::getMessages())) ?>'
+                        append-all-to="#loginbox"></system-notification-manager>
+
                     <h1><?= htmlReady(Config::get()->UNI_NAME_CLEAN) ?></h1>
                 </header>
 
@@ -139,7 +133,6 @@ $show_hidden_login = !$show_login && StudipAuthAbstract::isLoginEnabled();
 
     </div>
 
-
 </main>
 
 <script type="text/javascript" language="javascript">