From 6ff0a7f0215de1acd0490020b53485c45550df79 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 24 Apr 2024 15:58:26 +0300
Subject: refactor sizesetting into unitsetting allowing more unit types with
 i18n support

---
 .../settings_modal/helpers/unit_setting.vue        | 61 ++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 src/components/settings_modal/helpers/unit_setting.vue

(limited to 'src/components/settings_modal/helpers/unit_setting.vue')

diff --git a/src/components/settings_modal/helpers/unit_setting.vue b/src/components/settings_modal/helpers/unit_setting.vue
new file mode 100644
index 00000000..d6aafe26
--- /dev/null
+++ b/src/components/settings_modal/helpers/unit_setting.vue
@@ -0,0 +1,61 @@
+<template>
+  <span
+    v-if="matchesExpertLevel"
+    class="UnitSetting"
+  >
+    <label
+      :for="path"
+      class="size-label"
+    >
+      <slot />
+    </label>
+    <input
+      :id="path"
+      class="number-input"
+      type="number"
+      step="1"
+      :disabled="disabled"
+      :min="min || 0"
+      :value="stateValue"
+      @change="updateValue"
+    >
+    <Select
+      :id="path"
+      :model-value="stateUnit"
+      :disabled="disabled"
+      class="unit-input unstyled"
+      @change="updateUnit"
+    >
+      <option
+        v-for="option in units"
+        :key="option"
+        :value="option"
+      >
+        {{ getUnitString(option) }}
+      </option>
+    </Select>
+    {{ ' ' }}
+    <ModifiedIndicator
+      :changed="isChanged"
+      :onclick="reset"
+    />
+  </span>
+</template>
+
+<script src="./unit_setting.js"></script>
+
+<style lang="scss">
+.UnitSetting {
+  .number-input {
+    max-width: 6.5em;
+    text-align: right;
+  }
+
+  .unit-input,
+  .unit-input select {
+    min-width: 4em;
+    width: auto;
+  }
+}
+
+</style>
-- 
cgit v1.2.3-70-g09d2


From e4a819a0e2ed9c57dc2191428d86a33bb5918862 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 22 May 2024 19:54:19 +0300
Subject: initial Appearance Tab implementation, added text size/UI scale
 option

---
 src/App.scss                                       |  4 +-
 src/components/settings_modal/helpers/setting.js   | 10 ++-
 .../settings_modal/helpers/unit_setting.js         | 24 ++++++--
 .../settings_modal/helpers/unit_setting.vue        |  2 +-
 src/components/settings_modal/settings_modal.js    |  3 +
 src/components/settings_modal/settings_modal.vue   | 12 ++++
 .../settings_modal/settings_modal_user_content.js  |  2 +
 .../settings_modal/settings_modal_user_content.vue |  7 +++
 .../settings_modal/tabs/appearance_tab.js          | 39 ++++++++++++
 .../settings_modal/tabs/appearance_tab.vue         | 71 ++++++++++++++++++++++
 src/components/settings_modal/tabs/general_tab.vue | 15 -----
 src/i18n/en.json                                   |  8 +++
 src/modules/config.js                              | 33 +++++++++-
 src/modules/instance.js                            |  2 +
 src/modules/interface.js                           | 14 +++++
 src/services/style_setter/style_setter.js          | 16 ++++-
 16 files changed, 236 insertions(+), 26 deletions(-)
 create mode 100644 src/components/settings_modal/tabs/appearance_tab.js
 create mode 100644 src/components/settings_modal/tabs/appearance_tab.vue

(limited to 'src/components/settings_modal/helpers/unit_setting.vue')

diff --git a/src/App.scss b/src/App.scss
index 6e0aabca..32ca9189 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -3,7 +3,7 @@
 @import "./panel";
 
 :root {
-  --font-size: 14px;
+  --fontSize: 14px;
   --status-margin: 0.75em;
   --navbar-height: 3.5rem;
   --post-line-height: 1.4;
@@ -20,7 +20,7 @@
 }
 
 html {
-  font-size: var(--font-size);
+  font-size: var(--textSize);
   // overflow-x: clip causes my browser's tab to crash with SIGILL lul
 }
 
diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js
index abf9cfdf..3b3e6268 100644
--- a/src/components/settings_modal/helpers/setting.js
+++ b/src/components/settings_modal/helpers/setting.js
@@ -48,6 +48,10 @@ export default {
     draftMode: {
       type: Boolean,
       default: undefined
+    },
+    timedApplyMode: {
+      type: Boolean,
+      default: false
     }
   },
   inject: {
@@ -161,7 +165,11 @@ export default {
         case 'admin':
           return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v })
         default:
-          return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
+          if (this.timedApplyMode) {
+            return (k, v) => this.$store.dispatch('setOptionTemporarily', { name: k, value: v })
+          } else {
+            return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
+          }
       }
     },
     defaultState () {
diff --git a/src/components/settings_modal/helpers/unit_setting.js b/src/components/settings_modal/helpers/unit_setting.js
index c9c23cb0..daeddd81 100644
--- a/src/components/settings_modal/helpers/unit_setting.js
+++ b/src/components/settings_modal/helpers/unit_setting.js
@@ -21,15 +21,23 @@ export default {
     unitSet: {
       type: String,
       default: 'none'
+    },
+    step: {
+      type: Number,
+      default: 1
+    },
+    resetDefault: {
+      type: Object,
+      default: null
     }
   },
   computed: {
     ...Setting.computed,
     stateUnit () {
-      return this.state.replace(/\d+/, '')
+      return typeof this.state === 'string' ? this.state.replace(/[0-9,.]+/, '') : ''
     },
     stateValue () {
-      return this.state.replace(/\D+/, '')
+      return typeof this.state === 'string' ? this.state.replace(/[^0-9,.]+/, '') : ''
     }
   },
   methods: {
@@ -39,10 +47,18 @@ export default {
       return this.$t(['settings', 'units', this.unitSet, value].join('.'))
     },
     updateValue (e) {
-      this.configSink(this.path, parseInt(e.target.value) + this.stateUnit)
+      this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit)
     },
     updateUnit (e) {
-      this.configSink(this.path, this.stateValue + e.target.value)
+      let value = this.stateValue
+      const newUnit = e.target.value
+      if (this.resetDefault) {
+        const replaceValue = this.resetDefault[newUnit]
+        if (replaceValue != null) {
+          value = replaceValue
+        }
+      }
+      this.configSink(this.path, value + newUnit)
     }
   }
 }
diff --git a/src/components/settings_modal/helpers/unit_setting.vue b/src/components/settings_modal/helpers/unit_setting.vue
index 68f52b1c..86798e04 100644
--- a/src/components/settings_modal/helpers/unit_setting.vue
+++ b/src/components/settings_modal/helpers/unit_setting.vue
@@ -13,7 +13,7 @@
       :id="path"
       class="input number-input"
       type="number"
-      step="1"
+      step="step"
       :disabled="disabled"
       :min="min || 0"
       :value="stateValue"
diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js
index ff58f2c3..63c9b24a 100644
--- a/src/components/settings_modal/settings_modal.js
+++ b/src/components/settings_modal/settings_modal.js
@@ -4,6 +4,7 @@ import AsyncComponentError from 'src/components/async_component_error/async_comp
 import getResettableAsyncComponent from 'src/services/resettable_async_component.js'
 import Popover from '../popover/popover.vue'
 import Checkbox from 'src/components/checkbox/checkbox.vue'
+import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import { cloneDeep, isEqual } from 'lodash'
 import {
@@ -53,6 +54,7 @@ const SettingsModal = {
     Modal,
     Popover,
     Checkbox,
+    ConfirmModal,
     SettingsModalUserContent: getResettableAsyncComponent(
       () => import('./settings_modal_user_content.vue'),
       {
@@ -165,6 +167,7 @@ const SettingsModal = {
   },
   computed: {
     currentSaveStateNotice () {
+      console.log(this.$store.state.interface.settings.currentSaveStateNotice)
       return this.$store.state.interface.settings.currentSaveStateNotice
     },
     modalActivated () {
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index 50859c94..90dbbde0 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -147,6 +147,18 @@
         </span>
       </div>
     </div>
+    <teleport to="#modal">
+      <ConfirmModal
+        v-if="$store.state.interface.temporaryChangesTimeoutId"
+        :title="$t('settings.confirm_new_setting')"
+        :cancel-text="$t('settings.revert')"
+        :confirm-text="$t('settings.confirm')"
+        @cancelled="$store.state.interface.temporaryChangesRevert"
+        @accepted="$store.state.interface.temporaryChangesConfirm"
+      >
+        {{ $t('settings.confirm_new_question') }}
+      </ConfirmModal>
+    </teleport>
   </Modal>
 </template>
 
diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js
index 9ac0301f..ac119006 100644
--- a/src/components/settings_modal/settings_modal_user_content.js
+++ b/src/components/settings_modal/settings_modal_user_content.js
@@ -7,6 +7,7 @@ import FilteringTab from './tabs/filtering_tab.vue'
 import SecurityTab from './tabs/security_tab/security_tab.vue'
 import ProfileTab from './tabs/profile_tab.vue'
 import GeneralTab from './tabs/general_tab.vue'
+import AppearanceTab from './tabs/appearance_tab.vue'
 import VersionTab from './tabs/version_tab.vue'
 import ThemeTab from './tabs/theme_tab/theme_tab.vue'
 
@@ -44,6 +45,7 @@ const SettingsModalContent = {
     SecurityTab,
     ProfileTab,
     GeneralTab,
+    AppearanceTab,
     VersionTab,
     ThemeTab
   },
diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue
index 0221cccb..da99f340 100644
--- a/src/components/settings_modal/settings_modal_user_content.vue
+++ b/src/components/settings_modal/settings_modal_user_content.vue
@@ -21,6 +21,13 @@
     >
       <ProfileTab />
     </div>
+    <div
+      :label="$t('settings.appearance')"
+      icon="paint-brush"
+      data-tab-name="appearance"
+    >
+      <AppearanceTab />
+    </div>
     <div
       v-if="isLoggedIn"
       :label="$t('settings.security_tab')"
diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js
new file mode 100644
index 00000000..b96106bd
--- /dev/null
+++ b/src/components/settings_modal/tabs/appearance_tab.js
@@ -0,0 +1,39 @@
+import BooleanSetting from '../helpers/boolean_setting.vue'
+import ChoiceSetting from '../helpers/choice_setting.vue'
+import IntegerSetting from '../helpers/integer_setting.vue'
+import FloatSetting from '../helpers/float_setting.vue'
+import UnitSetting from '../helpers/unit_setting.vue'
+
+import SharedComputedObject from '../helpers/shared_computed_object.js'
+import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+  faGlobe
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+  faGlobe
+)
+
+const AppearanceTab = {
+  data () {
+    return {}
+  },
+  components: {
+    BooleanSetting,
+    ChoiceSetting,
+    IntegerSetting,
+    FloatSetting,
+    UnitSetting,
+    ProfileSettingIndicator
+  },
+  computed: {
+    instanceWallpaperUsed () {
+      return this.$store.state.instance.background &&
+        !this.$store.state.users.currentUser.background_image
+    },
+    ...SharedComputedObject()
+  }
+}
+
+export default AppearanceTab
diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue
new file mode 100644
index 00000000..7730b701
--- /dev/null
+++ b/src/components/settings_modal/tabs/appearance_tab.vue
@@ -0,0 +1,71 @@
+<template>
+  <div :label="$t('settings.general')">
+    <div class="setting-item">
+      <h2>{{ $t('settings.interface') }}</h2>
+      <ul class="setting-list">
+        <li v-if="instanceWallpaperUsed">
+          <BooleanSetting path="hideInstanceWallpaper">
+            {{ $t('settings.hide_wallpaper') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="disableStickyHeaders">
+            {{ $t('settings.disable_sticky_headers') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="showScrollbars">
+            {{ $t('settings.show_scrollbars') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <UnitSetting
+            path="textSize"
+            step="0.1"
+            :units="['px', 'rem']"
+            :reset-default="{ 'px': 14, 'rem': 1 }"
+            timed-apply-mode
+          >
+            {{ $t('settings.text_size') }}
+          </UnitSetting>
+          <div>
+            <small>
+              <i18n-t
+                scope="global"
+                keypath="settings.text_size_tip"
+                tag="span"
+              >
+                <code>px</code>
+                <code>rem</code>
+              </i18n-t>
+              <br/>
+              <i18n-t
+                scope="global"
+                keypath="settings.text_size_tip2"
+                tag="span"
+              >
+                <code>14px</code>
+              </i18n-t>
+            </small>
+          </div>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script src="./appearance_tab.js"></script>
+
+<style lang="scss">
+.column-settings {
+  display: flex;
+  justify-content: space-evenly;
+  flex-wrap: wrap;
+}
+
+.column-settings .size-label {
+  display: block;
+  margin-bottom: 0.5em;
+  margin-top: 0.5em;
+}
+</style>
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 208c49ee..240c0762 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -15,11 +15,6 @@
             {{ $t('settings.hide_isp') }}
           </BooleanSetting>
         </li>
-        <li v-if="instanceWallpaperUsed">
-          <BooleanSetting path="hideInstanceWallpaper">
-            {{ $t('settings.hide_wallpaper') }}
-          </BooleanSetting>
-        </li>
         <li>
           <BooleanSetting path="stopGifs">
             {{ $t('settings.stop_gifs') }}
@@ -101,16 +96,6 @@
         <li>
           <h3>{{ $t('settings.columns') }}</h3>
         </li>
-        <li>
-          <BooleanSetting path="disableStickyHeaders">
-            {{ $t('settings.disable_sticky_headers') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <BooleanSetting path="showScrollbars">
-            {{ $t('settings.show_scrollbars') }}
-          </BooleanSetting>
-        </li>
         <li>
           <BooleanSetting path="sidebarRight">
             {{ $t('settings.right_sidebar') }}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index f626e933..0c3f26b9 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -374,6 +374,14 @@
     "enter_current_password_to_confirm": "Enter your current password to confirm your identity",
     "post_look_feel": "Posts Look & Feel",
     "mention_links": "Mention links",
+    "appearance": "Appearance",
+    "confirm_new_setting": "Confirm new setting?",
+    "confirm_new_question": "Does this look ok? Setting will be reverted in 10 seconds.",
+    "revert": "Revert",
+    "confirm": "Confirm",
+    "text_size": "Text and interface size",
+    "text_size_tip": "Use {0} for absolute values, {1} will scale with browser default text size.",
+    "text_size_tip2": "Values other than {0} might break some things and themes",
     "mfa": {
       "otp": "OTP",
       "setup_otp": "Setup OTP",
diff --git a/src/modules/config.js b/src/modules/config.js
index 8001b854..4d5e8efc 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -115,7 +115,8 @@ export const defaultState = {
   sidebarColumnWidth: '25rem',
   contentColumnWidth: '45rem',
   notifsColumnWidth: '25rem',
-  emojiReactionsScale: 1.0,
+  emojiReactionsScale: undefined,
+  textSize: undefined, // instance default
   navbarColumnStretch: false,
   greentext: undefined, // instance default
   useAtIcon: undefined, // instance default
@@ -173,6 +174,10 @@ const config = {
     }
   },
   mutations: {
+    setOptionTemporarily (state, { name, value }) {
+      set(state, name, value)
+      applyConfig(state)
+    },
     setOption (state, { name, value }) {
       set(state, name, value)
     },
@@ -203,6 +208,31 @@ const config = {
     setHighlight ({ commit, dispatch }, { user, color, type }) {
       commit('setHighlight', { user, color, type })
     },
+    setOptionTemporarily ({ commit, dispatch, state, rootState }, { name, value }) {
+      if (rootState.interface.temporaryChangesTimeoutId !== null) {
+        console.warn('Can\'t track more than one temporary change')
+        return
+      }
+      const oldValue = state[name]
+
+      commit('setOptionTemporarily', { name, value })
+
+      const confirm = () => {
+        dispatch('setOption', { name, value })
+        commit('clearTemporaryChanges')
+      }
+
+      const revert = () => {
+        commit('setOptionTemporarily', { name, value: oldValue })
+        commit('clearTemporaryChanges')
+      }
+
+      commit('setTemporaryChanges', {
+        timeoutId: setTimeout(revert, 10000),
+        confirm,
+        revert
+      })
+    },
     setOption ({ commit, dispatch, state }, { name, value }) {
       const exceptions = new Set([
         'useStreamingApi'
@@ -231,6 +261,7 @@ const config = {
           case 'sidebarColumnWidth':
           case 'contentColumnWidth':
           case 'notifsColumnWidth':
+          case 'textSize':
           case 'emojiReactionsScale':
             applyConfig(state)
             break
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 0a5c1ae7..4a75d948 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -98,6 +98,8 @@ const defaultState = {
   sidebarRight: false,
   subjectLineBehavior: 'email',
   theme: 'pleroma-dark',
+  emojiReactionsScale: 1.0,
+  textSize: '14px',
   virtualScrolling: true,
   sensitiveByDefault: false,
   conversationDisplay: 'linear',
diff --git a/src/modules/interface.js b/src/modules/interface.js
index 39242b9d..e21b4204 100644
--- a/src/modules/interface.js
+++ b/src/modules/interface.js
@@ -1,5 +1,8 @@
 const defaultState = {
   themeApplied: false,
+  temporaryChangesTimeoutId: null, // used for temporary options that revert after a timeout
+  temporaryChangesConfirm: () => {}, // used for applying temporary options
+  temporaryChangesRevert: () => {}, // used for reverting temporary options
   settingsModalState: 'hidden',
   settingsModalLoadedUser: false,
   settingsModalLoadedAdmin: false,
@@ -36,6 +39,17 @@ const interfaceMod = {
         state.settings.currentSaveStateNotice = { error: true, errorData: error }
       }
     },
+    setTemporaryChanges (state, { timeoutId, confirm, revert }) {
+      state.temporaryChangesTimeoutId = timeoutId
+      state.temporaryChangesConfirm = confirm
+      state.temporaryChangesRevert = revert
+    },
+    clearTemporaryChanges (state) {
+      clearTimeout(state.temporaryChangesTimeoutId)
+      state.temporaryChangesTimeoutId = null
+      state.temporaryChangesConfirm = () => {}
+      state.temporaryChangesRevert = () => {}
+    },
     setThemeApplied (state) {
       state.themeApplied = true
     },
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 369d2c9f..a98456d3 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -148,8 +148,19 @@ export const applyTheme = async (input, onFinish = (data) => {}) => {
   return Promise.resolve()
 }
 
-const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) =>
-  ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale })
+const configColumns = ({
+  sidebarColumnWidth,
+  contentColumnWidth,
+  notifsColumnWidth,
+  emojiReactionsScale,
+  textSize
+}) => ({
+  sidebarColumnWidth,
+  contentColumnWidth,
+  notifsColumnWidth,
+  emojiReactionsScale,
+  textSize
+})
 
 const defaultConfigColumns = configColumns(defaultState)
 
@@ -175,6 +186,7 @@ export const applyConfig = (config) => {
 
   styleSheet.toString()
   styleSheet.insertRule(`:root { ${rules} }`, 'index-max')
+
   body.classList.remove('hidden')
 }
 
-- 
cgit v1.2.3-70-g09d2


From 041da0cdffd73e716a02cc232b01b56526864c63 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 21 Jul 2024 23:46:54 +0300
Subject: fix step not working in unit input

---
 src/components/settings_modal/helpers/unit_setting.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/components/settings_modal/helpers/unit_setting.vue')

diff --git a/src/components/settings_modal/helpers/unit_setting.vue b/src/components/settings_modal/helpers/unit_setting.vue
index 86798e04..e5da9012 100644
--- a/src/components/settings_modal/helpers/unit_setting.vue
+++ b/src/components/settings_modal/helpers/unit_setting.vue
@@ -13,7 +13,7 @@
       :id="path"
       class="input number-input"
       type="number"
-      step="step"
+      :step="step"
       :disabled="disabled"
       :min="min || 0"
       :value="stateValue"
-- 
cgit v1.2.3-70-g09d2


From f4544f0742508713a9cef2db08c6fa844345d9e0 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 23 Jul 2024 21:35:29 +0300
Subject: elaboration and spacing

---
 .../settings_modal/helpers/unit_setting.vue        |  1 +
 .../settings_modal/tabs/appearance_tab.vue         | 86 ++++++++++++----------
 .../settings_modal/tabs/theme_tab/theme_tab.scss   |  5 ++
 .../settings_modal/tabs/theme_tab/theme_tab.vue    |  2 +-
 src/i18n/en.json                                   |  3 +-
 5 files changed, 57 insertions(+), 40 deletions(-)

(limited to 'src/components/settings_modal/helpers/unit_setting.vue')

diff --git a/src/components/settings_modal/helpers/unit_setting.vue b/src/components/settings_modal/helpers/unit_setting.vue
index e5da9012..40ab6880 100644
--- a/src/components/settings_modal/helpers/unit_setting.vue
+++ b/src/components/settings_modal/helpers/unit_setting.vue
@@ -9,6 +9,7 @@
     >
       <slot />
     </label>
+    {{ ' ' }}
     <input
       :id="path"
       class="input number-input"
diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue
index 2b5c4db9..55f0d1be 100644
--- a/src/components/settings_modal/tabs/appearance_tab.vue
+++ b/src/components/settings_modal/tabs/appearance_tab.vue
@@ -1,5 +1,5 @@
 <template>
-  <div :label="$t('settings.general')">
+  <div class="appearance-tab" :label="$t('settings.general')">
     <div class="setting-item">
       <h2>{{ $t('settings.theme') }}</h2>
       <ul
@@ -34,6 +34,9 @@
         </button>
       </ul>
     </div>
+    <div class="alert neutral theme-notice">
+      {{ $t("settings.style.appearance_tab_note") }}
+    </div>
     <div class="setting-item">
       <h2>{{ $t('settings.scale_and_layout') }}</h2>
       <ul class="setting-list">
@@ -254,49 +257,56 @@
 <script src="./appearance_tab.js"></script>
 
 <style lang="scss">
-.column-settings {
-  display: flex;
-  justify-content: space-evenly;
-  flex-wrap: wrap;
-}
+.appearance-tab {
+  .theme-notice {
+    padding: 0.5em;
+    margin: 1em;
+  }
 
-.column-settings .size-label {
-  display: block;
-  margin-bottom: 0.5em;
-  margin-top: 0.5em;
-}
+  .column-settings {
+    display: flex;
+    justify-content: space-evenly;
+    flex-wrap: wrap;
+  }
 
-.theme-list {
-  list-style: none;
-  display: flex;
-  flex-wrap: wrap;
-  margin: -0.5em 0;
-  height: 25em;
-  overflow-x: hidden;
-  overflow-y: auto;
-  scrollbar-gutter: stable;
-  border-radius: var(--roundness);
-  border: 1px solid var(--border);
-  padding: 0;
+  .column-settings .size-label {
+    display: block;
+    margin-bottom: 0.5em;
+    margin-top: 0.5em;
+  }
 
-  .theme-preview {
-    font-size: 1rem; // fix for firefox
-    width: 19rem;
+  .theme-list {
+    list-style: none;
     display: flex;
-    flex-direction: column;
-    align-items: center;
-    margin: 0.5em;
+    flex-wrap: wrap;
+    margin: -0.5em 0;
+    height: 25em;
+    overflow-x: hidden;
+    overflow-y: auto;
+    scrollbar-gutter: stable;
+    border-radius: var(--roundness);
+    border: 1px solid var(--border);
+    padding: 0;
 
-    &.placeholder {
-      opacity: 0.2;
-    }
+    .theme-preview {
+      font-size: 1rem; // fix for firefox
+      width: 19rem;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      margin: 0.5em;
+
+      &.placeholder {
+        opacity: 0.2;
+      }
 
-    .preview-container {
-      pointer-events: none;
-      zoom: 0.5;
-      border: none;
-      border-radius: var(--roundness);
-      text-align: left;
+      .preview-container {
+        pointer-events: none;
+        zoom: 0.5;
+        border: none;
+        border-radius: var(--roundness);
+        text-align: left;
+      }
     }
   }
 }
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
index 3ae8864c..84933fb8 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
@@ -1,4 +1,9 @@
 .theme-tab {
+  .deprecation-warning {
+    padding: 0.5em;
+    margin: 2em;
+  }
+
   padding-bottom: 2em;
 
   .preset-switcher {
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
index dd23145c..4498c143 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="theme-tab">
-    <div class="alert warning">
+    <div class="alert warning deprecation-warning">
       {{ $t("settings.style.themes2_outdated") }}
     </div>
     <div class="presets-container">
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 7c6f4217..97973c15 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -741,7 +741,8 @@
     "more_settings": "More settings",
     "style": {
       "custom_theme_used": "(Custom theme)",
-      "themes2_outdated": "Editor for Themes V2 is no longer supported and presented here for sake of legacy.",
+      "themes2_outdated": "Editor for Themes V2 is being phased out and will eventually be replaced with a new one that takes advantage of new Themes V3 engine. It should still work but experience might be degraded and inconsistent.",
+      "appearance_tab_note": "Changes on this tab do not affect the theme used",
       "update_preview": "Update preview",
       "themes3": {
         "define": "Override",
-- 
cgit v1.2.3-70-g09d2

