diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1e0dec201c602a44824c3b3fb2ea7d9c3ba5422..5d1bd11880eefbff835cded14d453b9a9f4946d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Fix status ellipsis menu being cut off in notifications column
 - Fixed autocomplete sometimes not returning the right user when there's already some results
 - Reply filtering options in Settings -> Filtering now work again using filtering on server
+- Don't show just blank-screen when cookies are disabled
 
 ## [2.0.3] - 2020-05-02
 ### Fixed
diff --git a/src/App.js b/src/App.js
index 040138c978bd98dfe3e0eea0f417aba57e73be9b..92c4e2f58266b22c0b5b3e9cffa20af4e3c51f23 100644
--- a/src/App.js
+++ b/src/App.js
@@ -13,6 +13,7 @@ import MobilePostStatusButton from './components/mobile_post_status_button/mobil
 import MobileNav from './components/mobile_nav/mobile_nav.vue'
 import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
 import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
+import GlobalNoticeList from './components/global_notice_list/global_notice_list.vue'
 import { windowWidth } from './services/window_utils/window_utils'
 
 export default {
@@ -32,7 +33,8 @@ export default {
     MobileNav,
     SettingsModal,
     UserReportingModal,
-    PostStatusModal
+    PostStatusModal,
+    GlobalNoticeList
   },
   data: () => ({
     mobileActivePanel: 'timeline',
diff --git a/src/App.vue b/src/App.vue
index 7b9ad3dc1233f33d2311215454ec39c2d3c8c440..03b632eccb5a3be0667944789fbd0c4b8f6e1dae 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -128,6 +128,7 @@
     <PostStatusModal />
     <SettingsModal />
     <portal-target name="modal" />
+    <GlobalNoticeList />
   </div>
 </template>
 
diff --git a/src/components/global_notice_list/global_notice_list.js b/src/components/global_notice_list/global_notice_list.js
new file mode 100644
index 0000000000000000000000000000000000000000..3af29c23421eff018fb9f60320ca54e124827796
--- /dev/null
+++ b/src/components/global_notice_list/global_notice_list.js
@@ -0,0 +1,15 @@
+
+const GlobalNoticeList = {
+  computed: {
+    notices () {
+      return this.$store.state.interface.globalNotices
+    }
+  },
+  methods: {
+    closeNotice (notice) {
+      this.$store.dispatch('removeGlobalNotice', notice)
+    }
+  }
+}
+
+export default GlobalNoticeList
diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue
new file mode 100644
index 0000000000000000000000000000000000000000..0e4285ccff8ad1bb5b48512ad26f13c2d326a397
--- /dev/null
+++ b/src/components/global_notice_list/global_notice_list.vue
@@ -0,0 +1,77 @@
+<template>
+  <div class="global-notice-list">
+    <div
+      v-for="(notice, index) in notices"
+      :key="index"
+      class="alert global-notice"
+      :class="{ ['global-' + notice.level]: true }"
+    >
+      <div class="notice-message">
+        {{ $t(notice.messageKey, notice.messageArgs) }}
+      </div>
+      <i
+        class="button-icon icon-cancel"
+        @click="closeNotice(notice)"
+      />
+    </div>
+  </div>
+</template>
+
+<script src="./global_notice_list.js"></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.global-notice-list {
+  position: fixed;
+  top: 50px;
+  width: 100%;
+  pointer-events: none;
+  z-index: 1001;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  .global-notice {
+    pointer-events: auto;
+    text-align: center;
+    width: 40em;
+    max-width: calc(100% - 3em);
+    display: flex;
+    padding-left: 1.5em;
+    line-height: 2em;
+    .notice-message {
+      flex: 1 1 100%;
+    }
+    i {
+      flex: 0 0;
+      width: 1.5em;
+      cursor: pointer;
+    }
+  }
+
+  .global-error {
+    background-color: var(--alertPopupError, $fallback--cRed);
+    color: var(--alertPopupErrorText, $fallback--text);
+    i {
+      color: var(--alertPopupErrorText, $fallback--text);
+    }
+  }
+
+  .global-warning {
+    background-color: var(--alertPopupWarning, $fallback--cOrange);
+    color: var(--alertPopupWarningText, $fallback--text);
+    i {
+      color: var(--alertPopupWarningText, $fallback--text);
+    }
+  }
+
+  .global-info {
+    background-color: var(--alertPopupNeutral, $fallback--fg);
+    color: var(--alertPopupNeutralText, $fallback--text);
+    i {
+      color: var(--alertPopupNeutralText, $fallback--text);
+    }
+  }
+}
+</style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 2e48c4738c02885302a8c050170b4e92fbc6c66b..8f13dfe9e1ca901f6bea86327fa120ab25a97565 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -164,6 +164,9 @@
     "load_all_hint": "Loaded first {saneAmount} emoji, loading all emoji may cause performance issues.",
     "load_all": "Loading all {emojiAmount} emoji"
   },
+  "errors": {
+    "storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies."
+  },
   "interactions": {
     "favs_repeats": "Repeats and Favorites",
     "follows": "New follows",
diff --git a/src/main.js b/src/main.js
index 9a201e4fac76b041a7931c2b7e764750f7f33252..5bddc76e4a4428e23cf04b70418654c86d8c0478 100644
--- a/src/main.js
+++ b/src/main.js
@@ -62,7 +62,15 @@ const persistedStateOptions = {
 };
 
 (async () => {
-  const persistedState = await createPersistedState(persistedStateOptions)
+  let storageError = false
+  const plugins = [pushNotifications]
+  try {
+    const persistedState = await createPersistedState(persistedStateOptions)
+    plugins.push(persistedState)
+  } catch (e) {
+    console.error(e)
+    storageError = true
+  }
   const store = new Vuex.Store({
     modules: {
       i18n: {
@@ -85,11 +93,13 @@ const persistedStateOptions = {
       polls: pollsModule,
       postStatus: postStatusModule
     },
-    plugins: [persistedState, pushNotifications],
+    plugins,
     strict: false // Socket modifies itself, let's ignore this for now.
     // strict: process.env.NODE_ENV !== 'production'
   })
-
+  if (storageError) {
+    store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
+  }
   afterStoreSetup({ store, i18n })
 })()
 
diff --git a/src/modules/interface.js b/src/modules/interface.js
index eeebd65ebf5047b0083e0ac5849684ac0bc7f82b..e31630fcb2d05856bb32c6ab58094afcd79d6139 100644
--- a/src/modules/interface.js
+++ b/src/modules/interface.js
@@ -14,7 +14,8 @@ const defaultState = {
       window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
     )
   },
-  mobileLayout: false
+  mobileLayout: false,
+  globalNotices: []
 }
 
 const interfaceMod = {
@@ -58,6 +59,12 @@ const interfaceMod = {
       if (!state.settingsModalLoaded) {
         state.settingsModalLoaded = true
       }
+    },
+    pushGlobalNotice (state, notice) {
+      state.globalNotices.push(notice)
+    },
+    removeGlobalNotice (state, notice) {
+      state.globalNotices = state.globalNotices.filter(n => n !== notice)
     }
   },
   actions: {
@@ -81,6 +88,28 @@ const interfaceMod = {
     },
     togglePeekSettingsModal ({ commit }) {
       commit('togglePeekSettingsModal')
+    },
+    pushGlobalNotice (
+      { commit, dispatch },
+      {
+        messageKey,
+        messageArgs = {},
+        level = 'error',
+        timeout = 0
+      }) {
+      const notice = {
+        messageKey,
+        messageArgs,
+        level
+      }
+      if (timeout) {
+        setTimeout(() => dispatch('removeGlobalNotice', notice), timeout)
+      }
+      commit('pushGlobalNotice', notice)
+      return notice
+    },
+    removeGlobalNotice ({ commit }, notice) {
+      commit('removeGlobalNotice', notice)
     }
   }
 }
diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js
index b577cfab85b4055c5abeeaa7f13f1f839224be67..6b25cd6f65f37b3631269a576b527be243c6ad76 100644
--- a/src/services/theme_data/pleromafe.js
+++ b/src/services/theme_data/pleromafe.js
@@ -34,7 +34,8 @@ export const DEFAULT_OPACITY = {
   alert: 0.5,
   input: 0.5,
   faint: 0.5,
-  underlay: 0.15
+  underlay: 0.15,
+  alertPopup: 0.95
 }
 
 /**  SUBJECT TO CHANGE IN THE FUTURE, this is all beta
@@ -627,6 +628,39 @@ export const SLOT_INHERITANCE = {
     textColor: true
   },
 
+  alertPopupError: {
+    depends: ['alertError'],
+    opacity: 'alertPopup'
+  },
+  alertPopupErrorText: {
+    depends: ['alertErrorText'],
+    layer: 'popover',
+    variant: 'alertPopupError',
+    textColor: true
+  },
+
+  alertPopupWarning: {
+    depends: ['alertWarning'],
+    opacity: 'alertPopup'
+  },
+  alertPopupWarningText: {
+    depends: ['alertWarningText'],
+    layer: 'popover',
+    variant: 'alertPopupWarning',
+    textColor: true
+  },
+
+  alertPopupNeutral: {
+    depends: ['alertNeutral'],
+    opacity: 'alertPopup'
+  },
+  alertPopupNeutralText: {
+    depends: ['alertNeutralText'],
+    layer: 'popover',
+    variant: 'alertPopupNeutral',
+    textColor: true
+  },
+
   badgeNotification: '--cRed',
   badgeNotificationText: {
     depends: ['text', 'badgeNotification'],