diff --git a/maubot/management/frontend/src/pages/dashboard/index.js b/maubot/management/frontend/src/pages/dashboard/index.js
index ed72dc7..567c443 100644
--- a/maubot/management/frontend/src/pages/dashboard/index.js
+++ b/maubot/management/frontend/src/pages/dashboard/index.js
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 import React, { Component } from "react"
-import { Route, Switch, Link } from "react-router-dom"
+import { Route, Switch, Link, withRouter } from "react-router-dom"
 import api from "../../api"
 import { ReactComponent as Plus } from "../../res/plus.svg"
 import Instance from "./Instance"
@@ -28,10 +28,17 @@ class Dashboard extends Component {
             instances: {},
             clients: {},
             plugins: {},
+            sidebarOpen: false,
         }
         window.maubot = this
     }
 
+    componentDidUpdate(prevProps) {
+        if (this.props.location !== prevProps.location) {
+            this.setState({ sidebarOpen: false })
+        }
+    }
+
     async componentWillMount() {
         const [instanceList, clientList, pluginList] = await Promise.all([
             api.getInstances(), api.getClients(), api.getPlugins()])
@@ -90,15 +97,15 @@ class Dashboard extends Component {
     )
 
     render() {
-        return <div className="dashboard">
+        return <div className={`dashboard ${this.state.sidebarOpen ? "sidebar-open" : ""}`}>
             <Link to="/" className="title">
                 <img src="/favicon.png" alt=""/>
                 Maubot Manager
             </Link>
-
             <div className="user">
                 <span>{localStorage.username}</span>
             </div>
+
             <nav className="sidebar">
                 <div className="instances list">
                     <div className="title">
@@ -122,6 +129,14 @@ class Dashboard extends Component {
                     {this.renderList("plugins", Plugin.ListEntry)}
                 </div>
             </nav>
+
+            <div className="topbar">
+                <div className={`hamburger ${this.state.sidebarOpen ? "active" : ""}`}
+                     onClick={evt => this.setState({ sidebarOpen: !this.state.sidebarOpen })}>
+                    <span/><span/><span/>
+                </div>
+            </div>
+
             <main className="view">
                 <Switch>
                     <Route path="/" exact render={() => "Hello, World!"}/>
@@ -145,4 +160,4 @@ class Dashboard extends Component {
     }
 }
 
-export default Dashboard
+export default withRouter(Dashboard)
diff --git a/maubot/management/frontend/src/style/lib/preferencetable.sass b/maubot/management/frontend/src/style/lib/preferencetable.sass
index 64c2124..c72dff1 100644
--- a/maubot/management/frontend/src/style/lib/preferencetable.sass
+++ b/maubot/management/frontend/src/style/lib/preferencetable.sass
@@ -23,6 +23,9 @@
 
     > .entry
         display: block
+
+        @media screen and (max-width: 55rem)
+            width: calc(100% - 1rem)
         width: calc(50% - 1rem)
         margin: .5rem
 
diff --git a/maubot/management/frontend/src/style/pages/client/avatar.sass b/maubot/management/frontend/src/style/pages/client/avatar.sass
index 062a605..25e596a 100644
--- a/maubot/management/frontend/src/style/pages/client/avatar.sass
+++ b/maubot/management/frontend/src/style/pages/client/avatar.sass
@@ -21,6 +21,9 @@
     height: 8rem
     border-radius: 50%
 
+    @media screen and (max-width: 40rem)
+        margin: 0 auto 1rem
+
     > img.avatar
         position: absolute
         display: block
diff --git a/maubot/management/frontend/src/style/pages/client/index.sass b/maubot/management/frontend/src/style/pages/client/index.sass
index 9444513..3d004db 100644
--- a/maubot/management/frontend/src/style/pages/client/index.sass
+++ b/maubot/management/frontend/src/style/pages/client/index.sass
@@ -16,7 +16,6 @@
 
 > div.client
     display: flex
-    margin: 2rem 4rem
 
     > div.sidebar
         vertical-align: top
@@ -36,3 +35,10 @@
 
         > div.instances
             +instancelist
+
+    @media screen and (max-width: 40rem)
+        flex-wrap: wrap
+
+        > div.sidebar, > div.info
+            width: 100%
+            margin-right: 0
diff --git a/maubot/management/frontend/src/style/pages/dashboard-grid.css b/maubot/management/frontend/src/style/pages/dashboard-grid.css
index 88010db..516c7ff 100644
--- a/maubot/management/frontend/src/style/pages/dashboard-grid.css
+++ b/maubot/management/frontend/src/style/pages/dashboard-grid.css
@@ -5,3 +5,22 @@
         [row3-start] "sidebar main" auto [row3-end]
         / 15rem auto;
 }
+
+
+@media screen and (max-width: 35rem) {
+    .dashboard {
+        grid-template:
+            [row1-start] "topbar" 3.5rem [row1-end]
+            [row2-start] "main" auto [row2-end]
+            / auto;
+    }
+
+    .dashboard.sidebar-open {
+        grid-template:
+            [row1-start] "title topbar" 3.5rem [row1-end]
+            [row2-start] "user main" 2.5rem  [row2-end]
+            [row3-start] "sidebar main" auto [row3-end]
+            / 15rem 100%;
+        overflow-x: hidden;
+    }
+}
diff --git a/maubot/management/frontend/src/style/pages/dashboard.sass b/maubot/management/frontend/src/style/pages/dashboard.sass
index 56269f2..681728e 100644
--- a/maubot/management/frontend/src/style/pages/dashboard.sass
+++ b/maubot/management/frontend/src/style/pages/dashboard.sass
@@ -60,11 +60,19 @@
             border-radius: .25rem
 
     @import sidebar
+    @import topbar
+
+    @media screen and (max-width: 35rem)
+        &:not(.sidebar-open)
+            > nav.sidebar, > a.title, > div.user
+                display: none !important
 
     > main.view
         grid-area: main
         border-left: 1px solid $border-color
 
+        overflow-y: scroll
+
         @import client/index
         @import instance
         @import plugin
@@ -74,6 +82,12 @@
             margin-top: 5rem
             font-size: 1.5rem
 
+        > div:not(.not-found)
+            margin: 2rem 4rem
+
+            @media screen and (max-width: 50rem)
+                margin: 2rem 1rem
+
         div.buttons
             +button-group
             display: flex
diff --git a/maubot/management/frontend/src/style/pages/instance.sass b/maubot/management/frontend/src/style/pages/instance.sass
index 607352c..18b63a0 100644
--- a/maubot/management/frontend/src/style/pages/instance.sass
+++ b/maubot/management/frontend/src/style/pages/instance.sass
@@ -15,8 +15,6 @@
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 > div.instance
-    margin: 2rem 4rem
-
     > div.preference-table
         .select-client
             display: flex
diff --git a/maubot/management/frontend/src/style/pages/login.sass b/maubot/management/frontend/src/style/pages/login.sass
index 6447f54..c1be6a4 100644
--- a/maubot/management/frontend/src/style/pages/login.sass
+++ b/maubot/management/frontend/src/style/pages/login.sass
@@ -28,6 +28,10 @@
     border-radius: .25rem
     margin-top: 3rem
 
+    @media screen and (max-width: 27rem)
+        margin: 3rem 1rem 0
+        width: calc(100% - 2rem)
+
     h1
         color: $primary
         margin: 3rem 0
@@ -35,13 +39,14 @@
     input, button
         margin: .5rem 2.5rem
         height: 3rem
-        width: 20rem
+        width: calc(100% - 5rem)
+        box-sizing: border-box
 
     input
         +input
 
     button
-        +button($width: 20rem, $height: 3rem, $padding: 0)
+        +button($width: calc(100% - 5rem), $height: 3rem, $padding: 0)
         +main-color-button
 
         .spinner
diff --git a/maubot/management/frontend/src/style/pages/plugin.sass b/maubot/management/frontend/src/style/pages/plugin.sass
index b45d867..b9fed11 100644
--- a/maubot/management/frontend/src/style/pages/plugin.sass
+++ b/maubot/management/frontend/src/style/pages/plugin.sass
@@ -15,8 +15,6 @@
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 > .plugin
-    margin: 2rem 4rem
-
     > .upload-box
         +upload-box
 
diff --git a/maubot/management/frontend/src/style/pages/sidebar.sass b/maubot/management/frontend/src/style/pages/sidebar.sass
index 58078f4..497ba06 100644
--- a/maubot/management/frontend/src/style/pages/sidebar.sass
+++ b/maubot/management/frontend/src/style/pages/sidebar.sass
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-> .sidebar
+> nav.sidebar
     grid-area: sidebar
     background-color: white
 
diff --git a/maubot/management/frontend/src/style/pages/topbar.sass b/maubot/management/frontend/src/style/pages/topbar.sass
new file mode 100644
index 0000000..e7d8312
--- /dev/null
+++ b/maubot/management/frontend/src/style/pages/topbar.sass
@@ -0,0 +1,74 @@
+// maubot - A plugin-based Matrix bot system.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+.topbar
+    background-color: $primary
+
+    display: flex
+    justify-items: center
+    align-items: center
+    padding: 0 .75rem
+
+    @media screen and (min-width: calc(35rem + 1px))
+        display: none
+
+// Hamburger menu based on "Pure CSS Hamburger fold-out menu" codepen by Erik Terwan (MIT license)
+// https://codepen.io/erikterwan/pen/EVzeRP
+
+.hamburger
+    display: block
+    user-select: none
+    cursor: pointer
+
+    > span
+        display: block
+        width: 29px
+        height: 4px
+        margin-bottom: 5px
+        position: relative
+
+        background: white
+        border-radius: 3px
+
+        z-index: 1
+
+        transform-origin: 4px 0
+
+        //transition: transform 0.5s cubic-bezier(0.77, 0.2, 0.05, 1.0), background 0.5s cubic-bezier(0.77, 0.2, 0.05, 1.0), opacity 0.55s ease
+
+        &:nth-of-type(1)
+            transform-origin: 0 0
+
+        &:nth-of-type(3)
+            transform-origin: 0 100%
+
+    transform: translateY(2px)
+
+    &.active
+        transform: translateX(1px) translateY(4px)
+
+    &.active > span
+        opacity: 1
+
+        &:nth-of-type(1)
+            transform: rotate(45deg) translate(-2px, -1px)
+
+        &:nth-of-type(2)
+            opacity: 0
+            transform: rotate(0deg) scale(0.2, 0.2)
+
+        &:nth-of-type(3)
+            transform: rotate(-45deg) translate(0, -1px)