diff --git a/_debug_replacements.php b/_debug_replacements.php
index 555a99b..e393c6d 100644
--- a/_debug_replacements.php
+++ b/_debug_replacements.php
@@ -27,11 +27,19 @@ return [
 	'btn3' => '',
 	'btn4' => '',
 	'btn5' => 'Help',
+
 	'bm1' => '01,'.ord('y'),
 	'bm2' => '01,'.ord('n'),
 	'bm3' => '',
 	'bm4' => '',
 	'bm5' => '05',
+
+	'bc1' => '',
+	'bc2' => '',
+	'bc3' => '',
+	'bc4' => '',
+	'bc5' => '',
+
 	'button_count' => 5,
 	'labels_seq' => ESP_DEMO ? 'TESPTerm Web UI DemoOKCancelHelp' : 'TESPTerm local debugOKCancelHelp',
 	'want_all_fn' => '0',
@@ -86,6 +94,8 @@ return [
 	'default_fg' => '7',
 	'show_buttons' => '1',
 	'show_config_links' => '1',
+	'font_stack' => '',
+	'font_size' => '20',
 
 	'uart_baudrate' => 115200,
 	'uart_stopbits' => 1,
diff --git a/js/term/buttons.js b/js/term/buttons.js
index d9bd111..dac4a63 100644
--- a/js/term/buttons.js
+++ b/js/term/buttons.js
@@ -1,5 +1,6 @@
 const { getColor } = require('./themes')
 const { qs } = require('../utils')
+const { rgb2hsl, hex2rgb, rgb2hex, hsl2rgb } = require('../lib/color_utils')
 
 module.exports = function initButtons (input) {
   let container = qs('#action-buttons')
@@ -68,8 +69,19 @@ module.exports = function initButtons (input) {
       if (!label) button.classList.add('inactive')
       else button.classList.remove('inactive')
 
-      if (Number.isFinite(color)) button.style.background = getColor(color, palette)
-      else button.style.background = null
+      // 0 or undefined can be used to disable custom color
+      if (Number.isFinite(color) && color !== 0) {
+        const clr = getColor(color, palette)
+        button.style.background = clr
+
+        // darken the color a bit for the 3D side
+        const hsl = rgb2hsl(...hex2rgb(clr))
+        const hex = rgb2hex(...hsl2rgb(hsl[0], hsl[1], hsl[2] * 0.7))
+        button.style.boxShadow = `0 3px 0 ${hex}`
+      } else {
+        button.style.background = null
+        button.style.boxShadow = null
+      }
     }
   }
 
diff --git a/js/term/canvas_renderer.js b/js/term/canvas_renderer.js
index 15892d3..55dedba 100644
--- a/js/term/canvas_renderer.js
+++ b/js/term/canvas_renderer.js
@@ -61,6 +61,7 @@ module.exports = class CanvasRenderer extends EventEmitter {
     this.screenAttrs = []
     this.screenSelection = []
     this.cursor = {}
+    this.reverseVideo = false
     this.hasBlinkingCells = false
     this.statusScreen = null
 
@@ -540,7 +541,7 @@ module.exports = class CanvasRenderer extends EventEmitter {
       }
 
       if (attrs & ATTR_INVERSE) [fg, bg] = [bg, fg] // swap - reversed character colors
-      if (this.screen.reverseVideo) [fg, bg] = [bg, fg] // swap - reversed all screen
+      if (this.reverseVideo) [fg, bg] = [bg, fg] // swap - reversed all screen
 
       if (attrs & ATTR_BLINK && !this.blinkStyleOn) {
         // blinking is enabled and blink style is off
diff --git a/js/term/index.js b/js/term/index.js
index e883046..76f723a 100644
--- a/js/term/index.js
+++ b/js/term/index.js
@@ -49,7 +49,10 @@ module.exports = function (opts) {
 
   // buttons
   const buttons = initButtons(input)
-  screen.on('button-labels', labels => { buttons.labels = labels })
+  screen.on('buttons-update', update => {
+    buttons.labels = update.labels
+    buttons.colors = update.colors
+  })
   // TODO: don't access the renderer here
   buttons.palette = screen.layout.renderer.palette
   screen.layout.renderer.on('palette-update', palette => {
diff --git a/js/term/screen.js b/js/term/screen.js
index a092b72..25e5dc8 100644
--- a/js/term/screen.js
+++ b/js/term/screen.js
@@ -282,6 +282,7 @@ module.exports = class TermScreen extends EventEmitter {
       screenAttrs: this.screenAttrs,
       cursor: this.cursor,
       statusScreen: this.window.statusScreen,
+      reverseVideo: this.reverseVideo,
       hasBlinkingCells: !!this.blinkingCellCount
     })
   }
@@ -475,6 +476,11 @@ module.exports = class TermScreen extends EventEmitter {
           this.emit('opts-update')
           break
 
+        case 'static-opts':
+          this.layout.window.fontFamily = update.fontStack || null
+          this.layout.window.fontSize = update.fontSize
+          break
+
         case 'cursor':
           if (this.cursor.x !== update.x || this.cursor.y !== update.y || this.cursor.hanging !== update.hanging) {
             this.cursor.x = update.x
@@ -490,8 +496,8 @@ module.exports = class TermScreen extends EventEmitter {
           this.emit('title-update', this.title = update.title)
           break
 
-        case 'button-labels':
-          this.emit('button-labels', update.labels)
+        case 'buttons-update':
+          this.emit('buttons-update', update)
           break
 
         case 'backdrop':
diff --git a/js/term/screen_layout.js b/js/term/screen_layout.js
index 677ed87..277fd1c 100644
--- a/js/term/screen_layout.js
+++ b/js/term/screen_layout.js
@@ -2,6 +2,8 @@ const EventEmitter = require('events')
 const CanvasRenderer = require('./canvas_renderer')
 const WebGLRenderer = require('./webgl_renderer')
 
+const DEFAULT_FONT = '"DejaVu Sans Mono", "Liberation Mono", "Inconsolata", "Menlo", monospace'
+
 /**
  * Manages terminal screen layout and sizing
  */
@@ -21,7 +23,7 @@ module.exports = class ScreenLayout extends EventEmitter {
       width: 0,
       height: 0,
       devicePixelRatio: 1,
-      fontFamily: '"DejaVu Sans Mono", "Liberation Mono", "Inconsolata", "Menlo", monospace',
+      fontFamily: DEFAULT_FONT,
       fontSize: 20,
       padding: 6,
       gridScaleX: 1.0,
@@ -121,7 +123,10 @@ module.exports = class ScreenLayout extends EventEmitter {
   getFont (modifiers = {}) {
     let fontStyle = modifiers.style || 'normal'
     let fontWeight = modifiers.weight || 'normal'
-    return `${fontStyle} normal ${fontWeight} ${this.window.fontSize}px ${this.window.fontFamily}`
+    let fontFamily = this.window.fontFamily || ''
+    if (fontFamily.length > 0) fontFamily += ','
+    fontFamily += DEFAULT_FONT
+    return `${fontStyle} normal ${fontWeight} ${this.window.fontSize}px ${fontFamily}`
   }
 
   /**
diff --git a/js/term/screen_parser.js b/js/term/screen_parser.js
index bf19e3f..c3a8546 100644
--- a/js/term/screen_parser.js
+++ b/js/term/screen_parser.js
@@ -28,6 +28,7 @@ function du (str) {
 
 /* eslint-disable no-multi-spaces */
 const TOPIC_SCREEN_OPTS  = 'O'
+const TOPIC_STATIC_OPTS  = 'P'
 const TOPIC_CONTENT      = 'S'
 const TOPIC_TITLE        = 'T'
 const TOPIC_BUTTONS      = 'B'
@@ -85,6 +86,16 @@ module.exports = class ScreenParser {
       return text
     }
 
+    let collectColor = () => {
+      let c = du(strArray[ci++])
+      if (c & 0x10000) { // support for trueColor
+        c &= 0xFFF
+        c |= (du(strArray[ci++]) & 0xFFF) << 12
+        c += 256
+      }
+      return c
+    }
+
     const updates = []
 
     while (ci < strArray.length) {
@@ -94,8 +105,8 @@ module.exports = class ScreenParser {
         const height = du(strArray[ci++])
         const width = du(strArray[ci++])
         const theme = du(strArray[ci++])
-        const defFG = (du(strArray[ci++]) & 0xFFFF) | ((du(strArray[ci++]) & 0xFFFF) << 16)
-        const defBG = (du(strArray[ci++]) & 0xFFFF) | ((du(strArray[ci++]) & 0xFFFF) << 16)
+        const defFG = collectColor()
+        const defBG = collectColor()
 
         // process attributes
         const attributes = du(strArray[ci++])
@@ -153,6 +164,7 @@ module.exports = class ScreenParser {
           reverseVideo,
           debugEnabled
         })
+
       } else if (topic === TOPIC_CURSOR) {
         // cursor position
         const y = du(strArray[ci++])
@@ -165,28 +177,44 @@ module.exports = class ScreenParser {
           y,
           hanging
         })
+
+      } else if (topic === TOPIC_STATIC_OPTS) {
+        const fontStack = collectOneTerminatedString()
+        const fontSize = du(strArray[ci++])
+
+        updates.push({
+          topic: 'static-opts',
+          fontStack,
+          fontSize
+        })
+
       } else if (topic === TOPIC_TITLE) {
         updates.push({ topic: 'title', title: collectOneTerminatedString() })
+
       } else if (topic === TOPIC_BUTTONS) {
         const count = du(strArray[ci++])
 
         let labels = []
+        let colors = []
         for (let j = 0; j < count; j++) {
-          text = collectOneTerminatedString()
-          labels.push(text)
+          colors.push(collectColor())
+          labels.push(collectOneTerminatedString())
         }
 
         updates.push({
-          topic: 'button-labels',
-          labels
+          topic: 'buttons-update',
+          labels,
+          colors
         })
+
       } else if (topic === TOPIC_BACKDROP) {
         updates.push({ topic: 'backdrop', image: collectOneTerminatedString() })
+
       } else if (topic === TOPIC_BELL) {
         updates.push({ topic: 'bell' })
+
       } else if (topic === TOPIC_INTERNAL) {
         // debug info
-
         const flags = du(strArray[ci++])
         const cursorAttrs = du(strArray[ci++])
         const regionStart = du(strArray[ci++])
@@ -194,6 +222,10 @@ module.exports = class ScreenParser {
         const charsetGx = du(strArray[ci++])
         const charsetG0 = strArray[ci++]
         const charsetG1 = strArray[ci++]
+
+        let cursorFg = collectColor()
+        let cursorBg = collectColor()
+
         const freeHeap = du(strArray[ci++])
         const clientCount = du(strArray[ci++])
 
@@ -206,9 +238,12 @@ module.exports = class ScreenParser {
           charsetGx,
           charsetG0,
           charsetG1,
+          cursorFg,
+          cursorBg,
           freeHeap,
           clientCount
         })
+
       } else if (topic === TOPIC_CONTENT) {
         // set screen content
         const frameY = du(strArray[ci++])
diff --git a/js/term/themes.js b/js/term/themes.js
index ea0feb8..6828beb 100644
--- a/js/term/themes.js
+++ b/js/term/themes.js
@@ -139,7 +139,7 @@ exports.getColor = function (i, palette = []) {
   }
 
   // return error color
-  return Math.floor(Date.now() / 1000) % 2 === 0 ? '#f0f' : '#0f0'
+  return Math.floor(Date.now() / 1000) % 2 === 0 ? '#ff0ff' : '#00ff00'
 }
 
 exports.toHex = function (shade, themeN) {
diff --git a/js/term_conf.js b/js/term_conf.js
index 87d9d70..af011ab 100644
--- a/js/term_conf.js
+++ b/js/term_conf.js
@@ -9,9 +9,7 @@ function selectedTheme () {
 
 exports.init = function () {
   $('#theme').on('change', showColor)
-
-  $('#default_fg').on('input', showColor)
-  $('#default_bg').on('input', showColor)
+  $('#default_fg,#default_bg').on('input', showColor)
 
   let opts = {
     padding: 10,
@@ -27,6 +25,9 @@ exports.init = function () {
 
   ColorTriangle.initInput(qs('#default_fg'), opts)
   ColorTriangle.initInput(qs('#default_bg'), opts)
+  for (let i = 1; i <= 5; i++) {
+    ColorTriangle.initInput(qs(`#bc${i}`), opts)
+  }
 
   $('.colorprev.bg span').on('click', function () {
     const bg = this.dataset.bg
diff --git a/lang/cs.php b/lang/cs.php
index f81a444..ae146bb 100644
--- a/lang/cs.php
+++ b/lang/cs.php
@@ -82,6 +82,9 @@ return [
 	'term.ascii_debug' => 'Ladění vstupních dat',
 	'term.backdrop' => 'URL obrázku na pozadí',
 	'term.button_count' => 'Počet tlačítek',
+	'term.button_colors' => 'Barvy tlačítek',
+	'term.font_stack' => 'Font',
+	'term.font_size' => 'Velikost písma',
 
 	'cursor.block_blink' => 'Blok, blikající',
     'cursor.block_steady' => 'Blok, stálý',
diff --git a/lang/en.php b/lang/en.php
index 6597a6c..b23b26f 100644
--- a/lang/en.php
+++ b/lang/en.php
@@ -81,6 +81,9 @@ return [
 	'term.ascii_debug' => 'Display control codes',
 	'term.backdrop' => 'Background image URL',
 	'term.button_count' => 'Button count',
+	'term.button_colors' => 'Button colors',
+	'term.font_stack' => 'Font stack',
+	'term.font_size' => 'Font size',
 
 	'cursor.block_blink' => 'Block, blinking',
     'cursor.block_steady' => 'Block, steady',
diff --git a/pages/cfg_term.php b/pages/cfg_term.php
index 228a37c..a26f29b 100644
--- a/pages/cfg_term.php
+++ b/pages/cfg_term.php
@@ -177,6 +177,15 @@
 		<input class="tiny" type="text" name="bm5" id="bm5" value="%h:bm5%">
 	</div>
 
+	<div class="Row">
+		<label><?= tr("term.button_colors") ?></label>
+		<input class="tiny" type="text" name="bc1" id="bc1" value="%h:bc1%">
+		<input class="tiny" type="text" name="bc2" id="bc2" value="%h:bc2%">
+		<input class="tiny" type="text" name="bc3" id="bc3" value="%h:bc3%">
+		<input class="tiny" type="text" name="bc4" id="bc4" value="%h:bc4%">
+		<input class="tiny" type="text" name="bc5" id="bc5" value="%h:bc5%">
+	</div>
+
 	<div class="Row">
 		<label for="backdrop"><?= tr('term.backdrop') ?></label>
 		<input type="text" name="backdrop" id="backdrop" value="%h:backdrop%" required>
@@ -262,6 +271,17 @@
 		<?= tr('term.explain_expert') ?>
 	</div>
 
+	<div class="Row">
+		<label for="font_stack"><?= tr('term.font_stack') ?></label>
+		<input type="text" name="font_stack" id="font_stack" value="%h:font_stack%" required>
+	</div>
+
+	<div class="Row">
+		<label for="font_size"><?= tr('term.font_size') ?><span class="mq-phone">&nbsp;(px)</span></label>
+		<input type="number" step=1 min=0 name="font_size" id="font_size" value="%font_size%" required>
+		<span class="mq-no-phone">&nbsp;px</span>
+	</div>
+
 	<div class="Row">
 		<label for="parser_tout_ms"><?= tr('term.parser_tout_ms') ?><span class="mq-phone">&nbsp;(ms)</span></label>
 		<input type="number" step=1 min=0 name="parser_tout_ms" id="parser_tout_ms" value="%parser_tout_ms%" required>
