diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue
index 20c6f569d4d74150e5584bbde52f9c0e779e7081..ae00487f28cb18ee9ecef55f610d231227131998 100644
--- a/src/components/export_import/export_import.vue
+++ b/src/components/export_import/export_import.vue
@@ -42,7 +42,7 @@ export default {
   },
   methods: {
     exportData () {
-      const stringified = JSON.stringify(this.exportObject) // Pretty-print and indent with 2 spaces
+      const stringified = JSON.stringify(this.exportObject, null, 2) // Pretty-print and indent with 2 spaces
 
       // Create an invisible link with a data url and simulate a click
       const e = document.createElement('a')
diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js
index 1e5124078d4f9c5d6f4b03cedd881ece8d558c95..b450dc8e36637fde8762f0b30421d00c8f855892 100644
--- a/src/components/style_switcher/style_switcher.js
+++ b/src/components/style_switcher/style_switcher.js
@@ -1,6 +1,7 @@
 import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js'
 import { set, delete as del } from 'vue'
-import { generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js'
+import { merge } from 'lodash'
+import { generateCompat, generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js'
 import ColorInput from '../color_input/color_input.vue'
 import RangeInput from '../range_input/range_input.vue'
 import OpacityInput from '../opacity_input/opacity_input.vue'
@@ -123,6 +124,15 @@ export default {
     selectedVersion () {
       return Array.isArray(this.selected) ? 1 : 2
     },
+    currentCompat () {
+      return generateCompat({
+        shadows: this.shadowsLocal,
+        fonts: this.fontsLocal,
+        opacity: this.currentOpacity,
+        colors: this.currentColors,
+        radii: this.currentRadii
+      })
+    },
     currentColors () {
       return {
         bg: this.bgColorLocal,
@@ -332,7 +342,7 @@ export default {
 
       return {
         // To separate from other random JSON files and possible future theme formats
-        _pleroma_theme_version: 2, theme
+        _pleroma_theme_version: 2, theme: merge(theme, this.currentCompat)
       }
     }
   },
@@ -364,7 +374,7 @@ export default {
     onImport (parsed) {
       if (parsed._pleroma_theme_version === 1) {
         this.normalizeLocalState(parsed, 1)
-      } else if (parsed._pleroma_theme_version === 2) {
+      } else if (parsed._pleroma_theme_version >= 2) {
         this.normalizeLocalState(parsed.theme, 2)
       }
     },
@@ -414,6 +424,7 @@ export default {
 
     /**
      * This applies stored theme data onto form. Supports three versions of data:
+     * v3 (version = 3) - same as 2 but with some incompatible changes
      * v2 (version = 2) - newer version of themes.
      * v1 (version = 1) - older version of themes (import from file)
      * v1l (version = l1) - older version of theme (load from local storage)
@@ -421,12 +432,21 @@ export default {
      * @param {Object} input - input data
      * @param {Number} version - version of data. 0 means try to guess based on data. "l1" means v1, locastorage type
      */
-    normalizeLocalState (input, version = 0) {
-      const colors = input.colors || input
+    normalizeLocalState (originalInput, version = 0) {
+      let input
+      if (typeof originalInput.v3compat !== undefined) {
+        version = 3
+        input = merge(originalInput, originalInput.v3compat)
+      } else {
+        input = originalInput
+      }
+
+      const compat = input.v3compat
       const radii = input.radii || input
       const opacity = input.opacity
       const shadows = input.shadows || {}
       const fonts = input.fonts || {}
+      const colors = input.colors || input
 
       if (version === 0) {
         if (input.version) version = input.version
@@ -530,6 +550,7 @@ export default {
     currentColors () {
       try {
         this.previewColors = generateColors({
+          v3compat: this.currentCompat,
           opacity: this.currentOpacity,
           colors: this.currentColors
         })
@@ -542,8 +563,9 @@ export default {
     currentOpacity () {
       try {
         this.previewColors = generateColors({
+          v3compat: this.currentCompat,
           opacity: this.currentOpacity,
-          colors: this.currentColors
+          colors: this.currentColors,
         })
       } catch (e) {
         console.warn(e)
diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue
index 8bbdc7b7eef6f5fe48a19039c0ec4bee643d2d7b..2ecd275a47baddc2209bbe720627496165126f6d 100644
--- a/src/components/style_switcher/style_switcher.vue
+++ b/src/components/style_switcher/style_switcher.vue
@@ -2,7 +2,7 @@
   <div class="style-switcher">
     <div class="presets-container">
       <div class="save-load">
-        <export-import
+        <ExportImport
           :export-object="exportedTheme"
           :export-label="$t(&quot;settings.export_theme&quot;)"
           :import-label="$t(&quot;settings.import_theme&quot;)"
@@ -38,7 +38,7 @@
               </label>
             </div>
           </template>
-        </export-import>
+        </ExportImport>
       </div>
       <div class="save-load-options">
         <span class="keep-option">
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 1e4bc59ed932111fd841c84a0163b463043f1d24..19a0658770c9b37229091ccd2e8494ab221a0655 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -148,6 +148,26 @@ const getCssColor = (input, a) => {
   return rgb2rgba({ ...rgb, a })
 }
 
+// Generates a "patch" for theme to make it compatible with v2
+export const generateCompat = (input) => {
+  const { colors } = input
+  const v3compat = {
+    colors: {}
+  }
+  const v2colorsPatch = {}
+
+  // # Link became optional in v3
+  if (typeof colors.link === 'undefined') {
+    v2colorsPatch.link = colors.accent
+    v3compat.colors.link = null
+  }
+
+  return {
+    v3compat,
+    colors: v2colorsPatch
+  }
+}
+
 const generateColors = (input) => {
   const colors = {}
   const opacity = Object.assign({
@@ -164,7 +184,10 @@ const generateColors = (input) => {
   const inputColors = input.colors || input
 
   const compat = input.v3compat || {}
-  const compatColors = compat.colors || {}
+  const compatColors = Object.entries(compat.colors || {}).reduce((acc, [key, value]) => {
+    const newVal = value === null ? undefined : value
+    return { ...acc, [key]: newVal }
+  }, {})
 
   const col = Object.entries({ ...inputColors, ...compatColors }).reduce((acc, [k, v]) => {
     if (typeof v === 'object') {
@@ -210,7 +233,7 @@ const generateColors = (input) => {
   colors.topBarText = col.topBarText || getTextColor(colors.topBar, colors.fgText)
   colors.topBarLink = col.topBarLink || getTextColor(colors.topBar, colors.fgLink)
 
-  colors.faintLink = col.faintLink || Object.assign({}, col.link)
+  colors.faintLink = col.faintLink || Object.assign({}, col.link || col.accent)
   colors.linkBg = alphaBlend(colors.link, 0.4, colors.bg)
 
   colors.icon = mixrgb(colors.bg, colors.text)