diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js
index 98c2cbc5ecee893c82e84e8711f7bee6cb84ffd8..16be209aaf1196109e28c3fe3e55f54525d809ad 100644
--- a/src/components/style_switcher/style_switcher.js
+++ b/src/components/style_switcher/style_switcher.js
@@ -15,7 +15,7 @@ import {
 import {
   CURRENT_VERSION,
   SLOT_INHERITANCE,
-  DEFAULT_OPACITY,
+  OPACITIES,
   getLayers
 } from '../../services/theme_data/theme_data.service.js'
 import ColorInput from '../color_input/color_input.vue'
@@ -74,8 +74,8 @@ export default {
         .map(key => [key, ''])
         .reduce((acc, [key, val]) => ({ ...acc, [ key + 'ColorLocal' ]: val }), {}),
 
-      ...Object.keys(DEFAULT_OPACITY)
-        .map(key => [key, undefined])
+      ...Object.keys(OPACITIES)
+        .map(key => console.log(key) || [key, ''])
         .reduce((acc, [key, val]) => ({ ...acc, [ key + 'OpacityLocal' ]: val }), {}),
 
       shadowSelected: undefined,
@@ -115,8 +115,8 @@ export default {
         .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
     },
     currentOpacity () {
-      return Object.keys(DEFAULT_OPACITY)
-        .map(key => [key, this[key + 'OpacityLocal']])
+      return Object.keys(OPACITIES)
+        .map(key => console.log(key) || [key, this[key + 'OpacityLocal']])
         .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
     },
     currentRadii () {
diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js
index c727a9feac278fe3b0eaf2e9a59f3eb1bfa52950..6b228a5808357d4b40efcb3c6c2696aee65d6e87 100644
--- a/src/services/color_convert/color_convert.js
+++ b/src/services/color_convert/color_convert.js
@@ -159,7 +159,7 @@ export const hex2rgb = (hex) => {
  * @returns {Object} result
  */
 export const mixrgb = (a, b) => {
-  return Object.keys(a).reduce((acc, k) => {
+  return 'rgb'.split('').reduce((acc, k) => {
     acc[k] = (a[k] + b[k]) / 2
     return acc
   }, {})
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index c1a25101c0363df2745563c8b4f11219c13c3366..9237a8dc633536f66ed8cfd04247d1d526815136 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -1,7 +1,7 @@
 import { times } from 'lodash'
 import { convert } from 'chromatism'
 import { rgb2hex, hex2rgb, rgba2css, getCssColor } from '../color_convert/color_convert.js'
-import { getColors, DEFAULT_OPACITY } from '../theme_data/theme_data.service.js'
+import { getColors } from '../theme_data/theme_data.service.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
@@ -115,76 +115,12 @@ const getCssShadowFilter = (input) => {
 }
 
 export const generateColors = (themeData) => {
-  const rawOpacity = Object.assign({ ...DEFAULT_OPACITY }, Object.entries(themeData.opacity || {}).reduce((acc, [k, v]) => {
-    if (typeof v !== 'undefined') {
-      acc[k] = v
-    }
-    return acc
-  }, {}))
-
-  const inputColors = themeData.colors || themeData
-
-  const opacity = {
-    ...rawOpacity,
-    ...Object.entries(inputColors).reduce((acc, [k, v]) => {
-      if (v === 'transparent') {
-        acc[k] = 0
-      }
-      return acc
-    }, {})
-  }
-
-  // Cycle one: just whatever we have
-  const sourceColors = Object.entries(inputColors).reduce((acc, [k, v]) => {
-    if (typeof v === 'object') {
-      acc[k] = v
-    } else {
-      let value = v
-      if (v === 'transparent') {
-        // TODO: hack to keep rest of the code from complaining
-        value = '#FF00FF'
-      }
-      if (!value || value.startsWith('--')) {
-        acc[k] = value
-      } else {
-        acc[k] = hex2rgb(value)
-      }
-    }
-    return acc
-  }, {})
+  const sourceColors = themeData.colors || themeData
 
   const isLightOnDark = convert(sourceColors.bg).hsl.l < convert(sourceColors.text).hsl.l
   const mod = isLightOnDark ? 1 : -1
 
-  const colors = getColors(sourceColors, opacity, mod)
-
-  // Inheriting opacities
-  Object.entries(opacity).forEach(([ k, v ]) => {
-    if (typeof v === 'undefined') return
-    if (k === 'alert') {
-      colors.alertError.a = v
-      colors.alertWarning.a = v
-      return
-    }
-    if (k === 'faint') {
-      colors['faintLink'].a = v
-      colors['panelFaint'].a = v
-      colors['lightBgFaintText'].a = v
-      colors['lightBgFaintLink'].a = v
-    }
-    if (k === 'bg') {
-      colors['lightBg'].a = v
-    }
-    if (k === 'badge') {
-      colors['badgeNotification'].a = v
-      return
-    }
-    if (colors[k]) {
-      colors[k].a = v
-    } else {
-      console.error('Wrong key ' + k)
-    }
-  })
+  const { colors, opacity } = getColors(sourceColors, themeData.opacity || {}, mod)
 
   const htmlColors = Object.entries(colors)
     .reduce((acc, [k, v]) => {
diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js
index a345d9961ad308ee0bcfb9753f63d60a00bc1bb3..e76d70ed14fd4a3477b955beaa20cb84329ac907 100644
--- a/src/services/theme_data/theme_data.service.js
+++ b/src/services/theme_data/theme_data.service.js
@@ -26,24 +26,17 @@ export const LAYERS = {
 }
 
 export const DEFAULT_OPACITY = {
-  panel: 1,
-  btn: 1,
-  border: 1,
-  bg: 1,
-  badge: 1,
-  text: 1,
   alert: 0.5,
   input: 0.5,
   faint: 0.5,
-  underlay: 0.15,
-  poll: 1,
-  topBar: 1
+  underlay: 0.15
 }
 
 export const SLOT_INHERITANCE = {
   bg: {
     depends: [],
-    priority: 1
+    priority: 1,
+    opacity: 'bg'
   },
   fg: {
     depends: [],
@@ -53,7 +46,10 @@ export const SLOT_INHERITANCE = {
     depends: [],
     priority: 1
   },
-  underlay: '#000000',
+  underlay: {
+    default: '#000000',
+    opacity: 'underlay'
+  },
   link: {
     depends: ['accent'],
     priority: 1
@@ -62,8 +58,14 @@ export const SLOT_INHERITANCE = {
     depends: ['link'],
     priority: 1
   },
-  faint: '--text',
-  faintLink: '--link',
+  faint: {
+    depends: ['text'],
+    opacity: 'faint'
+  },
+  faintLink: {
+    depends: ['link'],
+    opacity: 'faint'
+  },
 
   cBlue: '#0000ff',
   cRed: '#FF0000',
@@ -158,11 +160,13 @@ export const SLOT_INHERITANCE = {
 
   border: {
     depends: ['fg'],
+    opacity: 'border',
     color: (mod, fg) => brightness(2 * mod, fg).rgb
   },
 
   poll: {
     depends: ['accent', 'bg'],
+    copacity: 'poll',
     color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg)
   },
   pollText: {
@@ -173,6 +177,7 @@ export const SLOT_INHERITANCE = {
 
   icon: {
     depends: ['bg', 'text'],
+    inheritsOpacity: false,
     color: (mod, bg, text) => mixrgb(bg, text)
   },
 
@@ -189,7 +194,10 @@ export const SLOT_INHERITANCE = {
   },
 
   // Panel header
-  panel: '--fg',
+  panel: {
+    depends: ['fg'],
+    opacity: 'panel'
+  },
   panelText: {
     depends: ['fgText'],
     layer: 'panel',
@@ -198,6 +206,7 @@ export const SLOT_INHERITANCE = {
   panelFaint: {
     depends: ['fgText'],
     layer: 'panel',
+    opacity: 'faint',
     textColor: true
   },
   panelLink: {
@@ -233,7 +242,10 @@ export const SLOT_INHERITANCE = {
   },
 
   // Buttons
-  btn: '--fg',
+  btn: {
+    depends: ['fg'],
+    opacity: 'btn'
+  },
   btnText: {
     depends: ['fgText'],
     layer: 'btn',
@@ -325,7 +337,10 @@ export const SLOT_INHERITANCE = {
   },
 
   // Input fields
-  input: '--fg',
+  input: {
+    depends: ['fg'],
+    opacity: 'input'
+  },
   inputText: {
     depends: ['text'],
     layer: 'input',
@@ -344,7 +359,10 @@ export const SLOT_INHERITANCE = {
     textColor: true
   },
 
-  alertError: '--cRed',
+  alertError: {
+    depends: ['cRed'],
+    opacity: 'alert'
+  },
   alertErrorText: {
     depends: ['text'],
     layer: 'alert',
@@ -358,7 +376,10 @@ export const SLOT_INHERITANCE = {
     textColor: true
   },
 
-  alertWarning: '--cOrange',
+  alertWarning: {
+    depends: ['cOrange'],
+    opacity: 'alert'
+  },
   alertWarningText: {
     depends: ['text'],
     layer: 'alert',
@@ -465,78 +486,144 @@ export const topoSort = (
   return output
 }
 
+export const getOpacitySlot = (
+  v,
+  inheritance = SLOT_INHERITANCE,
+  getDeps = getDependencies
+) => {
+  if (v.opacity === null) return
+  if (v.opacity) return v.opacity
+  const findInheritedOpacity = (val) => {
+    const depSlot = val.depends[0]
+    if (depSlot === undefined) return
+    const dependency = getDeps(depSlot, inheritance)[0]
+    if (dependency === undefined) return
+    if (dependency.opacity || dependency === null) {
+      return dependency.opacity
+    } else if (dependency.depends) {
+      return findInheritedOpacity(dependency)
+    } else {
+      return null
+    }
+  }
+  if (v.depends) {
+    return findInheritedOpacity(v)
+  }
+}
+
 export const SLOT_ORDERED = topoSort(
   Object.entries(SLOT_INHERITANCE)
     .sort(([aK, aV], [bK, bV]) => ((aV && aV.priority) || 0) - ((bV && bV.priority) || 0))
     .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
 )
 
-console.log(SLOT_ORDERED)
+export const SLOTS_OPACITIES_DICT = Object.entries(SLOT_INHERITANCE).reduce((acc, [k, v]) => {
+  const opacity = getOpacitySlot(v, SLOT_INHERITANCE, getDependencies)
+  if (opacity) {
+    return { ...acc, [k]: opacity }
+  } else {
+    return acc
+  }
+}, {})
 
-export const getColors = (sourceColors, sourceOpacity, mod) => SLOT_ORDERED.reduce((acc, key) => {
+export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k, v]) => {
+  const opacity = getOpacitySlot(v, SLOT_INHERITANCE, getDependencies)
+  if (opacity) {
+    return {
+      ...acc,
+      [opacity]: {
+        defaultValue: DEFAULT_OPACITY[opacity] || 1,
+        affectedSlots: [...((acc[opacity] && acc[opacity].affectedSlots) || []), k]
+      }
+    }
+  } else {
+    return acc
+  }
+}, {})
+
+export const getColors = (sourceColors, sourceOpacity, mod) => SLOT_ORDERED.reduce(({ colors, opacity }, key) => {
   const value = SLOT_INHERITANCE[key]
+  const isObject = typeof value === 'object'
+  const isString = typeof value === 'string'
   const sourceColor = sourceColors[key]
+  let outputColor = null
   if (sourceColor) {
+    // Color is defined in source color
     let targetColor = sourceColor
-    if (typeof sourceColor === 'string' && sourceColor.startsWith('--')) {
+    if (targetColor === 'transparent') {
+      targetColor = {
+        // TODO: try to use alpha-blended background here
+        ...convert('#FF00FF').rgb,
+        a: 0
+      }
+    } else if (typeof sourceColor === 'string' && sourceColor.startsWith('--')) {
+      // Color references other color
       const [variable, modifier] = sourceColor.split(/,/g).map(str => str.trim())
       const variableSlot = variable.substring(2)
-      targetColor = acc[variableSlot] || sourceColors[variableSlot]
+      targetColor = colors[variableSlot] || sourceColors[variableSlot]
       if (modifier) {
-        console.log(targetColor, acc, variableSlot)
         targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
       }
-      console.log(targetColor, acc, variableSlot)
+    } else if (typeof sourceColor === 'string' && sourceColor.startsWith('#')) {
+      targetColor = convert(targetColor).rgb
     }
-    return { ...acc, [key]: { ...targetColor } }
-  } else if (typeof value === 'string' && value.startsWith('#')) {
-    return { ...acc, [key]: convert(value).rgb }
+    outputColor = { ...targetColor }
+  } else if (isString && value.startsWith('#')) {
+    // slot: '#000000' shorthand
+    outputColor = convert(value).rgb
+  } else if (isObject && value.default) {
+    // same as above except in object form
+    outputColor = convert(value.default).rgb
   } else {
-    const isObject = typeof value === 'object'
+    // calculate color
     const defaultColorFunc = (mod, dep) => ({ ...dep })
     const deps = getDependencies(key, SLOT_INHERITANCE)
     const colorFunc = (isObject && value.color) || defaultColorFunc
 
     if (value.textColor) {
+      // textColor case
       const bg = alphaBlendLayers(
-        { ...acc[deps[0]] },
+        { ...colors[deps[0]] },
         getLayers(
           value.layer,
           value.variant || value.layer,
-          acc,
-          sourceOpacity
+          colors,
+          opacity
         )
       )
       if (value.textColor === 'bw') {
-        return {
-          ...acc,
-          [key]: contrastRatio(bg).rgb
-        }
+        outputColor = contrastRatio(bg).rgb
       } else {
-        let color = { ...acc[deps[0]] }
+        let color = { ...colors[deps[0]] }
         if (value.color) {
           const isLightOnDark = convert(bg).hsl.l < convert(color).hsl.l
           const mod = isLightOnDark ? 1 : -1
-          color = value.color(mod, ...deps.map((dep) => ({ ...acc[dep] })))
+          color = value.color(mod, ...deps.map((dep) => ({ ...colors[dep] })))
         }
 
-        return {
-          ...acc,
-          [key]: getTextColor(
-            bg,
-            { ...color },
-            value.textColor === 'preserve'
-          )
-        }
-      }
-    } else {
-      return {
-        ...acc,
-        [key]: colorFunc(
-          mod,
-          ...deps.map((dep) => ({ ...acc[dep] }))
+        outputColor = getTextColor(
+          bg,
+          { ...color },
+          value.textColor === 'preserve'
         )
       }
+    } else {
+      // background color case
+      outputColor = colorFunc(
+        mod,
+        ...deps.map((dep) => ({ ...colors[dep] }))
+      )
     }
   }
-}, {})
+  if (!outputColor) {
+    throw new Error('Couldn\'t generate color for ' + key)
+  }
+  const opacitySlot = SLOTS_OPACITIES_DICT[key]
+  if (opacitySlot && outputColor.a === undefined) {
+    outputColor.a = sourceOpacity[opacitySlot] || OPACITIES[opacitySlot].defaultValue || 1
+  }
+  return {
+    colors: { ...colors, [key]: outputColor },
+    opacity: { ...opacity, [opacitySlot]: outputColor.a }
+  }
+}, { colors: {}, opacity: {} })