From 936d1c00ea05bb0a5c56f441ff2eece6ccefada6 Mon Sep 17 00:00:00 2001
From: Thomas Hackl <hackl@data-quest.de>
Date: Mon, 12 Dec 2022 15:20:27 +0000
Subject: [PATCH] Resolve "StEP00348: Responsive Navigation Stud.IP 5.x"

Closes #32

Merge request studip/studip!65
---
 app/controllers/oer/market.php                |   9 +-
 app/views/profile/extern.php                  |   2 +-
 app/views/profile/index.php                   |   2 +-
 lib/classes/ContentBar.php                    |  34 +-
 lib/classes/ResponsiveHelper.php              | 173 +++-
 lib/classes/SkipLinks.php                     |  24 +-
 lib/classes/WidgetContainer.php               |  20 +-
 lib/classes/sidebar/Sidebar.php               |   6 +-
 lib/include/html_head.inc.php                 |   6 -
 lib/modules/ConsultationModule.class.php      |   8 +-
 lib/modules/CoursewareModule.class.php        |   1 +
 lib/navigation/FooterNavigation.php           |   6 +-
 lib/wiki.inc.php                              |  20 +-
 .../images/icons/black/focusmode-off.svg      |   1 +
 .../images/icons/blue/focusmode-off.svg       |   1 +
 .../images/icons/blue/fullscreen-off.svg      |   2 +-
 .../images/icons/blue/fullscreen-off2.svg     |   1 +
 .../images/icons/blue/fullscreen-off3.svg     |   1 +
 .../images/icons/blue/fullscreen-on.svg       |   2 +-
 .../images/icons/blue/fullscreen-on2.svg      |   1 +
 .../images/icons/blue/fullscreen-on3.svg      |   1 +
 .../images/icons/blue/fullscreen-on4.svg      |   1 +
 .../images/icons/blue/fullscreen-on5.svg      |   1 +
 public/assets/images/icons/blue/maximise.svg  |   1 +
 .../assets/images/icons/blue/sidebar-open.svg |   1 +
 public/assets/images/icons/blue/sidebar.svg   |   1 +
 public/assets/images/icons/blue/sidebar3.svg  |   1 +
 public/assets/images/icons/blue/zoom-in.svg   |   2 +-
 public/assets/images/icons/blue/zoom-in2.svg  |   2 +-
 public/assets/images/icons/blue/zoom-out.svg  |   2 +-
 public/assets/images/icons/blue/zoom-out2.svg |   2 +-
 .../images/icons/green/focusmode-off.svg      |   1 +
 .../images/icons/grey/focusmode-off.svg       |   1 +
 .../assets/images/icons/red/focusmode-off.svg |   1 +
 .../images/icons/white/focusmode-off.svg      |   1 +
 .../images/icons/white/fullscreen-off.svg     |   2 +-
 .../images/icons/white/fullscreen-off2.svg    |   1 +
 .../images/icons/white/fullscreen-off3.svg    |   1 +
 .../images/icons/white/fullscreen-on.svg      |   2 +-
 .../images/icons/white/fullscreen-on2.svg     |   1 +
 .../images/icons/white/fullscreen-on3.svg     |   1 +
 .../images/icons/white/fullscreen-on4.svg     |   1 +
 .../images/icons/yellow/focusmode-off.svg     |   1 +
 .../bootstrap/responsive-navigation.js        |  10 +
 .../javascripts/bootstrap/responsive.js       |   5 +
 resources/assets/javascripts/entry-base.js    |   1 +
 .../assets/javascripts/lib/header_magic.js    |  21 -
 .../assets/javascripts/lib/responsive.js      | 142 +---
 .../assets/stylesheets/highcontrast.scss      |   8 +-
 .../assets/stylesheets/less/breakpoints.less  |   4 +-
 .../assets/stylesheets/less/responsive.less   | 493 -----------
 .../assets/stylesheets/less/visibility.less   |  44 +-
 .../assets/stylesheets/scss/breakpoints.scss  |   7 +-
 .../assets/stylesheets/scss/buttons.scss      |   5 +
 .../assets/stylesheets/scss/contentbar.scss   | 188 +++--
 .../assets/stylesheets/scss/courseware.scss   |   2 +-
 resources/assets/stylesheets/scss/header.scss |  29 +-
 .../assets/stylesheets/scss/helpbar.scss      |   3 +-
 .../assets/stylesheets/scss/layouts.scss      |   4 +-
 .../assets/stylesheets/scss/navigation.scss   |  41 +-
 .../assets/stylesheets/scss/responsive.scss   | 796 ++++++++++++++++++
 .../stylesheets/scss/table_of_contents.scss   |  25 +-
 .../assets/stylesheets/scss/visibility.scss   |  49 +-
 resources/assets/stylesheets/studip.less      |   3 -
 resources/assets/stylesheets/studip.scss      |   1 +
 resources/vue/components/BlubberThread.vue    |   2 +-
 .../courseware/CoursewareRibbon.vue           |  10 +-
 .../CoursewareStructuralElement.vue           |   2 +-
 .../components/responsive/NavigationItem.vue  |  83 ++
 .../responsive/ResponsiveContentBar.vue       | 206 +++++
 .../responsive/ResponsiveNavigation.vue       | 525 ++++++++++++
 .../responsive/ResponsiveSkipLinks.vue        |  49 ++
 .../responsive/ToggleFullscreen.vue           |  51 ++
 resources/vue/mixins/MyCoursesMixin.js        |   4 +-
 templates/contentbar/contentbar.php           |  57 +-
 templates/footer.php                          |   3 +-
 templates/header.php                          |  36 +-
 templates/layouts/base.php                    |  16 +-
 templates/skiplinks.php                       |  15 +-
 templates/tabs.php                            |  10 +-
 80 files changed, 2356 insertions(+), 943 deletions(-)
 create mode 100644 public/assets/images/icons/black/focusmode-off.svg
 create mode 100644 public/assets/images/icons/blue/focusmode-off.svg
 create mode 100644 public/assets/images/icons/blue/fullscreen-off2.svg
 create mode 100644 public/assets/images/icons/blue/fullscreen-off3.svg
 create mode 100644 public/assets/images/icons/blue/fullscreen-on2.svg
 create mode 100644 public/assets/images/icons/blue/fullscreen-on3.svg
 create mode 100644 public/assets/images/icons/blue/fullscreen-on4.svg
 create mode 100644 public/assets/images/icons/blue/fullscreen-on5.svg
 create mode 100644 public/assets/images/icons/blue/maximise.svg
 create mode 100644 public/assets/images/icons/blue/sidebar-open.svg
 create mode 100644 public/assets/images/icons/blue/sidebar.svg
 create mode 100644 public/assets/images/icons/blue/sidebar3.svg
 create mode 100644 public/assets/images/icons/green/focusmode-off.svg
 create mode 100644 public/assets/images/icons/grey/focusmode-off.svg
 create mode 100644 public/assets/images/icons/red/focusmode-off.svg
 create mode 100644 public/assets/images/icons/white/focusmode-off.svg
 create mode 100644 public/assets/images/icons/white/fullscreen-off2.svg
 create mode 100644 public/assets/images/icons/white/fullscreen-off3.svg
 create mode 100644 public/assets/images/icons/white/fullscreen-on2.svg
 create mode 100644 public/assets/images/icons/white/fullscreen-on3.svg
 create mode 100644 public/assets/images/icons/white/fullscreen-on4.svg
 create mode 100644 public/assets/images/icons/yellow/focusmode-off.svg
 create mode 100644 resources/assets/javascripts/bootstrap/responsive-navigation.js
 delete mode 100644 resources/assets/stylesheets/less/responsive.less
 create mode 100644 resources/assets/stylesheets/scss/responsive.scss
 create mode 100644 resources/vue/components/responsive/NavigationItem.vue
 create mode 100644 resources/vue/components/responsive/ResponsiveContentBar.vue
 create mode 100644 resources/vue/components/responsive/ResponsiveNavigation.vue
 create mode 100644 resources/vue/components/responsive/ResponsiveSkipLinks.vue
 create mode 100644 resources/vue/components/responsive/ToggleFullscreen.vue

diff --git a/app/controllers/oer/market.php b/app/controllers/oer/market.php
index 23782a44e2a..4071d07b5ff 100644
--- a/app/controllers/oer/market.php
+++ b/app/controllers/oer/market.php
@@ -271,11 +271,10 @@ class Oer_MarketController extends StudipController
             }
         }
 
-        $this->contentbar = new ContentBar(
-            new TOCItem($this->material['name']),
-            $infotext,
-            Icon::create('oer-campus')
-        );
+        $this->contentbar = ContentBar::get()
+            ->setTOC(new TOCItem($this->material['name']))
+            ->setInfo($infotext)
+            ->setIcon(Icon::create('oer-campus'));
     }
 
     public function embed_action($material_id)
diff --git a/app/views/profile/extern.php b/app/views/profile/extern.php
index 9524837d8d1..60ec9612f3e 100644
--- a/app/views/profile/extern.php
+++ b/app/views/profile/extern.php
@@ -1,4 +1,4 @@
-<div class="responsive-visible">
+<div class="hidden-medium-up">
     <img src="<?= htmlReady($user['avatar_url'] ?: Avatar::getNobody()->getURL(Avatar::NORMAL)) ?>">
 </div>
 
diff --git a/app/views/profile/index.php b/app/views/profile/index.php
index 1062f74bcf5..72e9c9f603f 100644
--- a/app/views/profile/index.php
+++ b/app/views/profile/index.php
@@ -1,4 +1,4 @@
-<div class="responsive-visible">
+<div class="hidden-medium-up">
     <?= Avatar::getAvatar($current_user->user_id)->getImageTag(Avatar::NORMAL) ?>
 </div>
 <section class="contentbox">
diff --git a/lib/classes/ContentBar.php b/lib/classes/ContentBar.php
index 043fe1830bb..53b3dfce981 100644
--- a/lib/classes/ContentBar.php
+++ b/lib/classes/ContentBar.php
@@ -15,25 +15,41 @@ class ContentBar
     public $infoText = '';
     public $icon = '';
     public $toc = null;
+    public $actionMenu = null;
+
     /**
-     * ContentBar constructor.
-     *
-     * Note: An icon for consumer mode is always shown, this would have to be changed via template.
+     * The singleton instance of the container
+     */
+    protected static $instance = null;
+
+    /**
+     * Returns the instance of this container to ensure there is only one
+     * instance.
      *
      * @param TOCItem $toc Table of contents object.
      * @param string $info Some information to show, like creation date, author etc.
      * @param Icon|null $icon An icon to show in content bar.
      * @param ActionMenu|null $actionMenu Optional action menu for page actions.
+     * @return ContentBar
+     * @static
      */
-    public function __construct(TOCItem $toc, string $info = '', Icon $icon = null, ActionMenu $actionMenu = null)
+    public static function get(): ContentBar
     {
-        $this->infoText = $info;
-        $this->icon = $icon;
-        $this->toc = $toc;
-        $this->actionMenu = $actionMenu;
+        if (static::$instance === null) {
+            static::$instance = new static;
+        }
+        return static::$instance;
     }
 
-    public $actionMenu = null;
+    /**
+     * Private constructor to ensure that the singleton Get() method is always
+     * used.
+     *
+     * @see ContentBar::get
+     */
+    protected function __construct()
+    {
+    }
 
     /**
      * Provide some info text.
diff --git a/lib/classes/ResponsiveHelper.php b/lib/classes/ResponsiveHelper.php
index eb7f41c636c..c2b201ef116 100644
--- a/lib/classes/ResponsiveHelper.php
+++ b/lib/classes/ResponsiveHelper.php
@@ -24,27 +24,54 @@ class ResponsiveHelper
         $link_params = array_fill_keys(array_keys(URLHelper::getLinkParams()), null);
 
         foreach (Navigation::getItem('/')->getSubNavigation() as $path => $nav) {
-            if (!$nav->isVisible(true)) {
-                continue;
+            $image = $nav->getImage();
+
+            $forceVisibility = false;
+            /*
+             * Special treatment for "browse" navigation which is normally hidden
+             * when we are inside a course.
+             */
+            if ($path === 'browse' && !$image) {
+                $image = Icon::create('seminar');
+                $forceVisibility = true;
+            }
+            /*
+             * Special treatment for "footer" navigation because
+             * the real footer is hidden in responsive view.
+             */
+            if ($path === 'footer' && !$image) {
+                $image = Icon::create('info');
+                $nav->setTitle(_('Impressum & Information'));
+                $forceVisibility = true;
             }
 
-            $image = $nav->getImage();
             $image_src = $image ? $image->copyWithRole('info_alt')->asImagePath() : false;
             $item = [
-                'icon'   => $image_src ? self::getAssetsURL($image_src) : false,
-                'title'  => (string) $nav->getTitle(),
-                'url'    => self::getURL($nav->getURL(), $link_params),
+                'icon'     => $image_src ? self::getAssetsURL($image_src) : false,
+                'title'    => (string) $nav->getTitle(),
+                'url'      => URLHelper::getURL($nav->getURL(), $link_params, true),
+                'parent'   => '/',
+                'path'     => $path,
+                'visible'  => $forceVisibility ? true : $nav->isVisible(true),
+                'active'   => $nav->isActive()
             ];
 
             if ($nav->isActive()) {
-                $activated[] = $path;
+                // course navigation is integrated in course sub-navigation items
+                if ($path === 'course') {
+                    $activated[] = 'browse/my_courses/' . (Context::get()->getId());
+                } else {
+                    $activated[] = $path;
+                }
             }
 
-            if ($nav->getSubnavigation()  && $path != 'start') {
+            if ($nav->getSubnavigation() && $path != 'start') {
                 $item['children'] = self::getChildren($nav, $path, $activated);
             }
 
-            $navigation[$path] = $item;
+            if ($path !== 'course') {
+                $navigation[$path] = $item;
+            }
         }
 
         return [$navigation, $activated];
@@ -54,35 +81,50 @@ class ResponsiveHelper
      * Recursively build a navigation array from the subnavigation/children
      * of a navigation object.
      *
-     * @param Navigation $navigation The navigation object
-     * @param String     $path       Current path segment
-     * @param array      $activated  Activated items
+     * @param Navigation  $navigation The navigation object
+     * @param String      $path       Current path segment
+     * @param array       $activated  Activated items
+     * @param String|null $cid       Optional context ID
      * @return Array containing the children (+ grandchildren...)
      */
-    protected static function getChildren(Navigation $navigation, $path, &$activated = [])
+    protected static function getChildren(Navigation $navigation, $path, &$activated = [], string $cid = null)
     {
         $children = [];
 
         foreach ($navigation->getSubNavigation() as $subpath => $subnav) {
-            if (!$subnav->isVisible()) {
+            /*if (!$subnav->isVisible()) {
                 continue;
-            }
+            }*/
 
+            $originalSubpath = $subpath;
             $subpath = "{$path}/{$subpath}";
 
             $item = [
-                'title' => (string) $subnav->getTitle(),
-                'url'   => self::getURL($subnav->getURL()),
+                'title'   => (string) $subnav->getTitle(),
+                'url'     => URLHelper::getURL($subnav->getURL(), $cid ? ['cid' => $cid] : []),
+                'parent'  => $path,
+                'path'    => $subpath,
+                'visible' => $subnav->isVisible(),
+                'active'  => $subnav->isActive()
             ];
 
             if ($subnav->isActive()) {
-                $activated[] = $subpath;
+                // course navigation is integrated in course sub-navigation items
+                if ($path === 'course') {
+                    $activated[] = 'browse/my_courses/' . Context::get()->getId() . '/' . $originalSubpath;
+                } else {
+                    $activated[] = $subpath;
+                }
             }
 
             if ($subnav->getSubNavigation()) {
                 $item['children'] = self::getChildren($subnav, $subpath);
             }
 
+            if ($subpath === 'browse/my_courses') {
+                $item['children'] = array_merge($item['children'] ?? [], static::getMyCoursesNavigation($activated));
+            }
+
             $children[$subpath] = $item;
         }
 
@@ -113,4 +155,99 @@ class ResponsiveHelper
     {
         return str_replace($GLOBALS['ASSETS_URL'], '', $url);
     }
+
+    /**
+     * Specialty for responsive navigation: build navigation items
+     * for my courses in current semester.
+     *
+     * @return array
+     */
+    protected static function getMyCoursesNavigation($activated): array
+    {
+        if (!$GLOBALS['perm']->have_perm('admin')) {
+            $sem_data = Semester::getAllAsArray();
+
+            $currentIndex = -1;
+
+            foreach ($sem_data as $index => $semester) {
+                if ($semester['current']) {
+                    $currentIndex = $index;
+                    break;
+                }
+            }
+
+            $params = [
+                'deputies_enabled' => Config::get()->DEPUTIES_ENABLE
+            ];
+
+            $courses = MyRealmModel::getCourses($currentIndex, $currentIndex, $params);
+        } else {
+            $courses = [];
+        }
+
+        $items = [];
+
+        $standardIcon = Icon::create('seminar', Icon::ROLE_INFO_ALT)->asImagePath();
+
+        // Add current course to list.
+        if (Context::get() && Context::isCourse()) {
+            $courses[] = Context::get();
+        }
+
+        foreach ($courses as $course) {
+            $avatar = CourseAvatar::getAvatar($course->id);
+            if ($avatar->is_customized()) {
+                $icon = $avatar->getURL(Avatar::SMALL);
+            } else {
+                $icon = $standardIcon;
+            }
+
+            $cnav = [
+                'icon'     => $icon,
+                'title'    => $course->getFullname(),
+                'url'      => URLHelper::getURL('dispatch.php/course/details', ['cid' => $course->id]),
+                'parent'   => 'browse/my_courses',
+                'path'     => 'browse/my_courses/' . $course->id,
+                'visible'  => true,
+                'active'   => Course::findCurrent() ? Course::findCurrent()->id === $course->id : false,
+                'children' => []
+            ];
+
+            foreach ($course->tools as $tool) {
+                if (Seminar_Perm::get()->have_studip_perm($tool->getVisibilityPermission(), $course->id)) {
+
+                    $path = 'browse/my_courses/' . $course->id;
+
+                    $studip_module = $tool->getStudipModule();
+                    if ($studip_module instanceof StudipModule) {
+                        $tool_nav = $studip_module->getTabNavigation($course->id) ?: [];
+                        foreach ($tool_nav as $nav_name => $navigation) {
+                            if ($nav_name && is_a($navigation, 'Navigation')) {
+                                $cnav['children'][$path . '/' . $nav_name] = [
+                                    'icon'     => $navigation->getImage() ? $navigation->getImage()->asImagePath() : '',
+                                    'title'    => $tool->getDisplayname(),
+                                    'url'      => URLHelper::getURL($navigation->getURL(), ['cid' => $course->id]),
+                                    'parent'   => 'browse/my_courses/' . $course->id,
+                                    'path'     => 'browse/my_courses/' . $course->id . '/' . $nav_name,
+                                    'visible'  => true,
+                                    'active'   => $navigation->isActive(),
+                                    'children' => static::getChildren(
+                                        $navigation,
+                                        'browse/my_courses/' . $course->id . '/' . $nav_name,
+                                        $activated,
+                                        $course->id
+                                    ),
+                                ];
+                            }
+                        }
+                    }
+                }
+            }
+
+            $items['browse/my_courses/' . $course->id] = $cnav;
+
+        }
+
+        return $items;
+    }
 }
diff --git a/lib/classes/SkipLinks.php b/lib/classes/SkipLinks.php
index 684ae424189..796206d0f11 100644
--- a/lib/classes/SkipLinks.php
+++ b/lib/classes/SkipLinks.php
@@ -60,27 +60,29 @@ class SkipLinks
      * @param string $url the url of the links
      * @param integer $position the position of the link in the list
      */
-    public static function addLink(string $name, string $url, $position = null)
+    public static function addLink(string $name, string $url, $position = null, bool $inFullscreen = true)
     {
         $position = (!$position || $position < 1) ? count(self::$links) + 100 : (int) $position;
         self::$links[$url] = [
-            'name'     => $name,
-            'url'      => $url,
-            'position' => $position,
+            'name'       => $name,
+            'url'        => $url,
+            'position'   => $position,
+            'fullscreen' => $inFullscreen
         ];
     }
 
     /**
      * Adds a link to an anker on the same page to the list of skip links.
      *
-     * @param string  $name     the displayed name of the links
-     * @param string  $id       the id of the anker
-     * @param integer $position the position of the link in the list
+     * @param string  $name       the displayed name of the links
+     * @param string  $id         the id of the anker
+     * @param integer $position   the position of the link in the list
+     * @param bool    $inFullscreen is this link relevant in fullscreen mode?
      */
-    public static function addIndex($name, $id, $position = null)
+    public static function addIndex($name, $id, $position = null, bool $inFullscreen = true)
     {
         $url = '#' . $id;
-        self::addLink($name, $url, $position);
+        self::addLink($name, $url, $position, $inFullscreen);
     }
 
     /**
@@ -99,12 +101,14 @@ class SkipLinks
         });
 
         $navigation = new Navigation('');
+        $fullscreen = [];
         foreach (array_values(self::$links) as $index => $link) {
             $navigation->addSubNavigation(
                 "/skiplinks/link-{$index}",
                 new Navigation($link['name'], $link['url'])
             );
+            $fullscreen['/skiplinks/link-' . $index] = $link['fullscreen'];
         }
-        return $GLOBALS['template_factory']->render('skiplinks', compact('navigation'));
+        return $GLOBALS['template_factory']->render('skiplinks', compact('navigation', 'fullscreen'));
     }
 }
diff --git a/lib/classes/WidgetContainer.php b/lib/classes/WidgetContainer.php
index 108477a3844..c702d06b1c2 100644
--- a/lib/classes/WidgetContainer.php
+++ b/lib/classes/WidgetContainer.php
@@ -159,6 +159,24 @@ abstract class WidgetContainer
         unset($this->widgets[$index]);
     }
 
+    /**
+     * Returns the number of widgets in this container. Optionally constrained
+     * to widgets of a specific class.
+     *
+     * @param string|null $widget_class
+     * @return int
+     */
+    public function countWidgets(string $widget_class = null): int
+    {
+        $widgets = $this->widgets;
+        if ($widget_class !== null) {
+            $widgets = array_filter($widgets, function (Widget $widget) use ($widget_class) {
+                return is_a($widget, $widget_class);
+            });
+        }
+        return count($widgets);
+    }
+
     /**
      * Returns whether this container has any widget.
      *
@@ -167,7 +185,7 @@ abstract class WidgetContainer
      */
     public function hasWidgets()
     {
-        return count($this->widgets) > 0;
+        return $this->countWidgets() > 0;
     }
 
     /**
diff --git a/lib/classes/sidebar/Sidebar.php b/lib/classes/sidebar/Sidebar.php
index 7536a7f9df0..00c3b8fae62 100644
--- a/lib/classes/sidebar/Sidebar.php
+++ b/lib/classes/sidebar/Sidebar.php
@@ -224,7 +224,8 @@ class Sidebar extends WidgetContainer
             SkipLinks::addIndex(
                 _('Dritte Navigationsebene'),
                 $widget->getId(),
-                20
+                20,
+                false
             );
 
             $navigation_widget_added = true;
@@ -237,7 +238,8 @@ class Sidebar extends WidgetContainer
             SkipLinks::addIndex(
                 _('Aktionen'),
                 $widget->getId(),
-                21
+                21,
+                false
             );
 
             $actions_widget_added = true;
diff --git a/lib/include/html_head.inc.php b/lib/include/html_head.inc.php
index 4ab1c0ecd1e..a7f2c21e1c4 100644
--- a/lib/include/html_head.inc.php
+++ b/lib/include/html_head.inc.php
@@ -32,12 +32,6 @@ $lang_attr = str_replace('_', '-', $_SESSION['_language']);
         String.locale = "<?= htmlReady(strtr($_SESSION['_language'], '_', '-')) ?>";
 
         document.querySelector('html').className = 'js';
-        setTimeout(() => {
-            // This needs to be put in a timeout since otherwise it will not match
-            if (window.matchMedia('(max-width: 767px)').matches) {
-                document.querySelector('html').classList.add('responsive-display');
-            }
-        }, 0);
 
         window.STUDIP = {
             ABSOLUTE_URI_STUDIP: "<?= $GLOBALS['ABSOLUTE_URI_STUDIP'] ?>",
diff --git a/lib/modules/ConsultationModule.class.php b/lib/modules/ConsultationModule.class.php
index acc2cb4cd6d..56717766df2 100644
--- a/lib/modules/ConsultationModule.class.php
+++ b/lib/modules/ConsultationModule.class.php
@@ -103,11 +103,13 @@ class ConsultationModule extends CorePlugin implements StudipModule, SystemPlugi
      */
     public function getTabNavigation($course_id)
     {
-        if ($GLOBALS['user']->id === 'nobody') {
-            return [];
+        if ($GLOBALS['user']->id !== 'nobody') {
+            $navigation = new ConsultationNavigation(RangeFactory::find($course_id));
+            $navigation->setImage(Icon::create('consultation', Icon::ROLE_INFO_ALT));
+            return ['consultation' => $navigation];
         }
 
-        return ['consultation' => new ConsultationNavigation(RangeFactory::find($course_id))];
+        return [];
     }
 
     /**
diff --git a/lib/modules/CoursewareModule.class.php b/lib/modules/CoursewareModule.class.php
index 0d8dfe89dcb..e07d5d20aa8 100644
--- a/lib/modules/CoursewareModule.class.php
+++ b/lib/modules/CoursewareModule.class.php
@@ -39,6 +39,7 @@ class CoursewareModule extends CorePlugin implements SystemPlugin, StudipModule,
             _('Courseware'),
             URLHelper::getURL('dispatch.php/course/courseware/?cid='.$courseId)
         );
+        $navigation->setImage(Icon::create('courseware', Icon::ROLE_INFO_ALT));
         $navigation->addSubNavigation(
             'content',
             new Navigation(_('Inhalt'), 'dispatch.php/course/courseware/?cid='.$courseId)
diff --git a/lib/navigation/FooterNavigation.php b/lib/navigation/FooterNavigation.php
index d3f8b83ac36..a24dc119b54 100644
--- a/lib/navigation/FooterNavigation.php
+++ b/lib/navigation/FooterNavigation.php
@@ -27,6 +27,9 @@ class FooterNavigation extends Navigation
     {
         parent::initSubNavigation();
 
+        // imprint
+        $this->addSubNavigation('siteinfo', new Navigation(_('Impressum'), 'dispatch.php/siteinfo/show?cancel_login=1'));
+
         // sitemap
         if (is_object($GLOBALS['user']) && $GLOBALS['user']->id !== 'nobody') {
             $this->addSubNavigation('sitemap', new Navigation(_('Sitemap'), 'dispatch.php/sitemap/'));
@@ -35,9 +38,6 @@ class FooterNavigation extends Navigation
         //studip
         $this->addSubNavigation('studip', new Navigation(_('Stud.IP'), 'http://www.studip.de/'));
 
-        // imprint
-        $this->addSubNavigation('siteinfo', new Navigation(_('Impressum'), 'dispatch.php/siteinfo/show?cancel_login=1'));
-
         // Datenschutzerklärung
 
         //Check if the privacy url is one of the Stud.IP pages:
diff --git a/lib/wiki.inc.php b/lib/wiki.inc.php
index 29e50e448be..6d37e541686 100644
--- a/lib/wiki.inc.php
+++ b/lib/wiki.inc.php
@@ -1031,8 +1031,14 @@ function wikiEdit($keyword, $wikiData, $user_id, $backpage=NULL, $ancestor=NULL)
     }
 
     // Create content bar.
-    $contentBar = new ContentBar($toc,
-        $page_string, Icon::create('wiki'), $actionMenu);
+    $contentBar = ContentBar::get()
+        ->setTOC(CoreWiki::getTOC(WikiPage::getStartPage(Context::getId())))
+        ->setInfo($page_string)
+        ->setIcon(Icon::create('wiki'));
+
+    if ($actionMenu) {
+        $contentBar->setActionMenu($actionMenu);
+    }
 
     $template = $GLOBALS['template_factory']->open('wiki/edit.php');
     $template->keyword  = $keyword;
@@ -1590,8 +1596,14 @@ function showWikiPage($keyword, $version, $special="", $show_comments="icon", $h
     }
 
     // Create content bar.
-    $contentBar = new ContentBar($toc,
-        $page_string, Icon::create('wiki'), $actionMenu);
+    $contentBar = ContentBar::get()
+        ->setTOC(CoreWiki::getTOC(WikiPage::getStartPage(Context::getId())))
+        ->setInfo($page_string)
+        ->setIcon(Icon::create('wiki'));
+
+    if ($actionMenu) {
+        $contentBar->setActionMenu($actionMenu);
+    }
 
     if ($hilight) {
         // Highlighting must only take place outside HTML tags, so
diff --git a/public/assets/images/icons/black/focusmode-off.svg b/public/assets/images/icons/black/focusmode-off.svg
new file mode 100644
index 00000000000..d7fe2e4a128
--- /dev/null
+++ b/public/assets/images/icons/black/focusmode-off.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#000000"><g id="fullscreen-off"><polygon points="51 20.13 33.87 20.13 33.87 2.99 37.29 2.99 37.29 16.7 51 16.7 51 20.13"/><polygon points="33.86 50.87 33.86 33.73 51 33.73 51 37.16 37.29 37.16 37.29 50.87 33.86 50.87"/><polygon points="20.16 50.97 16.73 50.97 16.73 37.26 3.02 37.26 3.02 33.84 20.16 33.84 20.16 50.97"/><polygon points="3.02 20.02 3.02 16.6 16.73 16.6 16.73 2.89 20.16 2.89 20.16 20.02 3.02 20.02"/></g></svg>
diff --git a/public/assets/images/icons/blue/focusmode-off.svg b/public/assets/images/icons/blue/focusmode-off.svg
new file mode 100644
index 00000000000..b7e965abb2c
--- /dev/null
+++ b/public/assets/images/icons/blue/focusmode-off.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><polygon class="e" points="56 20 36 20 36 0 40 0 40 16 56 16 56 20"/><polygon class="e" points="20 56 16 56 16 40 0 40 0 36 20 36 20 56"/><polygon class="e" points="36 56 36 36 56 36 56 40 40 40 40 56 36 56"/><polygon class="e" points="0 20 0 16 16 16 16 0 20 0 20 20 0 20"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-off.svg b/public/assets/images/icons/blue/fullscreen-off.svg
index 9f60e1a7397..b7e965abb2c 100644
--- a/public/assets/images/icons/blue/fullscreen-off.svg
+++ b/public/assets/images/icons/blue/fullscreen-off.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#28497c"><g id="fullscreen-off"><polygon points="51 20.13 33.87 20.13 33.87 2.99 37.29 2.99 37.29 16.7 51 16.7 51 20.13"/><polygon points="33.86 50.87 33.86 33.73 51 33.73 51 37.16 37.29 37.16 37.29 50.87 33.86 50.87"/><polygon points="20.16 50.97 16.73 50.97 16.73 37.26 3.02 37.26 3.02 33.84 20.16 33.84 20.16 50.97"/><polygon points="3.02 20.02 3.02 16.6 16.73 16.6 16.73 2.89 20.16 2.89 20.16 20.02 3.02 20.02"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><polygon class="e" points="56 20 36 20 36 0 40 0 40 16 56 16 56 20"/><polygon class="e" points="20 56 16 56 16 40 0 40 0 36 20 36 20 56"/><polygon class="e" points="36 56 36 36 56 36 56 40 40 40 40 56 36 56"/><polygon class="e" points="0 20 0 16 16 16 16 0 20 0 20 20 0 20"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-off2.svg b/public/assets/images/icons/blue/fullscreen-off2.svg
new file mode 100644
index 00000000000..c10c1d1e5fa
--- /dev/null
+++ b/public/assets/images/icons/blue/fullscreen-off2.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52.83 52.83"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><polygon class="e" points="50.45 22.45 30.45 22.45 30.45 2.45 34.45 2.45 34.45 18.45 50.45 18.45 50.45 22.45"/><polygon class="e" points="22.45 50.45 18.45 50.45 18.45 34.45 2.45 34.45 2.45 30.45 22.45 30.45 22.45 50.45"/><polygon class="e" points="30.45 50.45 30.45 30.45 50.45 30.45 50.45 34.45 34.45 34.45 34.45 50.45 30.45 50.45"/><polygon class="e" points="2.45 22.45 2.45 18.45 18.45 18.45 18.45 2.45 22.45 2.45 22.45 22.45 2.45 22.45"/><rect class="e" x="8.95" y="-2.49" width="4" height="26.87" transform="translate(-4.54 10.95) rotate(-45)"/><rect class="e" x="39.66" y="28.53" width="4" height="26.26" transform="translate(-17.26 41.66) rotate(-45)"/><rect class="e" x="39.9" y="-2.53" width="4" height="26.92" transform="translate(20 -26.42) rotate(45)"/><rect class="e" x="8.93" y="28.44" width="4" height="26.92" transform="translate(32.83 4.54) rotate(45)"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-off3.svg b/public/assets/images/icons/blue/fullscreen-off3.svg
new file mode 100644
index 00000000000..6830b2e06e4
--- /dev/null
+++ b/public/assets/images/icons/blue/fullscreen-off3.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><polygon class="e" points="44.01 20.01 36.01 20.01 36.01 12.01 32.06 8.06 32.01 8.02 32.01 24.01 48 24.01 47.96 23.96 44.01 20.01"/><rect class="e" x="13" y="5.1" width="4" height="19.8" transform="translate(-6.21 15) rotate(-45)"/><rect class="e" x="39.5" y="30.89" width="4" height="21.21" transform="translate(-17.19 41.5) rotate(-45)"/><rect class="e" x="38.96" y="5.06" width="4" height="19.8" transform="translate(22.58 -24.58) rotate(45)"/><rect class="e" x="12.71" y="30.96" width="4" height="20.51" transform="translate(33.45 1.67) rotate(45)"/><path class="e" d="M52,4V52H4V4H52M56,0H0V56H56V0h0Z"/><polygon class="e" points="11.99 36.02 20 36.02 20 44.03 23.95 47.98 24 48.01 24 32.02 8.01 32.02 8.05 32.07 11.99 36.02"/><polygon class="e" points="36.01 44.03 36.01 36.02 44.01 36.02 47.96 32.07 48 32.02 32.01 32.02 32.01 48.01 32.06 47.98 36.01 44.03"/><polygon class="e" points="20 12.01 20 20.01 11.99 20.01 8.05 23.96 8.01 24.01 24 24.01 24 8.02 23.95 8.06 20 12.01"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-on.svg b/public/assets/images/icons/blue/fullscreen-on.svg
index 0ecc3e6a275..08c249cf959 100644
--- a/public/assets/images/icons/blue/fullscreen-on.svg
+++ b/public/assets/images/icons/blue/fullscreen-on.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#28497c"><g id="zoom-in-3"><polygon points="51 20.12 47.57 20.12 47.57 6.42 33.86 6.42 33.86 2.99 51 2.99 51 20.12"/><polygon points="20.15 50.97 3.02 50.97 3.02 33.83 6.44 33.83 6.44 47.54 20.15 47.54 20.15 50.97"/><polygon points="33.86 50.97 33.86 47.54 47.57 47.54 47.57 33.83 51 33.83 51 50.97 33.86 50.97"/><polygon points="3.02 20.12 3.02 2.99 20.15 2.99 20.15 6.42 6.44 6.42 6.44 20.12 3.02 20.12"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 19.99 55.98 19.99 55.98 0 35.99 0"/><polygon class="e" points="4 35.99 0 35.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 35.99"/></g><g><polygon class="e" points="55.98 35.99 51.98 35.99 51.98 51.98 35.99 51.98 35.99 55.98 55.98 55.98 55.98 35.99"/><polygon class="e" points="19.99 4 19.99 0 0 0 0 19.99 4 19.99 4 4 19.99 4"/></g></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-on2.svg b/public/assets/images/icons/blue/fullscreen-on2.svg
new file mode 100644
index 00000000000..16ba091dd1b
--- /dev/null
+++ b/public/assets/images/icons/blue/fullscreen-on2.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 19.99 55.98 19.99 55.98 0 35.99 0"/><polygon class="e" points="4 35.99 0 35.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 35.99"/></g><g><polygon class="e" points="55.98 35.99 51.98 35.99 51.98 51.98 35.99 51.98 35.99 55.98 55.98 55.98 55.98 35.99"/><polygon class="e" points="19.99 4 19.99 0 0 0 0 19.99 4 19.99 4 4 19.99 4"/></g><rect class="e" x="9.44" y="-2.04" width="4" height="26.97" transform="translate(-4.74 11.44) rotate(-45)"/><rect class="e" x="42.48" y="31.04" width="4" height="26.87" transform="translate(-18.42 44.48) rotate(-45)"/><rect class="e" x="42.46" y="-2.03" width="4" height="26.92" transform="translate(21.1 -28.09) rotate(45)"/><rect class="e" x="9.43" y="31" width="4" height="26.92" transform="translate(34.79 4.94) rotate(45)"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-on3.svg b/public/assets/images/icons/blue/fullscreen-on3.svg
new file mode 100644
index 00000000000..bdaad6d5942
--- /dev/null
+++ b/public/assets/images/icons/blue/fullscreen-on3.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 12.02 44 12.02 44 20.03 47.95 23.98 48 24.01 48 8.02 32.01 8.02 32.05 8.07 35.99 12.02"/><polygon class="e" points="20.01 44.01 12.01 44.01 12.01 36.01 8.06 32.06 8.01 32.02 8.01 48.01 24 48.01 23.96 47.96 20.01 44.01"/><polygon class="e" points="44 36.01 44 44.01 35.99 44.01 32.05 47.96 32.01 48.01 48 48.01 48 32.02 47.95 32.06 44 36.01"/><polygon class="e" points="12.01 20.03 12.01 12.02 20.01 12.02 23.96 8.07 24 8.02 8.01 8.02 8.01 24.01 8.06 23.98 12.01 20.03"/></g><rect class="e" x="16" y="8.1" width="4" height="19.8" transform="translate(-7.46 18) rotate(-45)"/><rect class="e" x="36.5" y="27.89" width="4" height="21.21" transform="translate(-15.95 38.5) rotate(-45)"/><rect class="e" x="35.96" y="8.06" width="4" height="19.8" transform="translate(23.82 -21.58) rotate(45)"/><rect class="e" x="15.71" y="27.96" width="4" height="20.51" transform="translate(32.21 -1.33) rotate(45)"/><path class="e" d="M52,4V52H4V4H52M56,0H0V56H56V0h0Z"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-on4.svg b/public/assets/images/icons/blue/fullscreen-on4.svg
new file mode 100644
index 00000000000..da6502ec76f
--- /dev/null
+++ b/public/assets/images/icons/blue/fullscreen-on4.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 19.99 55.98 19.99 55.98 0 35.99 0"/><polygon class="e" points="4 35.99 0 35.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 35.99"/></g><g><polygon class="e" points="55.98 35.99 51.98 35.99 51.98 51.98 35.99 51.98 35.99 55.98 55.98 55.98 55.98 35.99"/><polygon class="e" points="19.99 4 19.99 0 0 0 0 19.99 4 19.99 4 4 19.99 4"/></g><rect class="e" x="7.98" y="11.98" width="40" height="8"/><path class="e" d="M43.98,15.98v24H11.98V15.98H43.98m4-4H7.98V43.98H47.98V11.98h0Z"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/fullscreen-on5.svg b/public/assets/images/icons/blue/fullscreen-on5.svg
new file mode 100644
index 00000000000..8566dece4ed
--- /dev/null
+++ b/public/assets/images/icons/blue/fullscreen-on5.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 49.99 55.98 49.99 55.98 0 35.99 0"/><polygon class="e" points="4 5.99 0 5.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 5.99"/></g><g><polygon class="e" points="55.98 35.99 51.98 35.99 51.98 51.98 5.99 51.98 5.99 55.98 55.98 55.98 55.98 35.99"/><polygon class="e" points="49.99 4 49.99 0 0 0 0 19.99 4 19.99 4 4 49.99 4"/></g><rect class="e" x="7.98" y="19.98" width="32" height="8"/><path class="e" d="M35.98,23.98v20H11.98V23.98h24m4-4H7.98v28H39.98V19.98h0Z"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/maximise.svg b/public/assets/images/icons/blue/maximise.svg
new file mode 100644
index 00000000000..b096fc030bb
--- /dev/null
+++ b/public/assets/images/icons/blue/maximise.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><path class="e" d="M52,0H14V22H0V56H32v-14h24V0h-4ZM28,52H4V26H14v16h14v10Zm24-14H18V10H52v28Z"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/sidebar-open.svg b/public/assets/images/icons/blue/sidebar-open.svg
new file mode 100644
index 00000000000..3b7dff252a2
--- /dev/null
+++ b/public/assets/images/icons/blue/sidebar-open.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.f{fill:none;}.g{fill:#28497c;}</style></defs><g id="a"/><g id="b"><g id="c"><rect class="f" width="64" height="64"/></g><g id="d"><g id="e"><g><path class="g" d="M20,12v21H52V12H20Zm28,17H24V16h24v13Z"/><path class="g" d="M20,52H52v-15H20v15Zm4-11h24v7H24v-7Z"/><polygon class="g" points="16 8 36 8 36 4 12 4 12 60 36 60 36 56 16 56 16 8"/></g></g></g></g></svg>
diff --git a/public/assets/images/icons/blue/sidebar.svg b/public/assets/images/icons/blue/sidebar.svg
new file mode 100644
index 00000000000..676661efe8b
--- /dev/null
+++ b/public/assets/images/icons/blue/sidebar.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.f{fill:none;}.g{fill:#28497c;}</style></defs><g id="a"/><g id="b"><g id="c"><rect class="f" width="64" height="64"/></g><g id="d"><g id="e"><g><path class="g" d="M12,12v21h32V12H12Zm4,4h24v13H16V16Z"/><path class="g" d="M44,37H12v15h32v-15Zm-4,11H16v-7h24v7Z"/><polygon class="g" points="48 8 28 8 28 4 52 4 52 60 28 60 28 56 48 56 48 8"/></g></g></g></g></svg>
diff --git a/public/assets/images/icons/blue/sidebar3.svg b/public/assets/images/icons/blue/sidebar3.svg
new file mode 100644
index 00000000000..ab4b9b39d6a
--- /dev/null
+++ b/public/assets/images/icons/blue/sidebar3.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.f{fill:none;}.g{fill:#28497c;}</style></defs><g id="a"/><g id="b"><g id="c"><rect class="f" width="64" height="64"/></g><g id="d"><g id="e"><path class="g" d="M56,8V56H8V8H56m4-4H4V60H60V4h0Z"/><polygon class="g" points="26 6 22 6 22 58 26 58 26 6 26 6"/><rect class="g" x="7" y="6" width="19" height="52"/></g></g></g></svg>
diff --git a/public/assets/images/icons/blue/zoom-in.svg b/public/assets/images/icons/blue/zoom-in.svg
index 36f054e2c40..bf307ed2aec 100644
--- a/public/assets/images/icons/blue/zoom-in.svg
+++ b/public/assets/images/icons/blue/zoom-in.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#28497c"><g id="zoom-in"><polygon points="30.44 2.99 30.44 6.42 47.57 6.42 47.57 23.55 51 23.55 51 2.99 30.44 2.99"/><polygon points="6.44 30.41 3.02 30.41 3.02 50.97 23.58 50.97 23.58 47.54 6.44 47.54 6.44 30.41"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="31.99 0 31.99 4 51.98 4 51.98 23.99 55.98 23.99 55.98 0 31.99 0"/><polygon class="e" points="4 31.99 0 31.99 0 55.98 23.99 55.98 23.99 51.98 4 51.98 4 31.99"/></g></g></g></svg>
diff --git a/public/assets/images/icons/blue/zoom-in2.svg b/public/assets/images/icons/blue/zoom-in2.svg
index 3056bc405b2..65b590d240a 100644
--- a/public/assets/images/icons/blue/zoom-in2.svg
+++ b/public/assets/images/icons/blue/zoom-in2.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#28497c"><g id="zoom-in2"><path d="M39,15V39H15V15H39m3-3H12V42H42V12Z"/><polygon points="50.98 20.99 47.99 20.99 47.99 6 32.99 6 32.99 3 50.98 3 50.98 20.99"/><polygon points="21 50.98 3.01 50.98 3.01 32.99 6 32.99 6 47.98 21 47.98 21 50.98"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><g><path class="e" d="M10.98,44.98H44.98V10.98H10.98V44.98ZM14.98,14.98h26v26H14.98V14.98Z"/><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 19.99 55.98 19.99 55.98 0 35.99 0"/><polygon class="e" points="4 35.99 0 35.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 35.99"/></g></g></g></svg>
diff --git a/public/assets/images/icons/blue/zoom-out.svg b/public/assets/images/icons/blue/zoom-out.svg
index ef38c356fba..3a0a4133bc3 100644
--- a/public/assets/images/icons/blue/zoom-out.svg
+++ b/public/assets/images/icons/blue/zoom-out.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#28497c"><g id="zoom-out"><polygon points="51 23.55 30.44 23.55 30.44 2.99 33.86 2.99 33.86 20.12 51 20.12 51 23.55"/><polygon points="23.58 50.97 20.15 50.97 20.15 33.83 3.02 33.83 3.02 30.41 23.58 30.41 23.58 50.97"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><polygon class="e" points="55.98 23.99 31.99 23.99 31.99 0 35.99 0 35.99 19.99 55.98 19.99 55.98 23.99"/><polygon class="e" points="23.99 55.98 19.99 55.98 19.99 35.99 0 35.99 0 31.99 23.99 31.99 23.99 55.98"/></g></g></svg>
diff --git a/public/assets/images/icons/blue/zoom-out2.svg b/public/assets/images/icons/blue/zoom-out2.svg
index 67e11b3da43..d660c602b5c 100644
--- a/public/assets/images/icons/blue/zoom-out2.svg
+++ b/public/assets/images/icons/blue/zoom-out2.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#28497c"><g id="zoom-out2"><path d="M51,18H42V12H36V3H33v9H12V33H3v3h9v6h6v9h3V42H42V21h9ZM39,15v3H36V15ZM15,39V36h3v3Zm24,0H21V33H15V15H33v6h6Z"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#28497c;}</style></defs><g id="c"><g id="d"><path class="e" d="M55.98,15.99h-8V7.98h-7.99V0h-4V7.98H7.98v28.01H0v4H7.98v7.99H15.99v8h4v-8h27.99V19.99h8v-4Zm-12-4.01v4.01h-3.99v-4.01h3.99ZM11.98,43.98v-3.99h4.01v3.99h-4.01Zm32,0H19.99v-7.99H11.98V11.98h24.01v8.01h7.99v23.99Z"/></g></g></svg>
diff --git a/public/assets/images/icons/green/focusmode-off.svg b/public/assets/images/icons/green/focusmode-off.svg
new file mode 100644
index 00000000000..b5aa40664b3
--- /dev/null
+++ b/public/assets/images/icons/green/focusmode-off.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#00962d"><g id="fullscreen-off"><polygon points="51 20.13 33.87 20.13 33.87 2.99 37.29 2.99 37.29 16.7 51 16.7 51 20.13"/><polygon points="33.86 50.87 33.86 33.73 51 33.73 51 37.16 37.29 37.16 37.29 50.87 33.86 50.87"/><polygon points="20.16 50.97 16.73 50.97 16.73 37.26 3.02 37.26 3.02 33.84 20.16 33.84 20.16 50.97"/><polygon points="3.02 20.02 3.02 16.6 16.73 16.6 16.73 2.89 20.16 2.89 20.16 20.02 3.02 20.02"/></g></svg>
diff --git a/public/assets/images/icons/grey/focusmode-off.svg b/public/assets/images/icons/grey/focusmode-off.svg
new file mode 100644
index 00000000000..6509d7976b2
--- /dev/null
+++ b/public/assets/images/icons/grey/focusmode-off.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#6e6e6e"><g id="fullscreen-off"><polygon points="51 20.13 33.87 20.13 33.87 2.99 37.29 2.99 37.29 16.7 51 16.7 51 20.13"/><polygon points="33.86 50.87 33.86 33.73 51 33.73 51 37.16 37.29 37.16 37.29 50.87 33.86 50.87"/><polygon points="20.16 50.97 16.73 50.97 16.73 37.26 3.02 37.26 3.02 33.84 20.16 33.84 20.16 50.97"/><polygon points="3.02 20.02 3.02 16.6 16.73 16.6 16.73 2.89 20.16 2.89 20.16 20.02 3.02 20.02"/></g></svg>
diff --git a/public/assets/images/icons/red/focusmode-off.svg b/public/assets/images/icons/red/focusmode-off.svg
new file mode 100644
index 00000000000..9767595021a
--- /dev/null
+++ b/public/assets/images/icons/red/focusmode-off.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#cb1800"><g id="fullscreen-off"><polygon points="51 20.13 33.87 20.13 33.87 2.99 37.29 2.99 37.29 16.7 51 16.7 51 20.13"/><polygon points="33.86 50.87 33.86 33.73 51 33.73 51 37.16 37.29 37.16 37.29 50.87 33.86 50.87"/><polygon points="20.16 50.97 16.73 50.97 16.73 37.26 3.02 37.26 3.02 33.84 20.16 33.84 20.16 50.97"/><polygon points="3.02 20.02 3.02 16.6 16.73 16.6 16.73 2.89 20.16 2.89 20.16 20.02 3.02 20.02"/></g></svg>
diff --git a/public/assets/images/icons/white/focusmode-off.svg b/public/assets/images/icons/white/focusmode-off.svg
new file mode 100644
index 00000000000..f852c7fb908
--- /dev/null
+++ b/public/assets/images/icons/white/focusmode-off.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><polygon class="e" points="56 20 36 20 36 0 40 0 40 16 56 16 56 20"/><polygon class="e" points="20 56 16 56 16 40 0 40 0 36 20 36 20 56"/><polygon class="e" points="36 56 36 36 56 36 56 40 40 40 40 56 36 56"/><polygon class="e" points="0 20 0 16 16 16 16 0 20 0 20 20 0 20"/></g></g></svg>
diff --git a/public/assets/images/icons/white/fullscreen-off.svg b/public/assets/images/icons/white/fullscreen-off.svg
index 2709d176a1c..f852c7fb908 100644
--- a/public/assets/images/icons/white/fullscreen-off.svg
+++ b/public/assets/images/icons/white/fullscreen-off.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#ffffff"><g id="fullscreen-off"><polygon points="51 20.13 33.87 20.13 33.87 2.99 37.29 2.99 37.29 16.7 51 16.7 51 20.13"/><polygon points="33.86 50.87 33.86 33.73 51 33.73 51 37.16 37.29 37.16 37.29 50.87 33.86 50.87"/><polygon points="20.16 50.97 16.73 50.97 16.73 37.26 3.02 37.26 3.02 33.84 20.16 33.84 20.16 50.97"/><polygon points="3.02 20.02 3.02 16.6 16.73 16.6 16.73 2.89 20.16 2.89 20.16 20.02 3.02 20.02"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><polygon class="e" points="56 20 36 20 36 0 40 0 40 16 56 16 56 20"/><polygon class="e" points="20 56 16 56 16 40 0 40 0 36 20 36 20 56"/><polygon class="e" points="36 56 36 36 56 36 56 40 40 40 40 56 36 56"/><polygon class="e" points="0 20 0 16 16 16 16 0 20 0 20 20 0 20"/></g></g></svg>
diff --git a/public/assets/images/icons/white/fullscreen-off2.svg b/public/assets/images/icons/white/fullscreen-off2.svg
new file mode 100644
index 00000000000..5c7b0c61b72
--- /dev/null
+++ b/public/assets/images/icons/white/fullscreen-off2.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52.83 52.83"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><polygon class="e" points="50.45 22.45 30.45 22.45 30.45 2.45 34.45 2.45 34.45 18.45 50.45 18.45 50.45 22.45"/><polygon class="e" points="22.45 50.45 18.45 50.45 18.45 34.45 2.45 34.45 2.45 30.45 22.45 30.45 22.45 50.45"/><polygon class="e" points="30.45 50.45 30.45 30.45 50.45 30.45 50.45 34.45 34.45 34.45 34.45 50.45 30.45 50.45"/><polygon class="e" points="2.45 22.45 2.45 18.45 18.45 18.45 18.45 2.45 22.45 2.45 22.45 22.45 2.45 22.45"/><rect class="e" x="8.95" y="-2.49" width="4" height="26.87" transform="translate(-4.54 10.95) rotate(-45)"/><rect class="e" x="39.66" y="28.53" width="4" height="26.26" transform="translate(-17.26 41.66) rotate(-45)"/><rect class="e" x="39.9" y="-2.53" width="4" height="26.92" transform="translate(20 -26.42) rotate(45)"/><rect class="e" x="8.93" y="28.44" width="4" height="26.92" transform="translate(32.83 4.54) rotate(45)"/></g></g></svg>
diff --git a/public/assets/images/icons/white/fullscreen-off3.svg b/public/assets/images/icons/white/fullscreen-off3.svg
new file mode 100644
index 00000000000..d51bf9d6dde
--- /dev/null
+++ b/public/assets/images/icons/white/fullscreen-off3.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><polygon class="e" points="44.01 20.01 36.01 20.01 36.01 12.01 32.06 8.06 32.01 8.02 32.01 24.01 48 24.01 47.96 23.96 44.01 20.01"/><rect class="e" x="13" y="5.1" width="4" height="19.8" transform="translate(-6.21 15) rotate(-45)"/><rect class="e" x="39.5" y="30.89" width="4" height="21.21" transform="translate(-17.19 41.5) rotate(-45)"/><rect class="e" x="38.96" y="5.06" width="4" height="19.8" transform="translate(22.58 -24.58) rotate(45)"/><rect class="e" x="12.71" y="30.96" width="4" height="20.51" transform="translate(33.45 1.67) rotate(45)"/><path class="e" d="M52,4V52H4V4H52M56,0H0V56H56V0h0Z"/><polygon class="e" points="11.99 36.02 20 36.02 20 44.03 23.95 47.98 24 48.01 24 32.02 8.01 32.02 8.05 32.07 11.99 36.02"/><polygon class="e" points="36.01 44.03 36.01 36.02 44.01 36.02 47.96 32.07 48 32.02 32.01 32.02 32.01 48.01 32.06 47.98 36.01 44.03"/><polygon class="e" points="20 12.01 20 20.01 11.99 20.01 8.05 23.96 8.01 24.01 24 24.01 24 8.02 23.95 8.06 20 12.01"/></g></g></svg>
diff --git a/public/assets/images/icons/white/fullscreen-on.svg b/public/assets/images/icons/white/fullscreen-on.svg
index a59f1926d5e..6c1066524e5 100644
--- a/public/assets/images/icons/white/fullscreen-on.svg
+++ b/public/assets/images/icons/white/fullscreen-on.svg
@@ -1 +1 @@
-<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#ffffff"><g id="zoom-in-3"><polygon points="51 20.12 47.57 20.12 47.57 6.42 33.86 6.42 33.86 2.99 51 2.99 51 20.12"/><polygon points="20.15 50.97 3.02 50.97 3.02 33.83 6.44 33.83 6.44 47.54 20.15 47.54 20.15 50.97"/><polygon points="33.86 50.97 33.86 47.54 47.57 47.54 47.57 33.83 51 33.83 51 50.97 33.86 50.97"/><polygon points="3.02 20.12 3.02 2.99 20.15 2.99 20.15 6.42 6.44 6.42 6.44 20.12 3.02 20.12"/></g></svg>
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 19.99 55.98 19.99 55.98 0 35.99 0"/><polygon class="e" points="4 35.99 0 35.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 35.99"/></g><g><polygon class="e" points="55.98 35.99 51.98 35.99 51.98 51.98 35.99 51.98 35.99 55.98 55.98 55.98 55.98 35.99"/><polygon class="e" points="19.99 4 19.99 0 0 0 0 19.99 4 19.99 4 4 19.99 4"/></g></g></g></svg>
diff --git a/public/assets/images/icons/white/fullscreen-on2.svg b/public/assets/images/icons/white/fullscreen-on2.svg
new file mode 100644
index 00000000000..bc25ed8454d
--- /dev/null
+++ b/public/assets/images/icons/white/fullscreen-on2.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 19.99 55.98 19.99 55.98 0 35.99 0"/><polygon class="e" points="4 35.99 0 35.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 35.99"/></g><g><polygon class="e" points="55.98 35.99 51.98 35.99 51.98 51.98 35.99 51.98 35.99 55.98 55.98 55.98 55.98 35.99"/><polygon class="e" points="19.99 4 19.99 0 0 0 0 19.99 4 19.99 4 4 19.99 4"/></g><rect class="e" x="9.44" y="-2.04" width="4" height="26.97" transform="translate(-4.74 11.44) rotate(-45)"/><rect class="e" x="42.48" y="31.04" width="4" height="26.87" transform="translate(-18.42 44.48) rotate(-45)"/><rect class="e" x="42.46" y="-2.03" width="4" height="26.92" transform="translate(21.1 -28.09) rotate(45)"/><rect class="e" x="9.43" y="31" width="4" height="26.92" transform="translate(34.79 4.94) rotate(45)"/></g></g></svg>
diff --git a/public/assets/images/icons/white/fullscreen-on3.svg b/public/assets/images/icons/white/fullscreen-on3.svg
new file mode 100644
index 00000000000..0492439d477
--- /dev/null
+++ b/public/assets/images/icons/white/fullscreen-on3.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 12.02 44 12.02 44 20.03 47.95 23.98 48 24.01 48 8.02 32.01 8.02 32.05 8.07 35.99 12.02"/><polygon class="e" points="20.01 44.01 12.01 44.01 12.01 36.01 8.06 32.06 8.01 32.02 8.01 48.01 24 48.01 23.96 47.96 20.01 44.01"/><polygon class="e" points="44 36.01 44 44.01 35.99 44.01 32.05 47.96 32.01 48.01 48 48.01 48 32.02 47.95 32.06 44 36.01"/><polygon class="e" points="12.01 20.03 12.01 12.02 20.01 12.02 23.96 8.07 24 8.02 8.01 8.02 8.01 24.01 8.06 23.98 12.01 20.03"/></g><rect class="e" x="16" y="8.1" width="4" height="19.8" transform="translate(-7.46 18) rotate(-45)"/><rect class="e" x="36.5" y="27.89" width="4" height="21.21" transform="translate(-15.95 38.5) rotate(-45)"/><rect class="e" x="35.96" y="8.06" width="4" height="19.8" transform="translate(23.82 -21.58) rotate(45)"/><rect class="e" x="15.71" y="27.96" width="4" height="20.51" transform="translate(32.21 -1.33) rotate(45)"/><path class="e" d="M52,4V52H4V4H52M56,0H0V56H56V0h0Z"/></g></g></svg>
diff --git a/public/assets/images/icons/white/fullscreen-on4.svg b/public/assets/images/icons/white/fullscreen-on4.svg
new file mode 100644
index 00000000000..0e406efc1a9
--- /dev/null
+++ b/public/assets/images/icons/white/fullscreen-on4.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.98 55.98"><defs><style>.e{fill:#fff;}</style></defs><g id="c"><g id="d"><g><polygon class="e" points="35.99 0 35.99 4 51.98 4 51.98 19.99 55.98 19.99 55.98 0 35.99 0"/><polygon class="e" points="4 35.99 0 35.99 0 55.98 19.99 55.98 19.99 51.98 4 51.98 4 35.99"/></g><g><polygon class="e" points="55.98 35.99 51.98 35.99 51.98 51.98 35.99 51.98 35.99 55.98 55.98 55.98 55.98 35.99"/><polygon class="e" points="19.99 4 19.99 0 0 0 0 19.99 4 19.99 4 4 19.99 4"/></g><rect class="e" x="7.98" y="11.98" width="40" height="8"/><path class="e" d="M43.98,15.98v24H11.98V15.98H43.98m4-4H7.98V43.98H47.98V11.98h0Z"/></g></g></svg>
diff --git a/public/assets/images/icons/yellow/focusmode-off.svg b/public/assets/images/icons/yellow/focusmode-off.svg
new file mode 100644
index 00000000000..a98b0e00c12
--- /dev/null
+++ b/public/assets/images/icons/yellow/focusmode-off.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" id="icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" fill="#ffad00"><g id="fullscreen-off"><polygon points="51 20.13 33.87 20.13 33.87 2.99 37.29 2.99 37.29 16.7 51 16.7 51 20.13"/><polygon points="33.86 50.87 33.86 33.73 51 33.73 51 37.16 37.29 37.16 37.29 50.87 33.86 50.87"/><polygon points="20.16 50.97 16.73 50.97 16.73 37.26 3.02 37.26 3.02 33.84 20.16 33.84 20.16 50.97"/><polygon points="3.02 20.02 3.02 16.6 16.73 16.6 16.73 2.89 20.16 2.89 20.16 20.02 3.02 20.02"/></g></svg>
diff --git a/resources/assets/javascripts/bootstrap/responsive-navigation.js b/resources/assets/javascripts/bootstrap/responsive-navigation.js
new file mode 100644
index 00000000000..aa811078ea7
--- /dev/null
+++ b/resources/assets/javascripts/bootstrap/responsive-navigation.js
@@ -0,0 +1,10 @@
+import ResponsiveNavigation from '../../../vue/components/responsive/ResponsiveNavigation.vue';
+
+STUDIP.ready(() => {
+    STUDIP.Vue.load().then(({ createApp }) => {
+        createApp({
+            el: '#responsive-menu',
+            components: { ResponsiveNavigation }
+        });
+    });
+});
diff --git a/resources/assets/javascripts/bootstrap/responsive.js b/resources/assets/javascripts/bootstrap/responsive.js
index e7c1aed4f2f..8e216d08ba8 100644
--- a/resources/assets/javascripts/bootstrap/responsive.js
+++ b/resources/assets/javascripts/bootstrap/responsive.js
@@ -9,6 +9,11 @@ STUDIP.domReady(() => {
     }
 
     STUDIP.Responsive.engage();
+
+    if (STUDIP.Responsive.isFullscreen()) {
+        document.querySelector('html').classList.add('fullscreen-mode');
+    }
+
 }, true);
 
 // Trigger search in responsive display
diff --git a/resources/assets/javascripts/entry-base.js b/resources/assets/javascripts/entry-base.js
index 09779ddb164..b577ce49a2f 100644
--- a/resources/assets/javascripts/entry-base.js
+++ b/resources/assets/javascripts/entry-base.js
@@ -87,6 +87,7 @@ import "./bootstrap/cache-admin.js"
 import "./bootstrap/oer.js"
 import "./bootstrap/courseware.js"
 import "./bootstrap/beforeunload-event-listener.js"
+import "./bootstrap/responsive-navigation.js"
 
 import "./mvv_course_wizard.js"
 import "./mvv.js"
diff --git a/resources/assets/javascripts/lib/header_magic.js b/resources/assets/javascripts/lib/header_magic.js
index 410b7543753..f465e7e95b8 100644
--- a/resources/assets/javascripts/lib/header_magic.js
+++ b/resources/assets/javascripts/lib/header_magic.js
@@ -10,27 +10,6 @@ const scroll = function(scrolltop) {
     if (is_below_the_fold !== was_below_the_fold) {
         $('body').toggleClass('fixed', is_below_the_fold);
 
-        menu = $('#navigation-level-1-items').remove();
-        if (is_below_the_fold) {
-            menu.append(
-                $('.action-menu-list li', menu)
-                    .remove()
-                    .addClass('from-action-menu')
-            );
-            menu.appendTo('#responsive-menu');
-        } else {
-            $('.action-menu-list', menu).append(
-                $('.from-action-menu', menu)
-                    .remove()
-                    .removeClass('from-action-menu')
-            );
-            menu.prependTo('#navigation-level-1');
-
-            NavigationShrinker();
-
-            $('#navigation-level-1-items-toggle').prop('checked', false);
-        }
-
         was_below_the_fold = is_below_the_fold;
     }
 };
diff --git a/resources/assets/javascripts/lib/responsive.js b/resources/assets/javascripts/lib/responsive.js
index 16e63541f49..a493e8ddb7b 100644
--- a/resources/assets/javascripts/lib/responsive.js
+++ b/resources/assets/javascripts/lib/responsive.js
@@ -4,127 +4,6 @@ import Sidebar from './sidebar.js';
 const Responsive = {
     media_query: window.matchMedia('(max-width: 767px)'),
 
-    // Builds a dom element from a navigation object
-    buildMenu (navigation, id, activated) {
-        var list = $('<ul>');
-
-        if (id) {
-            list.attr('id', id);
-        }
-
-        // TODO: Templating?
-        _.forEach(navigation, (nav, node) => {
-            nav.url = STUDIP.URLHelper.getURL(nav.url, {}, true);
-            let li = $('<li class="navigation-item">');
-            let title = $('<div class="nav-title">').appendTo(li);
-            let link = $(`<a href="${nav.url}">`).text(nav.title).appendTo(title);
-
-            if (nav.icon) {
-                if (!nav.icon.match(/^https?:\/\//)) {
-                    nav.icon = STUDIP.ASSETS_URL + nav.icon;
-                }
-                $(link).prepend(`<img class="icon" src="${nav.icon}">`);
-            }
-
-            if (nav.children) {
-                let active = activated.indexOf(node) !== -1;
-                $(`<input type="checkbox" id="resp/${node}">`)
-                    .prop('checked', active)
-                    .appendTo(li);
-                li.append(
-                    `<label class="nav-label" for="resp/${node}"> </label>`,
-                    Responsive.buildMenu(nav.children, false, activated)
-                );
-            }
-
-            list.append(li);
-        });
-
-        return list;
-    },
-
-    // Adds the responsive menu to the dom
-    addMenu () {
-        let wrapper = $('<div id="responsive-container">').append(
-            '<label for="responsive-toggle">',
-            '<input type="checkbox" id="responsive-toggle">',
-            Responsive.buildMenu(
-                STUDIP.Navigation.navigation,
-                'responsive-navigation',
-                STUDIP.Navigation.activated
-            ),
-            '<label for="responsive-toggle">'
-        );
-
-        $('<li>', { html: wrapper }).prependTo('#header-links > ul');
-    },
-
-    // Responsifies the layout. Builds the responsive menu from existing
-    // STUDIP.Navigation object
-    responsify () {
-        Responsive.media_query.removeListener(Responsive.responsify);
-
-        $('html').addClass('responsified');
-
-        Responsive.addMenu();
-
-        if ($('#sidebar > section').length > 0) {
-            $('<li id="sidebar-menu">')
-                .on('click', () => Sidebar.open())
-                .appendTo('#header-links > ul');
-
-            $('<label id="sidebar-shadow-toggle">')
-                .on('click', () => Sidebar.close())
-                .prependTo('#sidebar');
-
-            $('#responsive-toggle').on('change', function() {
-                $('#sidebar').removeClass('visible-sidebar');
-                $('#responsive-navigation').toggleClass('visible', this.checked);
-            });
-        } else {
-            $('#responsive-toggle').on('change', function() {
-                $('#responsive-navigation').toggleClass('visible', this.checked);
-            });
-        }
-
-        $('#responsive-navigation :checkbox').on('change', function () {
-            let li = $(this).closest('li');
-            if ($(this).is(':checked')) {
-                li.siblings().find(':checkbox:checked').prop('checked', false);
-            }
-
-            // Force redraw of submenu (at least ios safari/chrome would
-            // not show it without a forced redraw)
-            $(this).siblings('ul').hide(0, function () {
-                $(this).show();
-            });
-        }).reverse().trigger('change');
-
-        var sidebar_avatar_menu = $('<div class="sidebar-widget sidebar-avatar-menu">');
-        var avatar_menu = $('#avatar-menu');
-        var title = $('.action-menu-title', avatar_menu).text();
-        var list = $('<ul class="widget-list widget-links">');
-        $('<div class="sidebar-widget-header">').text(title).appendTo(sidebar_avatar_menu);
-
-        $('.action-menu-item', avatar_menu).each(function() {
-            var src = $('img', this).attr('src');
-            var link = $('a', this).clone();
-
-            link.find('img').remove();
-
-            $('<li>').append(link).css({
-                backgroundSize: '16px',
-                backgroundImage: `url(${src})`
-            }).appendTo(list);
-        });
-
-        $('<div class="sidebar-widget-content">')
-            .append(list)
-            .appendTo(sidebar_avatar_menu);
-
-        $('#sidebar').prepend(sidebar_avatar_menu);
-    },
-
     setResponsiveDisplay (state = true) {
         $('html').toggleClass('responsive-display', state);
 
@@ -136,16 +15,21 @@ const Responsive = {
     },
 
     engage () {
-        if (Responsive.media_query.matches) {
-            Responsive.responsify();
-            Responsive.setResponsiveDisplay();
-        } else {
-            Responsive.media_query.addListener(Responsive.responsify);
-        }
+        Responsive.setResponsiveDisplay(Responsive.isResponsive());
 
-        Responsive.media_query.addListener(() => {
-            Responsive.setResponsiveDisplay(Responsive.media_query.matches);
+        Responsive.media_query.addEventListener('change', () => {
+            Responsive.setResponsiveDisplay(Responsive.isResponsive());
         });
+    },
+
+    isResponsive() {
+        return Responsive.media_query.matches;
+    },
+
+    isFullscreen() {
+        const cache = STUDIP.Cache.getInstance('responsive.');
+
+        return cache.get('fullscreen-mode') ?? false;
     }
 };
 
diff --git a/resources/assets/stylesheets/highcontrast.scss b/resources/assets/stylesheets/highcontrast.scss
index 27a4d01cd43..849f0d44097 100644
--- a/resources/assets/stylesheets/highcontrast.scss
+++ b/resources/assets/stylesheets/highcontrast.scss
@@ -305,8 +305,8 @@ button.button[disabled]:hover {
 }
 
 /* Black borders */
-.sidebar .sidebar-widget,
-.sidebar .sidebar-widget-placeholder,
+.sidebar-widget,
+.sidebar-widget-placeholder,
 section.contentbox,
 article.studip,
 form.default,
@@ -1421,12 +1421,12 @@ button.skiplink {
 // Benachrichtigungen: Schwarzer Text auf weißem Hintergrund, beim Hovern invertieren.
 
 #notification-container a.enable-desktop-notifications,
-#notification_container a.mark-all-as-read {
+#notification-container a.mark-all-as-read {
     background-color: $white;
     color: $contrast-blue;
 }
 
-#notification_container .list .item {
+#notification-container .list .item {
     background-color: $white;
     color: $black;
 
diff --git a/resources/assets/stylesheets/less/breakpoints.less b/resources/assets/stylesheets/less/breakpoints.less
index 638df23a5b7..c06be0b4021 100644
--- a/resources/assets/stylesheets/less/breakpoints.less
+++ b/resources/assets/stylesheets/less/breakpoints.less
@@ -2,4 +2,6 @@
 @major-breakpoint-tiny: 0;
 @major-breakpoint-small: 576px;
 @major-breakpoint-medium: 768px;
-@major-breakpoint-large: 1200px;
+@major-breakpoint-large: 1024px;
+@major-breakpoint-xlarge: 1280px;
+@major-breakpoint-xxlarge: 1600px;
diff --git a/resources/assets/stylesheets/less/responsive.less b/resources/assets/stylesheets/less/responsive.less
deleted file mode 100644
index b9a51522a58..00000000000
--- a/resources/assets/stylesheets/less/responsive.less
+++ /dev/null
@@ -1,493 +0,0 @@
-@import (reference) "breakpoints.less";
-@import (reference) "visibility.less";
-
-@header-bar-container-height:40px;
-
-@responsive-menu-width: 270px;
-@responsive-menu-shadow-width: 6px;
-@responsive-menu-shadow-color: rgba(0, 0, 0, 0.5);
-
-// Responsive main navigation (hamburger navigation to the left)
-#responsive-container {
-    display: none;
-    user-select: none;
-
-    input[type="checkbox"] {
-        display: none;
-    }
-    label[for="responsive-toggle"]:first-child {
-        .icon('before', 'hamburger-icon', 'info_alt', 20);
-        cursor: pointer;
-    }
-
-    ul, li {
-        list-style-type: none;
-        margin: 0;
-        padding: 0;
-    }
-    li {
-        border-top: 1px solid @brand-color-lighter;
-    }
-    a {
-        color: white;
-        &:hover {
-            color: white;
-        }
-    }
-
-    .nav-label {
-        .hide-text();
-        .icon('before', 'arr_1right', 'info_alt', 24);
-        &:before {
-            transition: transform 300ms;
-            vertical-align: text-bottom;
-        }
-
-        position: absolute;
-        left: 5px;
-        top: 5px;
-
-        border-right: 1px solid @brand-color-lighter;
-        padding-right: 2px;
-    }
-
-    // Create second, invisible toggle that closes the menu when clicked/touched
-    // outside of the menu
-    label[for="responsive-toggle"]:last-child {
-        display: none;
-
-        position: fixed;
-        top: @header-bar-container-height;
-        right: 0;
-        bottom: 0;
-        left: @responsive-menu-width;
-        height: 100vh;
-    }
-}
-
-#responsive-navigation {
-    #gradient > .horizontal(@brand-color-darker, @brand-color-light);
-    background-clip: content-box;
-    transition: left 300ms;
-
-
-    left: (-@responsive-menu-width - @responsive-menu-shadow-width);
-    &.visible {
-        left: 0;
-
-        + label[for="responsive-toggle"] {
-            display: initial;
-        }
-    }
-
-    position: fixed;
-    top: @header-bar-container-height; // + 1px white border
-    bottom: 0;
-    height: calc(100vh - @header-bar-container-height);
-
-    box-sizing: border-box;
-    max-width: @responsive-menu-width;
-    width: @responsive-menu-width;
-    margin-bottom: @header-bar-container-height;
-
-    border-right: @responsive-menu-shadow-width solid @responsive-menu-shadow-color;
-    overflow: auto;
-
-    > li {
-        &:first-child {
-            border-top: none;
-        }
-
-        .icon {
-            .square(26px);
-            display: inline-block;
-            padding-right: 8px;
-            vertical-align: text-bottom;
-            width: 26px;
-        }
-
-        > .navigation-item {
-            font-size: 1.3em;
-            margin: 10px;
-        }
-    }
-
-    .navigation-item {
-        position: relative;
-    }
-
-    .nav-title {
-        display: block;
-        padding-left: 34px;
-        a {
-            display: block;
-            padding: 5px;
-        }
-    }
-
-    ul {
-        transition: max-height 400ms ease;
-
-        max-height: 0px;
-        overflow: hidden;
-        > li {
-            background-color: @brand-color-lighter;
-            > .navigation-item {
-                padding: 10px;
-            }
-        }
-
-        .icon {
-            display: none;
-        }
-    }
-    input:checked + label::before {
-        transform: rotate(90deg);
-    }
-
-    input:checked + label + ul {
-        max-height: 600px;
-        > li {
-            background-color: mix(rgba(0, 0, 0, 0.2), @brand-color-lighter);
-        }
-    }
-}
-
-// Responsive sidebar menu (small hamburger menu to the right)
-#barBottomright #sidebar-menu {
-    .icon('before', 'hamburger-icon-small', 'info_alt', 20);
-    cursor: pointer;
-    display: none;
-    margin: 0 2px;
-    text-align: right;
-    vertical-align: top;
-}
-
-
-/* @deprecated use .hidden-medium-up */
-.media-breakpoint-medium-up({
-    .responsive-visible {
-        display: none;
-    }
-});
-
-.responsive-display {
-    // Hide sidebar from view until .responsified
-    &:not(.responsified) {
-        #sidebar {
-            display: none;
-        }
-    }
-
-    .media-breakpoint-small-down({
-        #header, #navigation-level-1, #site-title, #barTopMenu,
-        #barBottomLeft .current_page, #barBottommiddle, #barBottomLeft, #barBottomArrow,
-        #tabs, .sidebar-image, #sidebar-navigation:not(.show), #barTopFont, #main-footer-info, .sidebar-widget-header,
-        .tabs_wrapper .colorblock {
-            display: none !important;
-        }
-
-        #current-page-structure {
-            #navigation-level-2 {
-                display: flex;
-                flex-direction: row;
-                flex-wrap: nowrap;
-
-                background-color: @dark-gray-color-10;
-                border-bottom: 1px solid @dark-gray-color-40;
-
-                .colorblock,
-                #context-title,
-                .context_icon,
-                .tabs_wrapper {
-                    transition: unset;
-                }
-
-                #context-title,
-                .tabs_wrapper {
-                    background: transparent;
-                    border-width: 0;
-                    flex: 1;
-                }
-
-                #context-title {
-                    flex: 1;
-
-                    overflow: hidden;
-                    text-overflow: ellipsis;
-                    white-space: nowrap;
-
-                    + .tabs_wrapper {
-                        flex: 0;
-                        align-self: flex-end;
-                    }
-                }
-            }
-
-            .tabs_wrapper {
-                justify-content: flex-end;
-                .helpbar-container {
-                    top: 0px;
-                    right: 6px;
-                }
-            }
-        }
-        .responsive-hidden {
-            display: none;
-        }
-        #notification_marker {
-            display: inline-block;
-            margin-top: 0;
-            vertical-align: initial;
-
-            width: 22px;
-            padding-left: 5px;
-            padding-right: 5px;
-            height: 20px;
-            line-height: 20px;
-        }
-
-        #avatar-menu-container {
-            position: relative;
-            bottom: 0px;
-            right: 0px;
-            line-height: 20px !important;
-
-            #avatar-menu {
-                display: none;
-            }
-
-            &::after {
-                display: none !important;
-            }
-        }
-
-        #top-bar {
-            box-sizing: border-box;
-            height: @header-bar-container-height;
-            position: fixed;
-            top: 0;
-            margin-left: 0px;
-            margin-right: 0px;
-            width: 100%;
-        }
-
-        #header-links, #header-links ul {
-            box-sizing: border-box;
-            flex: 1;
-        }
-
-        #barBottomright {
-            flex: 1 !important;
-            #sidebar-menu {
-                display: inline-block;
-            }
-            .list {
-                &::before, &::after {
-                    display: none;
-                }
-                @width: 300px;
-                @arrow-height: 10px;
-
-                margin-top: 2px;
-                width: @width;
-                max-width: @width;
-
-                &.below {
-                    left: (-@width + 90);
-                    &:before {
-                        left: (@width - 90);
-                    }
-                }
-
-            }
-
-            > ul > li {
-                flex: 1 0 auto;
-
-                &:first-child {
-                    flex: 1 1 100%;
-                }
-            }
-        }
-
-        #notification_container {
-            position: inherit !important;
-            /*top: 8px;*/
-            width: 32px;
-            height: 20px;
-        }
-
-        #responsive-container {
-            display: block;
-        }
-
-        #current-page-structure {
-            margin-left: 0;
-            margin-right: 0;
-        }
-
-        #current-page-structure, #top-bar, #navigation-level-1, #layout_content {
-            min-width: inherit !important;
-        }
-
-        #layout_content {
-            margin: 0px 4px;
-        }
-
-        .visible-sidebar {
-            right: 0 !important;
-            transition: right 300ms;
-        }
-        #sidebar.visible-sidebar #sidebar-shadow-toggle {
-            display: initial;
-        }
-
-        #sidebar {
-            #gradient > .horizontal(@brand-color-light, @brand-color-darker);
-            background-clip: content-box;
-            transition: right 300ms;
-
-            position: fixed;
-            top: @header-bar-container-height;
-            right: (-@responsive-menu-width - @responsive-menu-shadow-width);
-            left: auto;
-            bottom: 0;
-
-            margin-right: 0px;
-
-            width: @responsive-menu-width;
-
-            overflow: hidden;
-            overflow-y: auto;
-            z-index: 10000;
-
-            border-left: @responsive-menu-shadow-width solid @responsive-menu-shadow-color;
-
-            box-sizing: border-box;
-
-            &:before {
-                border: 0 !important;
-            }
-
-            background: inherit;
-            border: 0;
-
-            .widget-links li.active {
-                &:before, &:after {
-                    display: none;
-                }
-                margin-right: 0;
-            }
-
-            // Create second, invisible toggle that closes the menu when
-            // clicked/touched outside of the menu
-            #sidebar-shadow-toggle {
-                position: fixed;
-                top: @header-bar-container-height;
-                right: @responsive-menu-width;
-                bottom: 0;
-                left: 0;
-                height: 100vh;
-
-                display: none;
-            }
-        }
-        #index,
-        #login,
-        #request_new_password,
-        #web_migrate {
-            div.index_container {
-                height: calc(100% - 74px);
-                position: static;
-                top: 0;
-
-                div.messagebox,
-                div.index_main {
-                    margin: 1em auto;
-                }
-            }
-
-            #background-desktop,
-            #background-mobile {
-                position: fixed;
-            }
-        }
-
-        #main-footer {
-            display: block;
-            min-width: 0;
-            width: 100vw;
-        }
-    });
-
-    .media-breakpoint-tiny-down({
-        #index,
-        #login,
-        #request_new_password,
-        #web_migrate {
-            div.index_container {
-                div.messagebox,
-                div.index_main {
-                    margin: 0 auto;
-                }
-            }
-        }
-    });
-}
-
-// Hide duplicated avatar menu in sidebar as default
-.sidebar-avatar-menu {
-    display: none;
-    margin-top: 0 !important;
-}
-
-.responsive-display {
-    .sidebar-avatar-menu {
-        display: block;
-    }
-
-    #quicksearch_item {
-        padding: 0;
-    }
-    #search_sem_quick_search_frame {
-        display: flex;
-        flex-direction: row;
-        justify-content: flex-end;
-
-        .quicksearchbox {
-            transition: all 300ms;
-            opacity: 0;
-            max-width: 0;
-        }
-
-        &.open {
-            .quicksearchbox {
-                opacity: 1;
-                max-width: 1000px;
-                width: 100% !important;
-            }
-        }
-    }
-
-    #barBottomright {
-        ul {
-            li:first-child {
-                flex: 1 0 auto;
-            }
-            li#quicksearch_item {
-                flex: 1 1 100%;
-            }
-        }
-    }
-
-    table.default tfoot .button {
-        margin-top: 0.5em;
-        margin-bottom: 0.5em;
-    }
-
-    .ui-dialog.ui-widget.ui-widget-content.studip-confirmation {
-        min-width: 20vw;
-        max-width: 100vw;
-    }
-}
diff --git a/resources/assets/stylesheets/less/visibility.less b/resources/assets/stylesheets/less/visibility.less
index f1d713bcdc3..50dafee85c5 100644
--- a/resources/assets/stylesheets/less/visibility.less
+++ b/resources/assets/stylesheets/less/visibility.less
@@ -1,30 +1,46 @@
-.media-breakpoint-large-down(@rules) {
+.media-breakpoint-xxlarge-down(@rules) {
     @rules();
 }
 
+.media-breakpoint-xlarge-down(@rules) {
+    @media (max-width: (@major-breakpoint-xxlarge - 1px)) { @rules(); }
+}
+
+.media-breakpoint-large-down(@rules) {
+    @media (max-width: (@major-breakpoint-xlarge - 1px)) { @rules(); }
+}
+
 .media-breakpoint-medium-down(@rules) {
-  @media (max-width: (@major-breakpoint-large - 1px)) { @rules(); }
+    @media (max-width: (@major-breakpoint-large - 1px)) { @rules(); }
 }
 
 .media-breakpoint-small-down(@rules) {
-  @media (max-width: (@major-breakpoint-medium - 1px)) { @rules(); }
+    @media (max-width: (@major-breakpoint-medium - 1px)) { @rules(); }
 }
 
 .media-breakpoint-tiny-down(@rules) {
-  @media (max-width: (@major-breakpoint-small - 1px)) { @rules(); }
+    @media (max-width: (@major-breakpoint-small - 1px)) { @rules(); }
 }
 
 
+.media-breakpoint-xxlarge-up(@rules) {
+    @media (min-width: (@major-breakpoint-xxlarge)) { @rules(); }
+}
+
+.media-breakpoint-xlarge-up(@rules) {
+    @media (min-width: (@major-breakpoint-xlarge)) { @rules(); }
+}
+
 .media-breakpoint-large-up(@rules) {
-  @media (min-width: (@major-breakpoint-large)) { @rules(); }
+    @media (min-width: (@major-breakpoint-large)) { @rules(); }
 }
 
 .media-breakpoint-medium-up(@rules) {
-  @media (min-width: (@major-breakpoint-medium)) { @rules(); }
+    @media (min-width: (@major-breakpoint-medium)) { @rules(); }
 }
 
 .media-breakpoint-small-up(@rules) {
-  @media (min-width: (@major-breakpoint-small)) { @rules(); }
+    @media (min-width: (@major-breakpoint-small)) { @rules(); }
 }
 
 .media-breakpoint-tiny-up(@rules) {
@@ -32,6 +48,20 @@
 }
 
 
+.hidden-xxlarge-down {
+    .media-breakpoint-xxlarge-down({ display: none !important; })
+}
+.hidden-xxlarge-up {
+    .media-breakpoint-xxlarge-up({ display: none !important; });
+}
+
+.hidden-xlarge-down {
+    .media-breakpoint-xlarge-down({ display: none !important; })
+}
+.hidden-xxlarge-up {
+    .media-breakpoint-large-up({ display: none !important; });
+}
+
 .hidden-large-down {
     .media-breakpoint-large-down({ display: none !important; })
 }
diff --git a/resources/assets/stylesheets/scss/breakpoints.scss b/resources/assets/stylesheets/scss/breakpoints.scss
index 18681fc3077..07bf9be5504 100644
--- a/resources/assets/stylesheets/scss/breakpoints.scss
+++ b/resources/assets/stylesheets/scss/breakpoints.scss
@@ -2,4 +2,9 @@
 $major-breakpoint-tiny: 0;
 $major-breakpoint-small: 576px;
 $major-breakpoint-medium: 768px;
-$major-breakpoint-large: 1200px;
+$major-breakpoint-large: 1024px;
+$major-breakpoint-xlarge: 1280px;
+$major-breakpoint-xxlarge: 1600px;
+
+//** Breakpoint for sidebar display
+$minor-breakpoint-sidebar-fullscreen: 450px;
diff --git a/resources/assets/stylesheets/scss/buttons.scss b/resources/assets/stylesheets/scss/buttons.scss
index 3ffb84ca067..88b208941a8 100644
--- a/resources/assets/stylesheets/scss/buttons.scss
+++ b/resources/assets/stylesheets/scss/buttons.scss
@@ -122,3 +122,8 @@ button.button {
         margin-right: 0;
     }
 }
+
+button.styleless {
+    background-color: unset;
+    border: 0;
+}
diff --git a/resources/assets/stylesheets/scss/contentbar.scss b/resources/assets/stylesheets/scss/contentbar.scss
index ba8abc5c7f9..df2026f8c70 100644
--- a/resources/assets/stylesheets/scss/contentbar.scss
+++ b/resources/assets/stylesheets/scss/contentbar.scss
@@ -1,109 +1,151 @@
 .contentbar {
     background-color: $dark-gray-color-5;
-    border: 1px solid $dark-gray-color-30;
-    gap: 10px;
-    margin-bottom: 15px;
-    min-height: 58px;
-    padding: 0px 9px 0px 9px;
+    border: solid thin $dark-gray-color-30;
     display: flex;
+    flex-wrap: wrap;
+    height: auto;
     justify-content: space-between;
-    flex-wrap: nowrap;
+    margin-bottom: 15px;
+    min-height: 30px;
+    padding: 1em;
 
-    .contentbar_title {
-        align-items: center;
+    .contentbar-wrapper-left {
         display: flex;
-        font-size: 1.3em;
-        max-width: 60%;
-        padding-left: 0.5em;
-
-        ul.breadcrumb {
-            list-style: none;
-            padding-left: 10px;
-            width: 100%;
-            display: inline-block;
-
-            div {
-                float: left;
-                max-width: 100%;
-            }
+        flex: auto;
+        /* Taken from courseware.scss line 323 */
+        max-width: calc(100% - 106px);
 
-            li {
-                display: inline;
-            }
+        .contentbar-nav {
+            display: flex;
+        }
 
-            li+li:before {
-                padding: 5px;
-                color: $black;
-                content: "/";
+        .contentbar-breadcrumb {
+            display: flex;
+            font-size: 1.25em;
+            line-height: 1.5em;
+            margin-right: 1em;
+            min-width: 0;
+
+            .contentbar-icon {
+                flex: 0;
+                height: 24px;
+                margin-top: 2px;
+                width: 24px;
             }
 
-        }
-    }
+            ul {
+                display: flex;
+                list-style: none;
+                margin-left: 15px;
+                padding-left: 0;
+
+                li+li:before {
+                    padding: 0 0.25em;
+                    content: '/';
+                    background-repeat: no-repeat;
+                    background-position: center;
+                }
 
-    .textblock {
-        display: inline-block;
-        margin-left: 10px;
-        margin-right: 10px;
+                .contentbar-breadcrumb-item {
+                    display: inline;
+                    flex-shrink: 100000;
+                    min-width: 0;
+                    overflow: hidden;
+                    text-overflow: ellipsis;
+                    white-space: nowrap;
+
+                    a {
+                        color: $base-color;
+                        text-decoration: none;
+                        &:hover {
+                            color: $active-color;
+                        }
+                    }
+
+                    &.contentbar-breadcrumb-item-current {
+                        flex-shrink: 1;
+                    }
+                }
+            }
+        }
     }
 
-    .contentbar_info {
-        align-items: center;
-        text-align: right;
+    .contentbar-wrapper-right {
         display: flex;
-        flex-wrap: nowrap;
-        gap: 5px;
-        margin: 5px;
+        justify-content: flex-end;
+        position: relative;
 
-        .contentbar-icons {
-            display: flex;
+        .contentbar-button-wrapper {
+            height: 24px;
+            margin: 0 7px;
 
-            label {
-                margin-left: 0.5em;
-                margin-right: 0.5em;
+            @-moz-document url-prefix() {
+                &.contentbar-action-menu-wrapper {
+                    margin-top: 2px;
+                }
             }
 
-            .consuming_mode_trigger {
-                margin-left: 0.5em;
-                margin-right: 0.5em;
-                margin-top: -1px;
+            .contentbar-button, .cw-ribbon-button {
+                background-color: transparent;
+                background-position: center;
+                background-repeat: no-repeat;
+                background-size: 24px;
+                border: none;
+                cursor: pointer;
+                display: inline-block;
+                height: 24px;
+                width: 24px;
 
-                @media (max-width: 767px) {
-                    display: none !important;
+                &.contentbar-button-menu,
+                &.cw-ribbon-button-menu {
+                    @include background-icon(table-of-contents, clickable, 24);
+                }
+
+                &.contentbar-button-zoom::before {
+                    left: -5px;
+                    position: relative;
+                    top: -2px;
                 }
 
                 @-moz-document url-prefix() {
-                    margin-top: -4px;
+                    &.contentbar-button-zoom::before {
+                        top: -3px;
+                    }
                 }
-            }
 
-            .consuming_mode_trigger:not(body.consuming_mode .consuming_mode_trigger) {
-                display: inline-block;
-                width: 24px;
-                height: 24px;
-                @include icon(before, fullscreen-on, clickable, 24px, 0px);
             }
 
-            nav {
-                margin-left: 2px;
-                margin-top: 2px;
-            }
         }
     }
+
 }
 
-body.consuming_mode {
-    #main-header, #main-footer {
-        display: none;
+body:not(.consuming_mode) {
+    .consuming_mode_trigger {
+        @include icon(before, fullscreen-on, clickable, 24px, 0);
+        margin-top: -2px;
+        margin-left: 0
     }
+}
 
-    #sidebar {
-        margin-left: -280px;
+body.consuming_mode {
+    #barBottomContainer,
+    #flex-header,
+    .secondary-navigation,
+    #barTopStudip,
+    #page_title_container {
+        max-height: 0px;
         opacity: 0;
+        overflow: hidden;
+    }
+
+    #layout_wrapper {
+        padding-top: 0px;
     }
 
-    #content {
-        grid-column: 1/3;
-        grid-row: 1/3;
+    #layout-sidebar {
+        margin-left: -280px;
+        opacity: 0;
     }
 
     .contentbar {
@@ -114,6 +156,8 @@ body.consuming_mode {
     }
 
     .consuming_mode_trigger {
-        @include icon(before, fullscreen-off, clickable, 25px, 5px);
+        @include icon(before, focusmode-off, clickable, 24px, 0);
+        margin-top: -2px;
+        margin-left: 0
     }
 }
diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss
index 2c6649f23ca..79476bae70e 100644
--- a/resources/assets/stylesheets/scss/courseware.scss
+++ b/resources/assets/stylesheets/scss/courseware.scss
@@ -404,7 +404,7 @@ $consum_ribbon_width: calc(100% - 58px);
         }
 
         &.cw-ribbon-button-zoom-out {
-            @include background-icon(fullscreen-off, clickable, 24);
+            @include background-icon(focusmode-off, clickable, 24);
         }
 
         &.cw-ribbon-button-prev {
diff --git a/resources/assets/stylesheets/scss/header.scss b/resources/assets/stylesheets/scss/header.scss
index 0c36b8989a3..1ddd3bdec56 100644
--- a/resources/assets/stylesheets/scss/header.scss
+++ b/resources/assets/stylesheets/scss/header.scss
@@ -1,5 +1,7 @@
+@import "./layouts.scss";
+
 /* --- header.css ----------------------------------------------------------- */
-body > header {
+#main-header {
     box-sizing: border-box;
     padding-top: $bar-bottom-container-height;
 }
@@ -23,13 +25,17 @@ body > header {
     z-index: 10000;
 
 }
+
 #responsive-menu,
 #site-title {
     flex: 0 0 auto;
-    padding: 0 15px;
     z-index: 2;
 }
 
+#site-title {
+    padding: 0 $page-margin;
+}
+
 // Fix header covering relevant other areas
 // $see https://gitlab.studip.de/studip/studip/-/issues/1019
 html {
@@ -188,22 +194,3 @@ html {
     height: $header-height;
     z-index: 3;
 }
-
-// Slide menu in header navigation
-#responsive-menu {
-    box-sizing: border-box;
-    overflow-x: hidden;
-    padding: 0;
-    text-indent: -150px;
-
-    @media not prefers-reduced-motion {
-        transition: all 500ms;
-    }
-}
-body.fixed {
-    #responsive-menu {
-        overflow-x: visible;
-        padding: 0 15px;
-        text-indent: 0;
-    }
-}
diff --git a/resources/assets/stylesheets/scss/helpbar.scss b/resources/assets/stylesheets/scss/helpbar.scss
index 14370d6e051..da0f0ca2fa6 100644
--- a/resources/assets/stylesheets/scss/helpbar.scss
+++ b/resources/assets/stylesheets/scss/helpbar.scss
@@ -15,7 +15,7 @@ $border-width: 4px;
     position: relative;
     top: 1px;
     min-width: 32px;
-    right: 12px;
+    right: 17px;
 
     float: right;
 
@@ -41,6 +41,7 @@ $border-width: 4px;
 
     > .helpbar-toggler {
         float: right;
+        margin-top: 2px;
     }
 }
 
diff --git a/resources/assets/stylesheets/scss/layouts.scss b/resources/assets/stylesheets/scss/layouts.scss
index 2f0a94f1943..f38518b5373 100644
--- a/resources/assets/stylesheets/scss/layouts.scss
+++ b/resources/assets/stylesheets/scss/layouts.scss
@@ -1,5 +1,7 @@
 // TODO: SCSSify
 
+@import "./variables.scss";
+
 $page-margin: 15px;
 
 $content-width: 400px;
@@ -253,7 +255,7 @@ body {
 
         flex-grow: 1;
 
-        min-width: $page-width;
+        min-width: $site-width;
 
         .tabs_wrapper {
             display: flex;
diff --git a/resources/assets/stylesheets/scss/navigation.scss b/resources/assets/stylesheets/scss/navigation.scss
index 50e47a21604..def2e913598 100644
--- a/resources/assets/stylesheets/scss/navigation.scss
+++ b/resources/assets/stylesheets/scss/navigation.scss
@@ -214,49 +214,11 @@ body:not(.fixed) #navigation-level-1-items {
     }
 }
 
-// Toggle mechanism for touch/hover
-#barTopMenu-toggle {
-    display: none;
-}
-label[for="barTopMenu-toggle"] {
-    @include background-icon(hamburger, info_alt, 16);
-    background-position: 0 center;
-    background-repeat: no-repeat;
-
-    color: $white;
-    line-height: $bar-bottom-container-height;
-    overflow: hidden;
-    padding-left: (5px + 16px + 5px); // padding + icon + next padding
-    white-space: nowrap;
-
-    height: 0;
-    max-height: 0;
-    opacity: 0;
-    @media not prefers-reduced-motion {
-        transition: all 300ms;
-    }
-
-    // 1/4 of the screen's width, creates a bigger hover area
-    width: 25vw;
-
-}
-html.no-touch {
-    #barTopMenu-toggle,
-    label[for="barTopMenu-toggle"] {
-        pointer-events: none;
-    }
-}
-
 body.fixed {
     #navigation-level-1 {
         height: $header-height;
     }
 
-    label[for="barTopMenu-toggle"] {
-        opacity: 1;
-        max-height: $bar-bottom-container-height;
-        height: auto;
-    }
     #navigation-level-1-items {
         background-color: $base-color;
 
@@ -318,8 +280,7 @@ body.fixed {
 
         }
     }
-    #responsive-menu:hover #navigation-level-1-items,
-    #barTopMenu-toggle:checked ~ #navigation-level-1-items {
+    #responsive-menu:hover #navigation-level-1-items {
         display: block;
     }
 }
diff --git a/resources/assets/stylesheets/scss/responsive.scss b/resources/assets/stylesheets/scss/responsive.scss
new file mode 100644
index 00000000000..95b97f3a0d6
--- /dev/null
+++ b/resources/assets/stylesheets/scss/responsive.scss
@@ -0,0 +1,796 @@
+@use "../mixins/colors.scss";
+@import "breakpoints";
+@import "buttons";
+@import "sidebar";
+
+$header-bar-container-height: 40px;
+
+$responsive-menu-width: 270px;
+
+$sidebarIn: -15px;
+$sidebarOut: -330px;
+
+#responsive-toggle-desktop,
+#responsive-toggle-fullscreen {
+    display: none;
+}
+
+#responsive-toggle-fullscreen {
+    position: relative;
+    top: 4px;
+
+    img {
+        cursor: pointer;
+    }
+}
+
+#non-responsive-toggle-fullscreen {
+    margin-left: auto;
+    margin-right: 15px;
+    position: relative;
+    top: 2px;
+
+    img {
+        cursor: pointer;
+    }
+}
+
+#responsive-menu {
+    font-size: $font-size-base;
+    padding: 0 5px;
+    z-index: 1002;
+
+    .responsive-navigation-header {
+        display: flex;
+        margin-top: 2px;
+        padding: 2px 0 5px;
+
+        .menu-closed {
+            cursor: pointer;
+            transform: rotate(0deg);
+
+            @media not prefers-reduced-motion {
+                transition: 0.5s ease-in-out;
+            }
+        }
+
+        .menu-open {
+            cursor: pointer;
+            transform: rotate(90deg);
+
+            @media not prefers-reduced-motion {
+                transition: 0.5s ease-in-out;
+            }
+        }
+    }
+
+}
+
+#responsive-navigation-button {
+    cursor: pointer;
+    left: 10px;
+    position: relative;
+}
+
+#responsive-navigation-items {
+    background-color: $base-color;
+    left: 0;
+    max-width: $responsive-menu-width;
+    overflow-y: auto;
+    padding-bottom: 5px;
+    position: fixed;
+    top: 40px;
+
+    /* Safari only */
+    @media not all and (min-resolution: .001dpcm) {
+        @supports (-webkit-appearance: none) {
+            top: 43px;
+        }
+    }
+
+    width: $responsive-menu-width;
+
+    @media not prefers-reduced-motion {
+        transition: all 0.5s ease-in-out;
+    }
+
+    header {
+        background-image: url("#{$image-path}/sidebar/noicon-sidebar.png");
+        background-size: cover;
+        display: flex;
+        flex-wrap: wrap;
+        max-height: 250px;
+        overflow-y: auto;
+        padding: 10px;
+
+        .profile-info {
+            flex: auto;
+            font-size: $font-size-small;
+            padding: 20px;
+            position: relative;
+
+            .profile-pic {
+                width: 100%;
+
+                img,
+                svg {
+                    cursor: pointer;
+                    height: 50px;
+                    width: 50px;
+                }
+            }
+        }
+
+        > div {
+            display: flex;
+            width: 100%;
+
+            .avatar-navigation {
+                flex: auto;
+                padding-left: 10px;
+
+                .navigation-item {
+                    background-color: transparent;
+
+                    &:hover {
+                        background-color: $base-color-80;
+                    }
+                }
+            }
+        }
+
+        .open-avatarmenu,
+        .close-avatarmenu {
+            button {
+                cursor: pointer;
+                position: relative;
+                top: calc(50% - 12px);
+            }
+        }
+    }
+
+    .main-navigation {
+        margin: 0 5px;
+        padding: 0 5px;
+    }
+
+    .navigation-item {
+        background-color: $base-color;
+        display: flex;
+        flex-wrap: wrap;
+        list-style-type: none;
+        margin: 0px;
+
+        &:not(:last-child) {
+            border-bottom: 1px solid $white;
+        }
+
+        &.navigation-up,
+        &.navigation-current {
+            border-bottom: 2px solid $white;
+
+            .navigation-icon {
+                img {
+                    padding-top: 0;
+                }
+            }
+
+            .navigation-title {
+                padding: 10px 10px 10px 0;
+            }
+
+            &:hover {
+                background-color: $base-color-80;
+            }
+
+        }
+
+        &.navigation-current {
+            background-color: $base-color-60;
+        }
+
+        a {
+            color: $white;
+            cursor: pointer;
+        }
+
+        button {
+            color: $white;
+            cursor: pointer;
+            display: flex;
+            flex: 0;
+            text-align: center;
+            width: 100%;
+
+            img,
+            svg {
+                padding-top: 12px;
+            }
+
+            &.navigation-in {
+                border-left: 1px solid $base-color-60;
+            }
+        }
+
+        &:not(.navigation-current):not(.navigation-up) {
+            button:hover {
+                background-color: $base-color-80;
+            }
+        }
+
+    }
+
+    .navigation-title {
+        color: $white;
+        flex: 1;
+
+        > a {
+            display: inline-block;
+            padding: 10px 10px 10px 5px;
+            text-align: left;
+            width: calc(100% - 15px);
+
+            .navigation-icon {
+                flex: 0;
+                width: 35px;
+            }
+
+            .navigation-text {
+                flex: 1;
+                padding-top: 2px;
+            }
+
+        }
+
+        img,
+        svg {
+            vertical-align: text-bottom;
+            margin-right: 10px;
+        }
+
+    }
+
+    a {
+        flex: 0;
+
+        &:hover {
+            background-color: $base-color-80;
+        }
+
+    }
+
+    img, svg {
+        vertical-align: text-bottom;
+    }
+}
+
+.responsive-display,
+.fullscreen-mode body:not(.consuming_mode) {
+
+    body {
+        display: inherit;
+    }
+
+    #responsive-menu {
+        flex: 0;
+    }
+
+    #site-title,
+    #quicksearch_item,
+    #avatar-menu-container,
+    #current-page-structure {
+        display: none;
+    }
+
+    #header-links {
+        > ul {
+            margin-right: 10px;
+
+            li {
+                padding: 0;
+
+                &.helpbar-container {
+                    float: unset;
+                    margin-top: 5px;
+                    right: 5px;
+                }
+            }
+        }
+
+        #notification-container,
+        .header_avatar_container,
+        #sidebar-menu {
+            display: none;
+        }
+    }
+
+    #navigation-level-1 {
+        display: none;
+    }
+
+    #sidebar {
+        background-color: $white;
+        position: absolute;
+        transform: translateX($sidebarOut);
+        z-index: 100;
+
+        &.responsive-hide {
+            @media not prefers-reduced-motion {
+                animation: slide-out 0.5s forwards;
+            }
+        }
+
+        &.responsive-show {
+            @media not prefers-reduced-motion {
+                animation: slide-in 0.5s forwards;
+            }
+
+            left: 15px;
+            position: relative;
+        }
+
+        .sidebar-image {
+            display: none;
+        }
+
+        > .sidebar-widget {
+            margin-top: 0;
+        }
+
+        > .sidebar-widget ~ .sidebar-widget {
+            margin-top: 15px;
+        }
+
+        @keyframes slide-in {
+            100% {
+                transform: translateX($sidebarIn);
+            }
+        }
+
+
+        @keyframes slide-out {
+            0% {
+                transform: translateX($sidebarIn);
+            }
+            100% {
+                transform: translateX($sidebarOut);
+            }
+        }
+    }
+
+    #sidebar-navigation {
+        display: none !important;
+    }
+
+    #current-page-structure {
+        #navigation-level-2 {
+            display: none !important;
+        }
+    }
+
+    #responsive-contentbar {
+        margin-bottom: 15px;
+        padding-bottom: 0.5em;
+        padding-left: 5px;
+
+        .contentbar-nav,
+        .cw-ribbon-nav {
+            width: auto;
+            margin-left: 5px;
+
+            .contentbar-button {
+
+                &.contentbar-button-sidebar {
+                    cursor: pointer;
+                    margin-right: 10px;
+
+                    img {
+                        transform: rotate(0deg);
+                    }
+
+                    &.contentbar-button-sidebar-open {
+                        img {
+                            transform: rotate(180deg);
+                        }
+                    }
+                }
+            }
+
+        }
+
+        .contentbar-wrapper-left {
+            flex: 1;
+            max-width: unset;
+
+            & > .contentbar-icon {
+                margin-right: 15px;
+            }
+
+            .contentbar-breadcrumb {
+                font-size: $font-size-large;
+                width: calc(100% - 75px);
+
+                > img {
+                    margin-left: 15px;
+                    width: 24px;
+                }
+            }
+        }
+
+        > .contentbar-wrapper-right {
+            position: relative;
+            left: 5px;
+
+            .contentbar-button,
+            nav {
+                position: relative;
+            }
+        }
+
+        &.cw-ribbon {
+            margin-top: 4px;
+
+            .cw-ribbon-tools {
+                right: 0;
+                top: 96px;
+            }
+        }
+    }
+
+    #toc {
+        position: absolute;
+        right: -8px;
+        top: 82px;
+    }
+
+    #toc_header {
+        height: 47px;
+    }
+
+    #main-footer {
+        display: none;
+    }
+}
+
+.responsive-display:not(.fullscreen-mode) {
+    #responsive-menu {
+        width: calc(100% - 56px);
+    }
+
+    #responsive-navigation-items {
+        max-width: unset;
+        width: 100%;
+    }
+
+    #sidebar {
+        height: 100%;
+        position: fixed;
+        transform: translateX($sidebarOut);
+        -webkit-transform: translateX($sidebarOut);
+        top: 80px;
+        z-index: 100;
+
+        &.responsive-show {
+            width: 100%;
+
+            .sidebar-widget {
+                width: calc(100% - 30px);
+            }
+        }
+    }
+}
+
+/* Settings especially for fullscreen mode */
+.fullscreen-mode:not(.responsive-display) {
+    body:not(.consuming_mode) {
+        display: flex;
+        flex-direction: row;
+        flex-wrap: wrap;
+
+        #top-bar {
+            max-height: unset;
+            opacity: 1;
+            overflow: unset;
+        }
+
+        #responsive-menu {
+            max-width: calc(100% - 100px);
+            min-width: calc(100% - 100px);
+            width: calc(100% - 100px);
+        }
+
+        #main-header {
+            flex-basis: 100%;
+        }
+
+        #responsive-toggle-fullscreen {
+            display: block;
+            margin-right: 5px;
+        }
+
+        .contentbar:not(#responsive-contentbar) {
+            display: none;
+        }
+
+        #sidebar {
+            flex: 0;
+
+            &.responsive-hide {
+                top: 110px;
+            }
+        }
+
+        #content-wrapper {
+            flex: 1;
+            height: 100%;
+        }
+    }
+}
+
+.consuming_mode {
+    display: unset;
+
+    #responsive-contentbar {
+        display: none;
+    }
+}
+
+html:not(.responsive-display):not(.fullscreen-mode) {
+    #responsive-navigation {
+        display: none;
+    }
+
+    body.fixed {
+        #responsive-navigation {
+            display: block;
+        }
+
+        #responsive-navigation-items {
+            margin-top: -5px;
+            width: 100%;
+        }
+    }
+
+}
+
+/* content from old responsive.less */
+.responsive-display {
+    @include media-breakpoint-small-down() {
+        #navigation-level-1,
+        #site-title,
+        #navigation-level1-items,
+        .current_page,
+        #tabs,
+        #site-title,
+        #footer,
+        .tabs_wrapper .colorblock {
+            display: none !important;
+        }
+
+        #layout_wrapper #current-page-structure {
+            #navigation-level-2 {
+                display: flex;
+                flex-direction: row;
+                flex-wrap: nowrap;
+
+                background-color: $dark-gray-color-10;
+                border-bottom: 1px solid $dark-gray-color-40;
+
+                .colorblock,
+                #context-title,
+                .context_icon,
+                .tabs_wrapper {
+                    transition: unset;
+                }
+
+                #context-title,
+                .tabs_wrapper {
+                    background: transparent;
+                    border-width: 0;
+                    flex: 1;
+                }
+
+                #context-title {
+                    flex: 1;
+
+                    overflow: hidden;
+                    text-overflow: ellipsis;
+                    white-space: nowrap;
+
+                    + .tabs_wrapper {
+                        flex: 0;
+                        align-self: flex-end;
+                    }
+                }
+            }
+        }
+        #layout_wrapper #current-page-structure .tabs_wrapper {
+            justify-content: flex-end;
+            .helpbar-container {
+                top: 0px;
+                right: 6px;
+            }
+        }
+        .responsive-hidden {
+            display: none;
+        }
+        #notification_marker {
+            display: inline-block;
+            margin-top: 0;
+            vertical-align: initial;
+
+            width: 22px;
+            padding-left: 5px;
+            padding-right: 5px;
+            height: 20px;
+            line-height: 20px;
+        }
+
+        #avatar-menu-container {
+            position: relative;
+            bottom: 0px;
+            right: 0px;
+            line-height: 20px !important;
+
+            #avatar-menu {
+                display: none;
+            }
+
+            &::after {
+                display: none !important;
+            }
+        }
+
+        #top-bar {
+            box-sizing: border-box;
+            height: $header-bar-container-height;
+            position: fixed;
+            top: 0;
+            margin-left: 0px;
+            margin-right: 0px;
+            width: 100%;
+        }
+
+        #header-links, #header-links ul {
+            box-sizing: border-box;
+            flex: 1;
+        }
+
+        #header-links {
+            flex: 1 !important;
+            .list {
+                &::before,
+                &::after {
+                    display: none;
+                }
+                $width: 300px;
+                $arrow-height: 10px;
+
+                margin-top: 2px;
+                width: $width;
+                max-width: $width;
+
+                &.below {
+                    left: (-$width + 90px);
+                    &:before {
+                        left: ($width - 90px);
+                    }
+                }
+
+            }
+
+            > ul > li {
+                flex: 1 0 auto;
+
+                &:first-child {
+                    flex: 1 1 100%;
+                }
+            }
+        }
+
+        #notification-container {
+            position: inherit !important;
+            /*top: 8px;*/
+            width: 32px;
+            height: 20px;
+        }
+
+        #responsive-container {
+            display: block;
+        }
+
+        #current-page-structure {
+            margin-left: 0;
+            margin-right: 0;
+        }
+
+        #current-page-structure, #top-bar, #navigation-level-1, #layout_content {
+            min-width: inherit !important;
+        }
+
+        #layout_content {
+            margin: 0px 4px;
+        }
+
+        #index,
+        #login,
+        #request_new_password,
+        #web_migrate {
+            div.index_container {
+                height: calc(100% - 74px);
+                position: static;
+                top: 0;
+
+                div.messagebox,
+                div.index_main {
+                    margin: 1em auto;
+                }
+            }
+
+            #background-desktop,
+            #background-mobile {
+                position: fixed;
+            }
+        }
+    }
+
+    @include media-breakpoint-tiny-down() {
+        #index,
+        #login,
+        #request_new_password,
+        #web_migrate {
+            div.index_container {
+                div.messagebox,
+                div.index_main {
+                    margin: 0 auto;
+                }
+            }
+        }
+    }
+}
+
+.responsive-display {
+    #quicksearch_item {
+        padding: 0;
+    }
+    #search_sem_quick_search_frame {
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-end;
+
+        .quicksearchbox {
+            transition: all 300ms;
+            opacity: 0;
+            max-width: 0;
+        }
+
+        &.open {
+            .quicksearchbox {
+                opacity: 1;
+                max-width: 1000px;
+                width: 100% !important;
+            }
+        }
+    }
+
+    #header-links {
+        ul {
+            li:first-child {
+                flex: 1 0 auto;
+            }
+            li#quicksearch_item {
+                flex: 1 1 100%;
+            }
+        }
+    }
+
+    table.default tfoot .button {
+        margin-top: 0.5em;
+        margin-bottom: 0.5em;
+    }
+
+    .ui-dialog.ui-widget.ui-widget-content.studip-confirmation {
+        min-width: 20vw;
+        max-width: 100vw;
+    }
+}
diff --git a/resources/assets/stylesheets/scss/table_of_contents.scss b/resources/assets/stylesheets/scss/table_of_contents.scss
index 4298e3dbc8f..adf8df9d171 100644
--- a/resources/assets/stylesheets/scss/table_of_contents.scss
+++ b/resources/assets/stylesheets/scss/table_of_contents.scss
@@ -31,11 +31,9 @@ ul.numberedchapters {
     width: 0%;
     z-index: 100;
     position: absolute;
-    right: 2px;
-    top: -10px;
+    right: -22px;
+    top: -25px;
     background-color: $white;
-    min-height: 10%;
-    max-height: 100%;
     border: 1px solid #d0d7e3;
     margin-bottom: 10px;
     -webkit-box-shadow: 2px 2px #ccc;
@@ -59,7 +57,7 @@ ul.numberedchapters {
     height: 58px;
     overflow: hidden;
     background-color: $white;
-    color: $black !important;
+    color: $black;
     margin-bottom: -0.5em;
     border-bottom: thin solid #d0d7e3;
     display: flex;
@@ -79,8 +77,11 @@ ul.numberedchapters {
     margin-left: 10px;
     margin-bottom: unset;
 }
-.toc_transform {
-    transition: all 0.8s ease!important;
+
+@media not prefers-reduced-motion {
+    .toc_transform {
+        transition: all 0.8s ease;
+    }
 }
 
 #main_content {
@@ -201,7 +202,7 @@ section > .toc {
     }
 
     #toc {
-        max-width: 94%!important;
+        max-width: 94%;
     }
 
     ul.breadcrumb {
@@ -211,17 +212,13 @@ section > .toc {
         width: 70%;
     }
 
-    #toc_header {
-        width: 90%;
-    }
-
     .consuming_mode .toc_overview {
-        top: 51px!important;
+        top: 51px;
     }
 }
 
 .wiki {
-    border: unset!important;
+    border: unset;
 }
 
 .action-menu {
diff --git a/resources/assets/stylesheets/scss/visibility.scss b/resources/assets/stylesheets/scss/visibility.scss
index 02004d21dc4..df56df15602 100644
--- a/resources/assets/stylesheets/scss/visibility.scss
+++ b/resources/assets/stylesheets/scss/visibility.scss
@@ -1,6 +1,16 @@
-@mixin media-breakpoint-large-down() {
+@mixin media-breakpoint-xxlarge-down() {
     @content;
 }
+@mixin media-breakpoint-xlarge-down() {
+  @media (max-width: ($major-breakpoint-xxlarge - 1px)) {
+      @content;
+  }
+}
+@mixin media-breakpoint-large-down() {
+  @media (max-width: ($major-breakpoint-xlarge - 1px)) {
+      @content;
+  }
+}
 @mixin media-breakpoint-medium-down() {
   @media (max-width: ($major-breakpoint-large - 1px)) {
       @content;
@@ -17,6 +27,16 @@
   }
 }
 
+@mixin media-breakpoint-xxlarge-up() {
+  @media (min-width: ($major-breakpoint-xxlarge)) {
+      @content;
+  }
+}
+@mixin media-breakpoint-xlarge-up() {
+  @media (min-width: ($major-breakpoint-xlarge)) {
+      @content;
+  }
+}
 @mixin media-breakpoint-large-up() {
   @media (min-width: ($major-breakpoint-large)) {
       @content;
@@ -36,6 +56,33 @@
     @content;
 }
 
+@mixin sidebar-breakpoint-down() {
+    @media (max-width: ($minor-breakpoint-sidebar-fullscreen)) {
+        @content;
+    }
+}
+
+@mixin hidden-xxlarge-down {
+    @include media-breakpoint-xxlarge-down() {
+        display: none !important;
+    }
+}
+@mixin hidden-xxlarge-up {
+    @include media-breakpoint-xxlarge-up() {
+        display: none !important;
+    }
+}
+
+@mixin hidden-xlarge-down {
+    @include media-breakpoint-xlarge-down() {
+        display: none !important;
+    }
+}
+@mixin hidden-xlarge-up {
+    @include media-breakpoint-xlarge-up() {
+        display: none !important;
+    }
+}
 
 @mixin hidden-large-down {
     @include media-breakpoint-large-down() {
diff --git a/resources/assets/stylesheets/studip.less b/resources/assets/stylesheets/studip.less
index ed40030871c..0b24c86ed22 100644
--- a/resources/assets/stylesheets/studip.less
+++ b/resources/assets/stylesheets/studip.less
@@ -8,7 +8,6 @@
 @import "less/variables.less";
 @import "less/breakpoints.less";
 @import "less/visibility.less";
-@import "less/responsive.less";
 
 @import "less/tables.less";
 @import "less/buttons.less";
@@ -478,8 +477,6 @@ a.new-member {
     brightness(100% - hsvvalue(@base-color) + hsvvalue(#28497c));
 }
 
-#navigation-level-1-items li a img,
-#navigation-level-1-items li a canvas,
 #sidebar .sidebar-image > img {
     .recolor;
 }
diff --git a/resources/assets/stylesheets/studip.scss b/resources/assets/stylesheets/studip.scss
index 1b0d2e0cb34..0cf922df9bc 100644
--- a/resources/assets/stylesheets/studip.scss
+++ b/resources/assets/stylesheets/studip.scss
@@ -77,6 +77,7 @@
 @import "scss/quicksearch";
 @import "scss/raumzeit";
 @import "scss/report";
+@import "scss/responsive";
 @import "scss/resources";
 @import "scss/sidebar";
 @import "scss/select";
diff --git a/resources/vue/components/BlubberThread.vue b/resources/vue/components/BlubberThread.vue
index 3dbe8dd9818..dab56cfea65 100644
--- a/resources/vue/components/BlubberThread.vue
+++ b/resources/vue/components/BlubberThread.vue
@@ -3,7 +3,7 @@
          :id="'blubberthread_' + threadData.thread_posting.thread_id"
          @dragover.prevent="dragover" @dragleave.prevent="dragleave"
          @drop.prevent="upload">
-        <div class="responsive-visible context_info" v-if="threadData.notifications">
+        <div class="hidden-medium-up context_info" v-if="threadData.notifications">
             <a href="#"
                @click.prevent="toggleFollow()"
                class="followunfollow"
diff --git a/resources/vue/components/courseware/CoursewareRibbon.vue b/resources/vue/components/courseware/CoursewareRibbon.vue
index 8c200141b31..119448c7f06 100644
--- a/resources/vue/components/courseware/CoursewareRibbon.vue
+++ b/resources/vue/components/courseware/CoursewareRibbon.vue
@@ -1,5 +1,5 @@
 <template>
-    <div :class="{ 'cw-ribbon-wrapper-consume': consumeMode }">
+    <div :class="{ 'cw-ribbon-wrapper-consume': consumeMode }" :id="isContentBar ? 'contentbar' : null" >
         <div v-if="stickyRibbon" class="cw-ribbon-sticky-top"></div>
         <header class="cw-ribbon" :class="{ 'cw-ribbon-sticky': stickyRibbon, 'cw-ribbon-consume': consumeMode }">
             <div class="cw-ribbon-wrapper-left">
@@ -67,6 +67,10 @@ export default {
             type: Boolean
         },
         buttonsClass: String,
+        isContentBar: {
+            type: Boolean,
+            default: false
+        }
     },
     data() {
         return {
@@ -101,6 +105,7 @@ export default {
     },
     methods: {
         toggleConsumeMode() {
+            STUDIP.Vue.emit('toggle-focus-mode', !this.consumeMode);
             if (!this.consumeMode) {
                 this.$store.dispatch('coursewareConsumeMode', true);
                 this.$store.dispatch('coursewareSelectedToolbarItem', 'contents');
@@ -125,6 +130,9 @@ export default {
     },
     mounted() {
         window.addEventListener('scroll', this.handleScroll);
+        if (this.isContentBar) {
+            STUDIP.Vue.emit('courseware-contentbar-mounted', this);
+        }
     },
     watch: {
         toolsActive(newState, oldState) {
diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue
index 8c12b889762..d029e65e5d3 100644
--- a/resources/vue/components/courseware/CoursewareStructuralElement.vue
+++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue
@@ -7,7 +7,7 @@
                 v-if="validContext"
             >
                 <div class="cw-structural-element-content" v-if="structuralElement">
-                    <courseware-ribbon :canEdit="canEdit && canAddElements">
+                    <courseware-ribbon :canEdit="canEdit && canAddElements" :isContentBar="true">
                         <template #buttons>
                             <router-link v-if="prevElement" :to="'/structural_element/' + prevElement.id">
                                 <div class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" />
diff --git a/resources/vue/components/responsive/NavigationItem.vue b/resources/vue/components/responsive/NavigationItem.vue
new file mode 100644
index 00000000000..0bfd6d2bd23
--- /dev/null
+++ b/resources/vue/components/responsive/NavigationItem.vue
@@ -0,0 +1,83 @@
+<template>
+    <li v-if="item.visible" class="navigation-item" :class="{'navigation-item-active': active}">
+        <template v-if="hasChildren()">
+            <div class="navigation-title">
+                <a :href="item.url" :title="navigateToText(item.title)" tabindex="0">
+                    <span class="navigation-icon">
+                        <studip-icon v-if="isCourse" shape="seminar" role="info_alt" size="24" alt=""></studip-icon>
+                        <img v-if="item.icon" :src="iconUrl" width="24" alt="">
+                    </span>
+                    <span class="navigation-text">
+                        {{ item.title }}
+                    </span>
+                </a>
+            </div>
+            <button class="styleless navigation-in" tabindex="0"
+                    :title="openNavigationText(item.title)"
+                    @click="moveTo(item.path)" @keydown.prevent.enter="moveTo(item.path)" @keydown.prevent.space="moveTo(item.path)">
+                <studip-icon shape="arr_1right" role="info_alt" size="20" alt=""></studip-icon>
+            </button>
+        </template>
+        <div v-else class="navigation-title">
+            <a :href="item.url" tabindex="0" :title="navigateToText(item.title)">
+                <studip-icon v-if="isCourse" shape="seminar" role="info_alt" size="24" alt=""></studip-icon>
+                <img v-if="item.icon" :src="iconUrl" width="24" alt="">
+                {{ item.title }}
+            </a>
+        </div>
+    </li>
+</template>
+
+<script>
+import StudipIcon from '../StudipIcon.vue';
+
+export default {
+    name: 'NavigationItem',
+    components: { StudipIcon },
+    props: {
+        item: {
+            type: Object,
+            required: true
+        },
+        active: {
+            type: Boolean,
+            default: false
+        },
+        isCourse: {
+            type: Boolean,
+            default: false
+        }
+    },
+    computed: {
+        iconUrl() {
+            if (this.item.icon && !this.item.icon.match(/^https?:\/\//)) {
+                return STUDIP.ASSETS_URL + this.item.icon;
+            }
+            return this.item.icon;
+        }
+    },
+    methods: {
+        getUrl(url) {
+            return STUDIP.URLHelper.getURL(url);
+        },
+        moveTo(path) {
+            STUDIP.Vue.emit('responsive-navigation-move-to', path);
+        },
+        hasChildren() {
+            return this.item.children && Object.keys(this.item.children).length > 0;
+        },
+        navigateToText(itemTitle) {
+            return this.$gettextInterpolate(
+                this.$gettext('Navigiere zu %{ title }'),
+                { title: itemTitle }
+            );
+        },
+        openNavigationText(itemTitle) {
+            return this.$gettextInterpolate(
+                this.$gettext('Öffne Navigation %{ title }'),
+                { title: itemTitle }
+            );
+        }
+    }
+}
+</script>
diff --git a/resources/vue/components/responsive/ResponsiveContentBar.vue b/resources/vue/components/responsive/ResponsiveContentBar.vue
new file mode 100644
index 00000000000..9e499b3fbf1
--- /dev/null
+++ b/resources/vue/components/responsive/ResponsiveContentBar.vue
@@ -0,0 +1,206 @@
+<template>
+    <div v-if="realContentbarSource === ''">
+        <MountingPortal mount-to="#responsive-contentbar-container" append>
+            <portal-target name="layout-page"></portal-target>
+        </MountingPortal>
+        <portal to="layout-page">
+            <div id="responsive-contentbar" class="contentbar" ref="contentbar">
+                <div v-if="hasSidebar" class="contentbar-nav" ref="leftNav">
+                    <button :class="sidebarIconClasses" @click.prevent="toggleSidebar" id="toggle-sidebar"
+                            :title="$gettext('Sidebar öffnen')">
+                        <studip-icon shape="sidebar3" size="24" ref="sidebarIcon"
+                                     alt=""></studip-icon>
+                    </button>
+                </div>
+                <div class="contentbar-wrapper-left">
+                    <studip-icon :shape="icon" size="24" role="info" class="text-bottom contentbar-icon"></studip-icon>
+                    <nav class="contentbar-breadcrumb" ref="breadcrumbs">{{ title }}</nav>
+                </div>
+                <div class="contentbar-wrapper-right" ref="wrapperRight"></div>
+            </div>
+        </portal>
+    </div>
+</template>
+
+<script>
+import StudipIcon from '../StudipIcon.vue';
+
+export default {
+    name: 'ResponsiveContentBar',
+    components: { StudipIcon },
+    props: {
+        icon: {
+            type: String,
+            default: 'seminar'
+        },
+        title: {
+            type: String,
+            default: ''
+        },
+        hasSidebar: {
+            type: Boolean,
+            default: true
+        }
+    },
+    data() {
+        return {
+            realContentbar: null,
+            realContentbarSource: null,
+            realContentbarIconContainer: null,
+            realContentbarType: null,
+            sidebarOpen: false
+        }
+    },
+    computed: {
+        sidebarIconClasses() {
+            let classes = ['styleless', 'contentbar-button', 'contentbar-button-sidebar'];
+            if (this.sidebarOpen) {
+                classes.push('contentbar-button-sidebar-open');
+            }
+            return classes;
+        }
+    },
+    methods: {
+        toggleSidebar() {
+
+            const sidebar = document.getElementById('sidebar');
+            const content = document.getElementById('content-wrapper');
+            const pageTitle = document.getElementById('page-title-container');
+            const html = document.querySelector('html');
+            if (this.sidebarOpen) {
+                sidebar.classList.add('responsive-hide');
+                sidebar.classList.remove('responsive-show');
+
+                if (html.classList.contains('responsive-display') && !html.classList.contains('fullscreen-mode')) {
+                    content.style.display = null;
+                    pageTitle.style.display = null;
+                }
+
+                this.sidebarOpen = false;
+            } else {
+                sidebar.classList.add('responsive-show');
+                sidebar.classList.remove('responsive-hide');
+                if (html.classList.contains('responsive-display') && !html.classList.contains('fullscreen-mode')) {
+                    content.style.display = 'none';
+                    pageTitle.style.display = 'none';
+                }
+                this.sidebarOpen = true;
+            }
+
+            // Adjust toggle sidebar button title
+            document.getElementById('toggle-sidebar').title = this.sidebarOpen
+                ? this.$gettext('Sidebar schließen')
+                : this.$gettext('Sidebar öffnen');
+        },
+        adjustExistingContentbar(responsiveMode) {
+            if (this.realContentbar) {
+                if (responsiveMode) {
+                    this.realContentbar.id = 'responsive-contentbar';
+                    this.realContentbar.classList.add('contentbar');
+                    if (!this.realContentbar.querySelector('#toggle-sidebar-button')) {
+                        this.realContentbar.querySelector(this.realContentbarIconContainer)
+                            .prepend(this.createSidebarIcon());
+                    }
+                    if (this.realContentbarType === 'courseware') {
+                        this.realContentbar.querySelector('.cw-ribbon-wrapper-left')
+                            .classList.add('contentbar-wrapper-left');
+                        this.realContentbar.querySelector('.cw-ribbon-wrapper-right')
+                            .classList.add('contentbar-wrapper-right');
+                    }
+
+                    document.getElementById('responsive-contentbar-container').prepend(this.realContentbar);
+                } else {
+                    this.realContentbar.id = 'contentbar';
+                    document.getElementById('toggle-sidebar-button').remove();
+
+                    if (this.realContentbarType === 'courseware') {
+                        this.realContentbar.classList.remove('contentbar');
+                        this.realContentbar.querySelector('.cw-ribbon-wrapper-left')
+                            .classList.remove('contentbar-wrapper-left');
+                        this.realContentbar.querySelector('.cw-ribbon-wrapper-right')
+                            .classList.remove('contentbar-wrapper-right');
+                    }
+
+                    document.querySelector(this.realContentbarSource).prepend(this.realContentbar);
+                }
+            }
+        },
+        createSidebarIcon() {
+            const button = document.createElement('button');
+
+            this.sidebarIconClasses.map(className => {
+                button.classList.add(className);
+            });
+            button.id = 'toggle-sidebar-button';
+            button.title = this.$gettext('Sidebar einblenden');
+            button.addEventListener('click', (event) => {
+                button.classList.toggle('contentbar-button-sidebar-open');
+                event.preventDefault();
+                this.toggleSidebar();
+            })
+            const sidebarIcon = document.createElement('img');
+            sidebarIcon.src = STUDIP.ASSETS_URL + '/images/icons/blue/sidebar3.svg';
+            sidebarIcon.height = 24;
+            sidebarIcon.width = 24;
+            button.appendChild(sidebarIcon);
+
+            return button;
+        }
+    },
+    mounted() {
+        // There's already a PHP contentbar on this page, use it.
+        this.$nextTick(() => {
+            const realContentbar = document.querySelector('.contentbar:not(#responsive-contentbar)');
+            if (realContentbar) {
+                this.realContentbar = realContentbar;
+                this.realContentbarSource = '#layout_content';
+                this.realContentbarIconContainer = '.contentbar-nav';
+                this.realContentbarType = 'wiki';
+                this.adjustExistingContentbar(true);
+            } else {
+                this.realContentbarSource = '';
+
+                const cwContentbar = document.querySelector('#contentbar header');
+                if (cwContentbar) {
+                    this.realContentbar = cwContentbar;
+                    this.realContentbarSource = '.cw-structural-element-content > div';
+                    this.realContentbarIconContainer = '.cw-ribbon-nav';
+                    this.realContentbarType = 'courseware';
+                    this.adjustExistingContentbar(true);
+                }
+            }
+
+            // Add click listener to sidebar so that it can be hidden on clicking an item
+            document.querySelectorAll('.sidebar-widget a').forEach(item => {
+                item.addEventListener('click', () => this.toggleSidebar());
+            });
+        })
+
+        // Use courseware contentbar instead of this Vue component.
+        STUDIP.Vue.on('courseware-contentbar-mounted', element => {
+            this.realContentbar = element.$el.querySelector('header');
+            this.realContentbarSource = '.cw-structural-element-content > div';
+            this.realContentbarIconContainer = '.cw-ribbon-nav';
+            this.realContentbarType = 'courseware';
+            this.adjustExistingContentbar(true);
+
+            document.querySelectorAll('.sidebar-widget button span').forEach(item => {
+                item.addEventListener('click', () => this.toggleSidebar());
+            });
+        })
+
+        STUDIP.Vue.on('toggle-focus-mode', (state) => {
+            const html = document.querySelector('html');
+            if (html.classList.contains('responsive-display') || html.classList.contains('fullscreen-mode')) {
+                this.adjustExistingContentbar(!state);
+            }
+        })
+
+    },
+    beforeDestroy() {
+        if (this.realContentbar) {
+            this.adjustExistingContentbar(false);
+        }
+    }
+}
+</script>
diff --git a/resources/vue/components/responsive/ResponsiveNavigation.vue b/resources/vue/components/responsive/ResponsiveNavigation.vue
new file mode 100644
index 00000000000..d71152027c7
--- /dev/null
+++ b/resources/vue/components/responsive/ResponsiveNavigation.vue
@@ -0,0 +1,525 @@
+<template>
+    <div role="navigation">
+        <div class="responsive-navigation-header">
+            <button v-if="menuNeeded"
+                    id="responsive-navigation-button" class="styleless"
+                    :title="showMenu ? $gettext('Navigation schließen') : $gettext('Navigation öffnen')"
+                    aria-owns="responsive-navigation-items"
+                    @click.prevent="toggleMenu"
+                    @keydown.prevent.space="toggleMenu"
+                    @keydown.prevent.enter="toggleMenu">
+                <studip-icon shape="hamburger" role="info_alt"
+                             :alt="showMenu ? $gettext('Navigation schließen') : $gettext('Navigation öffnen')"
+                             :size="iconSize" :class="showMenu ? 'menu-open' : 'menu-closed'">
+                </studip-icon>
+            </button>
+            <toggle-fullscreen v-if="!isResponsive && !isFocusMode && me.username != 'nobody'"
+                               :is-fullscreen="isFullscreen"></toggle-fullscreen>
+        </div>
+        <transition name="appear" appear>
+            <nav v-if="showMenu" id="responsive-navigation-items" class="responsive" ref="navigation"
+                 :aria-expanded="showMenu">
+                <header v-if="me.username !== 'nobody'">
+                    <template v-if="!avatarMenuOpen">
+                        <section class="profile-info">
+                            <div class="profile-pic">
+                                <img :src="me.avatar"
+                                     @click.prevent="toggleAvatarMenu"
+                                     :title="$gettext('Profilnavigation öffnen')">
+                            </div>
+                            <div class="profile-data">
+                                <div>{{ me.fullname }}</div>
+                                <div>{{ me.email }} ({{ me.perm }})</div>
+                            </div>
+                        </section>
+                        <section class="open-avatarmenu">
+                            <button class="styleless" tabindex="0" ref="openAvatarmenu"
+                                    @keydown.prevent.enter="toggleAvatarMenu"
+                                    @keydown.prevent.space="toggleAvatarMenu"
+                                    @click.prevent="toggleAvatarMenu"
+                                    :title="$gettext('Profilnavigation öffnen')">
+                                <studip-icon shape="arr_1right" role="info_alt" :size="iconSize" alt=""></studip-icon>
+                            </button>
+                        </section>
+                    </template>
+                    <template v-else>
+                        <focus-trap active="true" :return-focus-on-deactivate="true"
+                                    :click-outside-deactivates="true">
+                            <div>
+                                <div class="close-avatarmenu">
+                                    <button class="styleless" ref="closeAvatarmenu"  tabindex="0"
+                                            @keydown.prevent.enter="toggleAvatarMenu"
+                                            @keydown.prevent.space="toggleAvatarMenu"
+                                            @click="toggleAvatarMenu">
+                                        <studip-icon shape="arr_1left" role="info_alt" :size="iconSize"></studip-icon>
+                                    </button>
+                                </div>
+                                <ul class="avatar-navigation">
+                                    <navigation-item v-for="(item, index) in avatarNavigation.children" :key="index"
+                                                     :item="item"></navigation-item>
+                                </ul>
+                            </div>
+                        </focus-trap>
+                    </template>
+                </header>
+                <ul class="main-navigation">
+                    <li v-if="currentParent != null" class="navigation-item navigation-up">
+                        <div class="navigation-title" :title="$gettext('Zum Start')" @click="moveTo('/')">
+                            <button class="styleless" @click="moveTo('/')" @keydown.prevent.enter="moveTo('/')"
+                                    @keydown.prevent.space="moveTo('/')" tabindex="0">
+                                <div class="navigation-icon">
+                                    <studip-icon shape="arr_2up" role="info_alt" :size="iconSize - 4"></studip-icon>
+                                </div>
+                                <div class="navigation-text">
+                                    {{ $gettext('Start') }}
+                                </div>
+                            </button>
+                        </div>
+                    </li>
+                    <li v-if="currentParent != null" class="navigation-item navigation-current">
+                        <div class="navigation-title">
+                            <button class="styleless" tabindex="0"
+                                    @click="moveTo(currentParent.path)"
+                                    @keydown.prevent.enter="moveTo(currentParent.path)"
+                                    @keydown.prevent.space="moveTo(currentParent.path)"
+                                    :title="$gettext('Eine Ebene höher')">
+                                <div class="navigation-icon">
+                                    <studip-icon shape="arr_1left" role="info_alt" :size="iconSize - 4"></studip-icon>
+                                </div>
+                                <div class="navigation-text">
+                                    {{ $gettext('Eine Ebene höher') }}
+                                </div>
+                            </button>
+                        </div>
+                    </li>
+                    <navigation-item v-for="(item, index) in currentNavigation.children" :key="index"
+                                     :item="item"></navigation-item>
+                </ul>
+            </nav>
+        </transition>
+        <responsive-content-bar v-if="(isResponsive || isFullscreen) && !isFocusMode"
+                                :has-sidebar="hasSidebar" :title="initialTitle"
+                                ref="contentbar"></responsive-content-bar>
+        <responsive-skip-links v-if="isFullscreen && hasSkiplinks" :links="skipLinks"></responsive-skip-links>
+    </div>
+</template>
+
+<script>
+import NavigationItem from './NavigationItem.vue';
+import StudipIcon from '../StudipIcon.vue';
+import ResponsiveContentBar from './ResponsiveContentBar.vue';
+import ToggleFullscreen from './ToggleFullscreen.vue';
+import ResponsiveSkipLinks from './ResponsiveSkipLinks.vue';
+import { FocusTrap } from 'focus-trap-vue';
+
+export default {
+    name: 'ResponsiveNavigation',
+    components: { ResponsiveContentBar, StudipIcon, NavigationItem, ToggleFullscreen, ResponsiveSkipLinks, FocusTrap },
+    props: {
+        me: {
+            type: Object,
+            required: true
+        },
+        context: {
+            type: String,
+            default: ''
+        },
+        hasSidebar: {
+            type: Boolean,
+            default: true
+        }
+    },
+    data() {
+        return {
+            isResponsive: false,
+            isFullscreen: false,
+            isFocusMode: false,
+            headerMagic: false,
+            iconSize: 28,
+            showMenu: false,
+            activeItem: STUDIP.Navigation.activated.at(-1) ?? 'start',
+            currentNavigation: this.findItem(STUDIP.Navigation.activated.at(0) ?? 'start'),
+            initialNavigation: {},
+            initialTitle: '',
+            isAdmin: ['root','admin'].includes(this.me.perm),
+            courses: [],
+            avatarNavigation: STUDIP.Navigation.navigation.avatar,
+            avatarMenuOpen: false,
+            observer: null,
+            hasSkiplinks: document.querySelector('#skiplink_list') !== null
+        }
+    },
+    computed: {
+        // Current navigation title, supplemented by context title if available
+        currentTitle() {
+            return this.context != '' && this.currentNavigation.path.indexOf('my_courses/') != -1 ?
+                this.context : '';
+        },
+        // The parent element of the current navigation item
+        currentParent() {
+            return this.currentNavigation.parent != null
+                 ? this.findItem(this.currentNavigation.parent)
+                 : null;
+        },
+        /*
+         * The parent element of the current navigation item parent
+         * which is used to provide a link up
+         */
+        currentGrandparent() {
+            return this.currentParent != null && this.currentParent.parent != null
+                 ? this.findItem(this.currentParent.parent)
+                 : null;
+        },
+        /*
+         * Is the responsive navigation menu needed (because we are in responsive or fullscreen mode)?
+         */
+        menuNeeded() {
+            return !this.isFocusMode
+                && (this.isResponsive || this.isFullscreen || this.headerMagic);
+        },
+
+        skipLinks() {
+            let links = [
+                { url: '#responsive-navigation-button', label: this.$gettext('Hauptnavigation') }
+            ];
+
+            if (this.isFullscreen) {
+                links.push(
+                    { url: '#toggle-fullscreen', label: this.$gettext('Vollbildmodus verlassen') },
+                );
+
+                if (this.hasSidebar) {
+                    let name = '';
+                    if (document.getElementById('sidebar').classList.contains('responsive-show')) {
+                        name = this.$gettext('Sidebar ausblenden');
+                    } else {
+                        name = this.$gettext('Sidebar anzeigen');
+                    }
+                    links.push({ url: '#toggle-sidebar', label: name });
+                }
+            }
+
+            return links;
+        }
+    },
+    methods: {
+        /**
+         * Find a navigation item specified by given path in the given navigation structure
+         * @param path
+         * @param navigation
+         * @returns {{parent: null, path: string, visible: boolean, children, icon: null, active: boolean, title, url}|null}
+         */
+        findItem(path, navigation) {
+            // No navigation given, use full Stud.IP navigation hierarchy.
+            if (!navigation) {
+
+                const nav = STUDIP.Navigation.navigation;
+
+                // Some "pseudo" navigation directly at root level.
+                if (path === '/' || path === 'start') {
+                    return {
+                        active: true,
+                        children: nav,
+                        icon: null,
+                        parent: null,
+                        path: '/',
+                        title: nav.start.title,
+                        url: nav.start.url,
+                        visible: true
+                    };
+                    // Direct hit in sub navigation items.
+                } else if (nav[path]) {
+                    return nav[path];
+                    // Recurse through sub navigation items.
+                } else {
+
+                    let found = null;
+                    for (const sub in nav) {
+                        found = this.findItem(path, nav[sub]);
+                        if (found) {
+                            break;
+                        }
+                    }
+                    return found;
+
+                }
+
+            } else {
+
+                // Found requested item at current level.
+                if (navigation[path]) {
+                    return navigation[path];
+                } else {
+
+                    if (navigation.children) {
+                        // Found requested item as child of current one.
+                        if (navigation.children[path]) {
+                            return navigation.children[path];
+                        // Recurse deeper.
+                        } else {
+                            let found = null;
+                            for (const sub in navigation.children) {
+                                found = this.findItem(path, navigation.children[sub]);
+                                if (found) {
+                                    break;
+                                }
+                            }
+                            return found;
+
+                        }
+                    // No children left to search through, we are doomed.
+                    } else {
+                        return null;
+                    }
+
+                }
+            }
+
+        },
+        /**
+         * Open or close the navigation menu
+         * @param event
+         */
+        toggleMenu() {
+
+            this.showMenu = !this.showMenu;
+
+            this.$nextTick(() => {
+                if (this.showMenu && !this.headerMagic) {
+                    this.currentNavigation = this.initialNavigation;
+
+                    if (this.isResponsive) {
+                        this.$refs.navigation.style.height = (document.documentElement.clientHeight - 42) + 'px';
+                    }
+                    document.getElementById('header-links').style.display = 'none';
+                } else {
+                    document.getElementById('header-links').style.display = null;
+                }
+            })
+        },
+        /**
+         * Turn fullscreen mode on or off
+         * @param event
+         */
+        setFullscreen(state) {
+            const html = document.querySelector('html');
+            const sidebar = document.getElementById('sidebar');
+            const cache = STUDIP.Cache.getInstance('responsive.');
+
+            if (state) {
+                html.classList.add('fullscreen-mode');
+                cache.set('fullscreen-mode', true);
+            } else {
+                html.classList.remove('fullscreen-mode');
+                sidebar.classList.remove('responsive-show', 'fullscreen-mode');
+                this.showMenu = false;
+                cache.remove('fullscreen-mode');
+            }
+
+            this.isFullscreen = state;
+
+            if (!this.isResponsive) {
+                this.moveHelpbar();
+            }
+        },
+        getUrl(url) {
+            return STUDIP.URLHelper.getURL(url, {}, true);
+        },
+        /**
+         * Move to another item in navigation structure, specified by path
+         * @param string path
+         */
+        moveTo(path) {
+            this.avatarMenuOpen = false;
+            this.currentNavigation = this.findItem(path ? path : '/');
+            this.$nextTick(() => {
+                const current = document.querySelector('.navigation-current') ?? document.querySelector('.navigation-item');
+                if (current) {
+                    current.focus();
+                }
+            })
+        },
+        /**
+         * Relocate the helpbar icon to another DOM location
+         * as it is part of top bar in responsive view.
+         */
+        moveHelpbar() {
+            let tag = 'div';
+            let target = '.tabs_wrapper';
+            if (this.isFullscreen || this.isResponsive) {
+                tag = 'li';
+                target = '#header-links ul';
+            }
+
+            let helpBar = document.createElement(tag);
+            const realHelpBar = document.querySelector('.helpbar-container');
+
+            const helpbarIcon = document.querySelector('#helpbar_icon');
+
+            if (helpbarIcon) {
+                const realIcon = helpbarIcon.querySelector('img.icon-shape-question-circle');
+                realIcon.src = (this.isFullscreen || this.isResponsive)
+                             ? realIcon.src.replace('blue', 'white')
+                             : realIcon.src.replace('white', 'blue');
+
+                helpBar.appendChild(helpbarIcon);
+                helpBar.appendChild(document.querySelector('div.helpbar'));
+                helpBar.classList.add('helpbar-container');
+                document.querySelector(target).appendChild(helpBar);
+                realHelpBar.remove();
+            }
+        },
+        /**
+         * Show or hide avatar navigation menu.
+         */
+        toggleAvatarMenu() {
+            this.avatarMenuOpen = !this.avatarMenuOpen;
+        },
+        onChangeViewMode(tagName, classes) {
+            const classList = classes.split(' ');
+
+            switch (tagName) {
+                // watch for "consuming_mode" or "fixed" class changes
+                case 'BODY':
+                    if (classList.includes('consuming_mode')) {
+                        this.isFocusMode = true;
+                        STUDIP.Vue.emit('consuming-mode-enabled');
+                    } else {
+                        this.isFocusMode = false;
+                        STUDIP.Vue.emit('consuming-mode-disabled');
+                    }
+                    if (classList.includes('fixed')) {
+                        this.headerMagic = true;
+                        STUDIP.Vue.emit('header-magic-enabled');
+                    } else {
+                        this.headerMagic = false;
+                        STUDIP.Vue.emit('header-magic-disabled');
+                    }
+                    break;
+                // Watch for "responsive-display" and "fullscreen-mode" class changes
+                case 'HTML':
+                    if (classList.includes('responsive-display')) {
+                        this.isResponsive = true;
+
+                        if (classList.includes('fullscreen-mode')) {
+                            this.setFullscreen(false);
+                        }
+
+                        STUDIP.Vue.emit('responsive-display-enabled');
+                        this.$nextTick(() => {
+                            this.moveHelpbar();
+                        })
+                    } else {
+                        this.isResponsive = false;
+                        STUDIP.Vue.emit('responsive-display-disabled');
+                        this.$nextTick(() => {
+                            this.moveHelpbar();
+                        })
+                    }
+
+                    if (classList.includes('fullscreen-mode')) {
+                        this.isFullscreen = true;
+
+                        STUDIP.Vue.emit('fullscreen-enabled');
+                    } else {
+                        this.isFullscreen = false;
+                        STUDIP.Vue.emit('fullscreen-disabled');
+                    }
+                    break;
+                case 'HEADER':
+                    if (classList.includes('cw-ribbon-consume')) {
+                        this.isFocusMode = true;
+                    } else {
+                        this.isFocusMode = false;
+                    }
+            }
+        }
+    },
+    mounted() {
+        const cache = STUDIP.Cache.getInstance('responsive.');
+        const html = document.querySelector('html');
+        const body = document.querySelector('body');
+        const fullscreen = cache.get('fullscreen-mode') ?? false;
+        const fullscreenDocument = html.classList.contains('fullscreen-mode');
+
+        this.isFullscreen = fullscreenDocument || fullscreen;
+        if (this.isFullscreen && !fullscreenDocument) {
+            html.classList.add('fullscreen-mode');
+        }
+
+        if (html.classList.contains('responsive-display')) {
+            this.isResponsive = true;
+        }
+
+        // Re-structure some DOM elements
+        this.$nextTick(() => {
+            if (this.isResponsive || (this.isFullscreen && !this.isFocusMode)) {
+                this.moveHelpbar();
+            }
+        })
+
+        this.initialNavigation = this.currentNavigation;
+        this.initialTitle = this.initialNavigation.title;
+
+        STUDIP.Vue.on('responsive-navigation-move-to', path => {
+            this.moveTo(path);
+        })
+
+        // Listen to changes in fullscreen setting
+        STUDIP.Vue.on('toggle-fullscreen', value => {
+            this.setFullscreen(value);
+        })
+
+        /*
+         * Use an observer for html and body in order to check
+         * whether we move into consuming mode or leave responsive mode.
+         */
+        this.observer = new MutationObserver(mutations => {
+            for (const m of mutations) {
+                const newValue = m.target.getAttribute(m.attributeName);
+                this.onChangeViewMode(m.target.tagName, newValue);
+            }
+        })
+
+        // Observe <html> for class changes.
+        this.observer.observe(html, {
+            attributes: true,
+            attributeOldValue : false,
+            attributeFilter: ['class']
+        })
+
+        // Observe <body> for class changes.
+        this.observer.observe(body, {
+            attributes: true,
+            attributeOldValue : false,
+            attributeFilter: ['class']
+        })
+
+        // Observe courseware contentbar for consuming mode.
+        STUDIP.Vue.on('courseware-ribbon-mounted', element => {
+            this.observer.observe(element.$el.querySelector('header.cw-ribbon'), {
+                attributes: true,
+                attributeOldValue : false,
+                attributeFilter: ['class']
+            })
+        })
+
+    },
+    beforeDestroy() {
+        this.observer.disconnect();
+        document.getElementById('header-links').style.display = null;
+    }
+}
+</script>
+
+<style lang="scss">
+.appear-enter-active,
+.appear-leave-active {
+    transition: opacity .3s ease;
+}
+
+.appear-enter,
+.appear-leave-to {
+   opacity: 0;
+}
+</style>
diff --git a/resources/vue/components/responsive/ResponsiveSkipLinks.vue b/resources/vue/components/responsive/ResponsiveSkipLinks.vue
new file mode 100644
index 00000000000..52f0bfcfdbb
--- /dev/null
+++ b/resources/vue/components/responsive/ResponsiveSkipLinks.vue
@@ -0,0 +1,49 @@
+<template>
+    <div>
+        <MountingPortal mount-to="#skiplink_list" append>
+            <portal-target name="additional-skiplinks"></portal-target>
+        </MountingPortal>
+        <portal to="additional-skiplinks">
+            <li v-for="(link) in links" :key="link.url">
+                <button class="skiplink" role="link" @click.prevent="goto(link.url)">
+                    {{ link.label }}
+                </button>
+            </li>
+        </portal>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'ResponsiveSkipLinks',
+    props: {
+        links: {
+            type: Array,
+            default: () => []
+        }
+    },
+    methods: {
+        goto(url) {
+            window.location = url;
+        }
+    },
+    created() {
+        const allButtons = document.querySelectorAll('button.skiplink');
+        const buttons = document.querySelectorAll('button.skiplink:not([data-in-fullscreen="1"])');
+        buttons.forEach(button => {
+            button.style.display = 'none';
+        });
+        this.$nextTick(() => {
+            allButtons.forEach(button => {
+                document.getElementById('skiplink_list').appendChild(button.parentNode);
+            });
+        })
+    },
+    beforeDestroy() {
+        const buttons = document.querySelectorAll('button.skiplink:not([data-in-fullscreen="1"])');
+        buttons.forEach(button => {
+            button.style.display = null;
+        });
+    }
+}
+</script>
diff --git a/resources/vue/components/responsive/ToggleFullscreen.vue b/resources/vue/components/responsive/ToggleFullscreen.vue
new file mode 100644
index 00000000000..77ce8fbdbc6
--- /dev/null
+++ b/resources/vue/components/responsive/ToggleFullscreen.vue
@@ -0,0 +1,51 @@
+<template>
+    <div>
+        <MountingPortal mount-to="#responsive-toggle-fullscreen" append>
+            <portal-target name="toggle-fullscreen-off"></portal-target>
+        </MountingPortal>
+        <MountingPortal mount-to="#non-responsive-toggle-fullscreen" append>
+            <portal-target name="toggle-fullscreen-on"></portal-target>
+        </MountingPortal>
+        <portal :to="isFullscreen ? 'toggle-fullscreen-off' : 'toggle-fullscreen-on'">
+            <button class="styleless" id="toggle-fullscreen"
+                    :title="isFullscreen ? $gettext('Vollbildmodus verlassen') : $gettext('Vollbildmodus aktivieren')"
+                    @click.prevent="toggleFullscreen"
+                    @keydown.prevent.enter="toggleFullscreen"
+                    @keydown.prevent.space="toggleFullscreen">
+                <studip-icon :shape="isFullscreen ? 'fullscreen-off' : 'fullscreen-on4'"
+                             :role="isFullscreen ? 'info_alt' : 'clickable'" :size="iconSize" alt=""></studip-icon>
+            </button>
+        </portal>
+    </div>
+</template>
+
+<script>
+import StudipIcon from '../StudipIcon.vue';
+
+export default {
+    name: 'ToggleFullscreen',
+    components: { StudipIcon },
+    props: {
+        isFullscreen: {
+            type: Boolean,
+            default: false
+        },
+        iconSize: {
+            type: Number,
+            default: 24
+        }
+    },
+    methods: {
+        toggleFullscreen() {
+            STUDIP.Vue.emit('toggle-fullscreen', !this.isFullscreen);
+        }
+    },
+    created() {
+        window.addEventListener('keydown', e => {
+            if (e.key === 'Escape' && this.isFullscreen) {
+                this.toggleFullscreen();
+            }
+        });
+    }
+}
+</script>
diff --git a/resources/vue/mixins/MyCoursesMixin.js b/resources/vue/mixins/MyCoursesMixin.js
index 81d85ab5e7b..203340b5ebb 100644
--- a/resources/vue/mixins/MyCoursesMixin.js
+++ b/resources/vue/mixins/MyCoursesMixin.js
@@ -164,9 +164,9 @@ export default {
     },
 
     created () {
-        this.responsiveDisplay = Responsive.media_query.matches;
+        this.responsiveDisplay = Responsive.isResponsive();
         Responsive.media_query.addListener(() => {
-            this.responsiveDisplay = Responsive.media_query.matches;
+            this.responsiveDisplay = Responsive.isResponsive();
         })
     }
 }
diff --git a/templates/contentbar/contentbar.php b/templates/contentbar/contentbar.php
index 4433aabdc5b..851c77e3988 100644
--- a/templates/contentbar/contentbar.php
+++ b/templates/contentbar/contentbar.php
@@ -1,36 +1,37 @@
-<div class="contentbar">
-    <div class="contentbar_title">
-        <? if (!$toc->isActive()) : ?>
-        <a href="<?= $toc->getUrl() ?>" title="<?= htmlReady($toc->getTitle()) ?>">
-        <? endif ?>
-            <?= $icon->asImg(24, ['class' => 'text-bottom']) ?>
-        <? if (!$toc->isActive()) : ?>
-            </a>
-        <? endif ?>
-        <ul class="breadcrumb"><?= $breadcrumbs->render() ?></ul>
-    </div>
-
-    <div class="contentbar_info">
-        <div class="textblock"><?= $info ?></div>
-
-        <div class="contentbar-icons">
+<header>
+    <header class="contentbar">
+        <nav class="contentbar-nav"></nav>
+        <div class="contentbar-wrapper-left">
+            <nav class="contentbar-breadcrumb">
+                <? if (!$toc->isActive()) : ?>
+                <a href="<?= $toc->getUrl() ?>" title="<?= htmlReady($toc->getTitle()) ?>" class="contentbar-icon">
+                    <? endif ?>
+                    <?= $icon->asImg(24, ['class' => 'text-bottom']) ?>
+                    <? if (!$toc->isActive()) : ?>
+                </a>
+            <? endif ?>
+                <?= $breadcrumbs->render() ?>
+            </nav>
+        </div>
+        <div class="contentbar-wrapper-right">
             <? if ($toc->hasChildren()) : ?>
-                <input type="checkbox" id="cb-toc">
-                <label for="cb-toc" class="check-box enter-accessible" title="<?= _('Inhaltsverzeichnis') ?>" tabindex="0">
-                    <?= Icon::create('table-of-contents')->asImg(24) ?>
-                </label>
-                <?= $ttpl->render() ?>
+                <div class="contentbar-button-wrapper contentbar-toc-wrapper">
+                    <input type="checkbox" id="cb-toc">
+                    <label for="cb-toc" class="contentbar-button contentbar-button-menu check-box enter-accessible" title="<?= _('Inhaltsverzeichnis') ?>" tabindex="0">
+                    </label>
+                    <?= $ttpl->render() ?>
+                </div>
             <? endif ?>
 
-            <a class="consuming_mode_trigger"
-               href="#"
-               title="<?= _("Konsummodus ein-/ausschalten") ?>">
-            </a>
+            <div class="contentbar-button-wrapper contentbar-consuming-mode-wrapper">
+                <button class="contentbar-button contentbar-button-zoom consuming_mode_trigger"></button>
+            </div>
 
             <? if ($actionMenu) : ?>
-                <?= $actionMenu->render() ?>
+                <div class="contentbar-button-wrapper contentbar-action-menu-wrapper">
+                    <?= $actionMenu->render() ?>
+                </div>
             <? endif ?>
         </div>
-    </div>
-
+    </header>
 </div>
diff --git a/templates/footer.php b/templates/footer.php
index 5f4f0504bab..57fe54cf050 100644
--- a/templates/footer.php
+++ b/templates/footer.php
@@ -1,5 +1,5 @@
 <!-- Beginn Footer -->
-<?= SkipLinks::addIndex(_('Fußzeile'), 'main-footer',900) ?>
+<?= SkipLinks::addIndex(_('Fußzeile'), 'main-footer', 900, false) ?>
 <footer id="main-footer" aria-label="<?= _('Fußzeile') ?>">
 <? if (is_object($GLOBALS['user']) && $GLOBALS['user']->id != 'nobody') : ?>
     <div id="main-footer-info">
@@ -54,5 +54,4 @@
 <? endif; ?>
 </footer>
 <?= $this->render_partial('debug/db-log.php') ?>
-<?= $this->render_partial('responsive-navigation.php') ?>
 <!-- Ende Footer -->
diff --git a/templates/header.php b/templates/header.php
index a234358627c..b3bf385be84 100644
--- a/templates/header.php
+++ b/templates/header.php
@@ -59,11 +59,27 @@ if ($navigation) {
     <!-- Top bar with site title, quick search and avatar menu -->
     <div id="top-bar" role="banner">
         <div id="responsive-menu">
-            <input type="checkbox" id="barTopMenu-toggle">
-            <label for="barTopMenu-toggle">
-                <?= _('Menü') ?>
-            </label>
-            <? // The main menu will be placed here when scrolled, see navigation.less ?>
+            <?= $this->render_partial('responsive-navigation.php') ?>
+            <?
+            $user = User::findCurrent();
+            if ($user) {
+                $me = [
+                    'avatar' => Avatar::getAvatar($user->id)->getURL(Avatar::MEDIUM),
+                    'email' => $user->email,
+                    'fullname' => $user->getFullName(),
+                    'username' => $user->username,
+                    'perm' => $GLOBALS['perm']->get_perm()
+                ];
+
+                $hasSidebar = Sidebar::get()->countWidgets(NavigationWidget::class) > 0;
+                ?>
+            <? } else {
+                $me = ['username' => 'nobody'];
+                $hasSidebar = false;
+            } ?>
+            <responsive-navigation :me='<?= json_encode($me) ?>' context="<?= htmlReady(Context::get() ?
+                Context::get()->getFullname() : '') ?>" :has-sidebar="<?= $hasSidebar ? 'true' : 'false' ?>">
+            </responsive-navigation>
         </div>
         <div id="site-title">
             <?= htmlReady(Config::get()->UNI_NAME_CLEAN) ?>
@@ -97,7 +113,7 @@ if ($navigation) {
                 <? if (PageLayout::hasCustomQuicksearch()): ?>
                     <?= PageLayout::getCustomQuicksearch() ?>
                 <? else: ?>
-                    <? SkipLinks::addIndex(_('Suche'), 'globalsearch-input', 910) ?>
+                    <? SkipLinks::addIndex(_('Suche'), 'globalsearch-input', 910, false) ?>
                     <li id="quicksearch_item">
                         <script>
                             var selectSem = function (seminar_id, name) {
@@ -179,7 +195,7 @@ if ($navigation) {
                             $subnav->getImage()
                         );
                     }
-                    SkipLinks::addIndex(_('Profilmenü'), "header_avatar_image_link", 1);
+                    SkipLinks::addIndex(_('Profilmenü'), 'header_avatar_image_link', 1, false);
                     ?>
                     <?= $action_menu->render(); ?>
                     </div>
@@ -187,6 +203,8 @@ if ($navigation) {
                 </li>
             <? endif; ?>
 
+                <li id="responsive-toggle-desktop"></li>
+                <li id="responsive-toggle-fullscreen"></li>
             </ul>
         </div>
     </div>
@@ -194,7 +212,7 @@ if ($navigation) {
 
     <!-- Main navigation and right-hand logo -->
     <nav id="navigation-level-1" aria-current="page" aria-label="<?= _('Hauptnavigation') ?>">
-        <? SkipLinks::addIndex(_('Hauptnavigation'), 'navigation-level-1', 2); ?>
+        <? SkipLinks::addIndex(_('Hauptnavigation'), 'navigation-level-1', 2, false); ?>
         <ul id="navigation-level-1-items" <? if (count($header_nav['hidden']) > 0) echo 'class="overflown"'; ?>>
         <? foreach ($header_nav['visible'] as $path => $nav): ?>
             <?= $this->render_partial(
@@ -309,5 +327,7 @@ if ($navigation) {
         </div>
     </div>
 
+    <div id="responsive-contentbar-container"></div>
+
 <!-- End main site header -->
 </header>
diff --git a/templates/layouts/base.php b/templates/layouts/base.php
index 1345dcdf854..e3eb6c39ec1 100644
--- a/templates/layouts/base.php
+++ b/templates/layouts/base.php
@@ -26,12 +26,6 @@ $lang_attr = str_replace('_', '-', $_SESSION['_language']);
         String.locale = "<?= htmlReady(strtr($_SESSION['_language'], '_', '-')) ?>";
 
         document.querySelector('html').classList.replace('no-js', 'js');
-        setTimeout(() => {
-            // This needs to be put in a timeout since otherwise it will not match
-            if (window.matchMedia('(max-width: 767px)').matches) {
-                document.querySelector('html').classList.add('responsive-display');
-            }
-        }, 0);
 
         window.STUDIP = {
             ABSOLUTE_URI_STUDIP: "<?= $GLOBALS['ABSOLUTE_URI_STUDIP'] ?>",
@@ -66,7 +60,14 @@ $lang_attr = str_replace('_', '-', $_SESSION['_language']);
 
     <script>
     window.STUDIP.editor_enabled = <?= json_encode((bool) Studip\Markup::editorEnabled()) ?>;
-    </script>
+
+    setTimeout(() => {
+        // This needs to be put in a timeout since otherwise it will not match
+        if (STUDIP.Responsive.isResponsive()) {
+            document.querySelector('html').classList.add('responsive-display');
+        }
+    }, 0);
+</script>
 </head>
 
 <body id="<?= PageLayout::getBodyElementId() ?>" <? if (SkipLinks::isEnabled()) echo 'class="enable-skiplinks"'; ?>>
@@ -79,6 +80,7 @@ $lang_attr = str_replace('_', '-', $_SESSION['_language']);
     <!-- Start main page content -->
     <main id="content-wrapper">
         <div id="content">
+            <h1 class="sr-only"><?= htmlReady(PageLayout::getTitle()) ?></h1>
             <? if (PageLayout::isFullscreenModeAllowed()): ?>
                 <button hidden class="fullscreen-toggle unfullscreen" aria-label="<?= _('Vollbildmodus verlassen') ?>" title="<?= _('Vollbildmodus verlassen') ?>">
                     <?= Icon::create('zoom-out2')->asImg(24) ?>
diff --git a/templates/skiplinks.php b/templates/skiplinks.php
index 3854c4c6a96..da222107894 100644
--- a/templates/skiplinks.php
+++ b/templates/skiplinks.php
@@ -3,15 +3,22 @@
 ?>
 <? if ($navigation instanceof Navigation && iterator_count($navigation) > 0) : ?>
     <ul role="navigation" id="skiplink_list">
-    <? foreach ($navigation as $nav) : ?>
+    <? foreach ($navigation as $index => $nav) : ?>
         <li>
         <? if (mb_substr($url = $nav->getURL(), 0, 1) == '#') : ?>
-            <button class="skiplink" role="link" onclick="STUDIP.SkipLinks.setActiveTarget('<?= $url ?>');"><?= htmlReady($nav->getTitle()) ?></button>
+            <button class="skiplink" role="link" onclick="STUDIP.SkipLinks.setActiveTarget('<?= htmlReady($url) ?>');"
+                    data-in-fullscreen="<?= $fullscreen[$index] ?>">
+                <?= htmlReady($nav->getTitle()) ?>
+            </button>
         <? else : ?>
             <? if (is_internal_url($url)) : ?>
-                <a href="<?= URLHelper::getLink($url) ?>"><?= htmlReady($nav->getTitle()) ?></a>
+                <a href="<?= URLHelper::getLink($url) ?>" data-in-fullscreen="<?= $fullscreen[$index] ?>">
+                    <?= htmlReady($nav->getTitle()) ?>
+                </a>
             <? else : ?>
-                <a href="<?= htmlReady($url) ?>"><?= htmlReady($nav->getTitle()) ?></a>
+                <a href="<?= htmlReady($url) ?>" data-in-fullscreen="<?= $fullscreen[$index] ?>">
+                    <?= htmlReady($nav->getTitle()) ?>
+                </a>
             <? endif ?>
         <? endif ?>
         </li>
diff --git a/templates/tabs.php b/templates/tabs.php
index 1b505e7c33d..cfdd550cecc 100644
--- a/templates/tabs.php
+++ b/templates/tabs.php
@@ -8,7 +8,7 @@ foreach (Navigation::getItem("/")->getSubNavigation() as $path => $nav) {
 $ebene3 = [];
 ?>
 <div class="tabs_wrapper">
-    <? SkipLinks::addIndex(_('Zweite Navigationsebene'), 'navigation-level-2', 10); ?>
+    <? SkipLinks::addIndex(_('Zweite Navigationsebene'), 'navigation-level-2', 10, false); ?>
     <ul id="tabs" role="navigation">
         <? if (!empty($navigation)): ?>
         <? foreach ($navigation as $path => $nav) : ?>
@@ -60,13 +60,7 @@ $ebene3 = [];
         <? endforeach ?>
        <? endif; ?>
     </ul>
-    <? if (PageLayout::isFullscreenModeAllowed()): ?>
-        <div class="fullscreen-container">
-            <button class="fullscreen-toggle" aria-label="<?= _('Vollbildmodus') ?>" title="<?= _('Vollbildmodus') ?>">
-                <?= Icon::create('zoom-in2')->asImg(24) ?>
-            </button>
-        </div>
-    <? endif ?>
+    <div id="non-responsive-toggle-fullscreen"></div>
     <? if (is_object($GLOBALS['perm']) && $GLOBALS['perm']->have_perm('autor')) : ?>
         <?= Helpbar::get()->render() ?>
     <? endif; ?>
-- 
GitLab