diff --git a/src/App.scss b/src/App.scss
index 3b03a76197b9c3a5dd001775cd46a5c7f59cf95a..b6d4943a907637aaccc9fd1fc6c1ddbb77a9356d 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -32,6 +32,7 @@ h4 {
   min-height: 100vh;
   max-width: 980px;
   background-color: rgba(0,0,0,0.15);
+  background-color: var(--underlay, rgba(0,0,0,0.15));
   align-content: flex-start;
 }
 
diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js
index b450dc8e36637fde8762f0b30421d00c8f855892..602a635e391cce3f19b9a640c083ac1b6d4ae777 100644
--- a/src/components/style_switcher/style_switcher.js
+++ b/src/components/style_switcher/style_switcher.js
@@ -1,4 +1,4 @@
-import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js'
+import { rgb2hex, hex2rgb, getContrastRatio, getContrastRatioLayers, alphaBlend } from '../../services/color_convert/color_convert.js'
 import { set, delete as del } from 'vue'
 import { merge } from 'lodash'
 import { generateCompat, generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js'
@@ -53,6 +53,9 @@ export default {
       bgColorLocal: '',
       bgOpacityLocal: undefined,
 
+      underlayColorLocal: '',
+      underlayOpacityLocal: undefined,
+
       fgColorLocal: '',
       fgTextColorLocal: undefined,
       fgLinkColorLocal: undefined,
@@ -145,6 +148,8 @@ export default {
 
         accent: this.accentColorLocal,
 
+        underlay: this.underlayColorLocal,
+
         panel: this.panelColorLocal,
         panelText: this.panelTextColorLocal,
         panelLink: this.panelLinkColorLocal,
@@ -182,7 +187,8 @@ export default {
         panel: this.panelOpacityLocal,
         topBar: this.topBarOpacityLocal,
         border: this.borderOpacityLocal,
-        faint: this.faintOpacityLocal
+        faint: this.faintOpacityLocal,
+        underlay: this.underlayOpacityLocal
       }
     },
     currentRadii () {
@@ -240,6 +246,7 @@ export default {
 
       const bgs = {
         bg: hex2rgb(colors.bg),
+        underlay: hex2rgb(colors.underlay),
         btn: hex2rgb(colors.btn),
         panel: hex2rgb(colors.panel),
         topBar: hex2rgb(colors.topBar),
@@ -249,29 +256,31 @@ export default {
         badgeNotification: hex2rgb(colors.badgeNotification)
       }
 
-      /* This is a bit confusing because "bottom layer" used is text color
-       * This is done to get worst case scenario when background below transparent
-       * layer matches text color, making it harder to read the lower alpha is.
-       */
-      const ratios = {
-        bgText: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.text), fgs.text),
-        bgLink: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.link), fgs.link),
-        bgRed: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.red), fgs.red),
-        bgGreen: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.green), fgs.green),
-        bgBlue: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.blue), fgs.blue),
-        bgOrange: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.orange), fgs.orange),
+      const bg = [bgs.bg, opacity.bg]
+      const underlay = [bgs.underlay, opacity.underlay]
 
+      const panel = [underlay, bg]
+
+      const ratios = {
+        bgText: getContrastRatioLayers(fgs.text, panel, fgs.text),
+        bgLink: getContrastRatioLayers(fgs.link, panel, fgs.link),
+        bgRed: getContrastRatioLayers(fgs.red, panel, fgs.red),
+        bgGreen: getContrastRatioLayers(fgs.green, panel, fgs.green),
+        bgBlue: getContrastRatioLayers(fgs.blue, panel, fgs.blue),
+        bgOrange: getContrastRatioLayers(fgs.orange, panel, fgs.orange),
+
+        // TODO what's this?
         tintText: getContrastRatio(alphaBlend(bgs.bg, 0.5, fgs.panelText), fgs.text),
 
-        panelText: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText),
-        panelLink: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelLink), fgs.panelLink),
+        panelText: getContrastRatioLayers(fgs.text, [...panel, [bgs.panel, opacity.panel]], fgs.panelText),
+        panelLink: getContrastRatioLayers(fgs.link, [...panel, [bgs.panel, opacity.panel]], fgs.panelLink),
 
-        btnText: getContrastRatio(alphaBlend(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText),
+        btnText: getContrastRatioLayers(fgs.text, [...panel, [bgs.btn, opacity.btn]], fgs.btnText),
 
-        inputText: getContrastRatio(alphaBlend(bgs.input, opacity.input, fgs.inputText), fgs.inputText),
+        inputText: getContrastRatioLayers(fgs.text, [...panel, [bgs.input, opacity.input]], fgs.inputText),
 
-        topBarText: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText),
-        topBarLink: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink)
+        topBarText: getContrastRatioLayers(fgs.text, [...panel, [bgs.topBar, opacity.topBar]], fgs.topBarText),
+        topBarLink: getContrastRatioLayers(fgs.link, [...panel, [bgs.topBar, opacity.topBar]], fgs.topBarLink)
       }
 
       return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {})
diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue
index 2ecd275a47baddc2209bbe720627496165126f6d..38ca2017a0707473cf6838f43135e8c12dc0686d 100644
--- a/src/components/style_switcher/style_switcher.vue
+++ b/src/components/style_switcher/style_switcher.vue
@@ -366,6 +366,20 @@
               :fallback="previewTheme.opacity.faint || 0.5"
             />
           </div>
+          <div class="color-item">
+            <h4>{{ $t('settings.style.advanced_colors.underlay') }}</h4>
+            <ColorInput
+              v-model="underlayColorLocal"
+              name="underlay"
+              :label="$t('settings.style.advanced_colors.underlay')"
+              fallback='#000000'
+            />
+            <OpacityInput
+              v-model="underlayOpacityLocal"
+              name="underlayOpacity"
+              :fallback="previewTheme.opacity.underlay || 0.15"
+            />
+          </div>
         </div>
 
         <div
diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js
index d1b17c61a23908edcc6baebe49573b66125771bc..757a36bdd20c2f53b120f93aed1af903a5d07299 100644
--- a/src/services/color_convert/color_convert.js
+++ b/src/services/color_convert/color_convert.js
@@ -1,5 +1,8 @@
 import { map } from 'lodash'
 
+// useful for visualizing color when debugging
+export const consoleColor = (color) => console.log('%c##########', 'background: ' + color + '; color: ' + color)
+
 const rgb2hex = (r, g, b) => {
   if (r === null || typeof r === 'undefined') {
     return undefined
@@ -78,6 +81,16 @@ const getContrastRatio = (a, b) => {
 
   return (l1 + 0.05) / (l2 + 0.05)
 }
+/**
+ * Same as `getContrastRatio` but for multiple layers in-between
+ *
+ * @param {Object} text - text color (topmost layer)
+ * @param {[Object, Number]} layers[] - layers between text and bedrock
+ * @param {Object} bedrock - layer at the very bottom
+ */
+export const getContrastRatioLayers = (text, layers, bedrock) => {
+  return getContrastRatio(alphaBlendLayers(bedrock, layers), text)
+}
 
 /**
  * This performs alpha blending between solid background and semi-transparent foreground
@@ -97,6 +110,16 @@ const alphaBlend = (fg, fga, bg) => {
   }, {})
 }
 
+/**
+ * Same as `alphaBlend` but for multiple layers in-between
+ *
+ * @param {Object} bedrock - layer at the very bottom
+ * @param {[Object, Number]} layers[] - layers between text and bedrock
+ */
+export const alphaBlendLayers = (bedrock, layers) => layers.reduce((acc, [color, opacity]) => {
+  return alphaBlend(color, opacity, acc)
+}, bedrock)
+
 const invert = (rgb) => {
   return 'rgb'.split('').reduce((acc, c) => {
     acc[c] = 255 - rgb[c]
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 19a0658770c9b37229091ccd2e8494ab221a0655..8740fc552f04b418bd254c7a8e3ce4503fbd3fe2 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -1,6 +1,6 @@
 import { times } from 'lodash'
 import { brightness, invertLightness, convert, contrastRatio } from 'chromatism'
-import { rgb2hex, hex2rgb, mixrgb, getContrastRatio, alphaBlend } from '../color_convert/color_convert.js'
+import { rgb2hex, hex2rgb, mixrgb, getContrastRatio, alphaBlend, alphaBlendLayers } from '../color_convert/color_convert.js'
 
 // While this is not used anymore right now, I left it in if we want to do custom
 // styles that aren't just colors, so user can pick from a few different distinct
@@ -64,8 +64,10 @@ const getTextColor = function (bg, text, preserve) {
     const base = typeof text.a !== 'undefined' ? { a: text.a } : {}
     const result = Object.assign(base, invertLightness(text).rgb)
     if (!preserve && getContrastRatio(bg, result) < 4.5) {
+      // B&W
       return contrastRatio(bg, text).rgb
     }
+    // Inverted color
     return result
   }
   return text
@@ -173,7 +175,8 @@ const generateColors = (input) => {
   const opacity = Object.assign({
     alert: 0.5,
     input: 0.5,
-    faint: 0.5
+    faint: 0.5,
+    underlay: 0.15
   }, Object.entries(input.opacity || {}).reduce((acc, [k, v]) => {
     if (typeof v !== 'undefined') {
       acc[k] = v
@@ -210,28 +213,37 @@ const generateColors = (input) => {
   colors.faint = col.faint || Object.assign({}, col.text)
 
   colors.bg = col.bg
-  colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb
+  colors.lightBg = col.lightBg || brightness(5 * mod, colors.bg).rgb
+
+  const underlay = [col.underlay, opacity.underlay]
+  const fg = [col.fg, opacity.fg]
+  const bg = [col.bg, opacity.bg]
 
   colors.fg = col.fg
-  colors.fgText = col.fgText || getTextColor(colors.fg, colors.text)
-  colors.fgLink = col.fgLink || getTextColor(colors.fg, colors.link, true)
+  colors.fgText = col.fgText || getTextColor(alphaBlendLayers(colors.text, [underlay, bg, fg]), colors.text)
+  colors.fgLink = col.fgLink || getTextColor(alphaBlendLayers(colors.link, [underlay, bg, fg]), colors.link, true)
+  colors.underlay = col.underlay || hex2rgb('#000000')
 
   colors.border = col.border || brightness(2 * mod, colors.fg).rgb
 
   colors.btn = col.btn || Object.assign({}, col.fg)
-  colors.btnText = col.btnText || getTextColor(colors.btn, colors.fgText)
+  const btn = [colors.btn, opacity.btn || 1]
+  colors.btnText = col.btnText || getTextColor(alphaBlendLayers(colors.fgText, [underlay, bg, fg, btn]), colors.fgText)
 
   colors.input = col.input || Object.assign({}, col.fg)
-  colors.inputText = col.inputText || getTextColor(colors.input, colors.lightText)
+  const inputCol = [colors.input, opacity.input]
+  colors.inputText = col.inputText || getTextColor(alphaBlendLayers(colors.lightText, [underlay, bg, fg, inputCol]), colors.lightText)
 
   colors.panel = col.panel || Object.assign({}, col.fg)
-  colors.panelText = col.panelText || getTextColor(colors.panel, colors.fgText)
-  colors.panelLink = col.panelLink || getTextColor(colors.panel, colors.fgLink)
-  colors.panelFaint = col.panelFaint || getTextColor(colors.panel, colors.faint)
+  const panel = [colors.panel, opacity.panel]
+  colors.panelText = col.panelText || getTextColor(alphaBlendLayers(colors.fgText, [underlay, bg, panel]), colors.fgText)
+  colors.panelLink = col.panelLink || getTextColor(alphaBlendLayers(colors.fgLink, [underlay, bg, panel]), colors.fgLink)
+  colors.panelFaint = col.panelFaint || getTextColor(alphaBlendLayers(colors.faint, [underlay, bg, panel]), colors.faint)
 
   colors.topBar = col.topBar || Object.assign({}, col.fg)
-  colors.topBarText = col.topBarText || getTextColor(colors.topBar, colors.fgText)
-  colors.topBarLink = col.topBarLink || getTextColor(colors.topBar, colors.fgLink)
+  const topBar = [colors.topBar, opacity.topBar]
+  colors.topBarText = col.topBarText || getTextColor(alphaBlendLayers(colors.fgText, [topBar]), colors.fgText)
+  colors.topBarLink = col.topBarLink || getTextColor(alphaBlendLayers(colors.fgLink, [topBar]), colors.fgLink)
 
   colors.faintLink = col.faintLink || Object.assign({}, col.link || col.accent)
   colors.linkBg = alphaBlend(colors.link, 0.4, colors.bg)
@@ -255,6 +267,7 @@ const generateColors = (input) => {
   colors.badgeNotificationText = contrastRatio(colors.badgeNotification).rgb
 
   Object.entries(opacity).forEach(([ k, v ]) => {
+    console.log(k)
     if (typeof v === 'undefined') return
     if (k === 'alert') {
       colors.alertError.a = v