diff --git a/include/components/builder.hpp b/include/components/builder.hpp
index 8083005d..c64c094c 100644
--- a/include/components/builder.hpp
+++ b/include/components/builder.hpp
@@ -58,6 +58,10 @@ class builder {
 
   void line_color(string color);
   void line_color_close(bool force = false);
+  void overline_color(string color);
+  void overline_color_close(bool force = false);
+  void underline_color(string color);
+  void underline_color_close(bool force = false);
 
   void overline(string color = "");
   void overline_close(bool force = false);
@@ -85,6 +89,8 @@ class builder {
       {syntaxtag::F, 0},
       {syntaxtag::T, 0},
       {syntaxtag::U, 0},
+      {syntaxtag::Uo, 0},
+      {syntaxtag::Uu, 0},
       {syntaxtag::O, 0},
       {syntaxtag::R, 0},
       // clang-format on
@@ -95,6 +101,8 @@ class builder {
       {syntaxtag::B, ""},
       {syntaxtag::F, ""},
       {syntaxtag::U, ""},
+      {syntaxtag::Uu, ""},
+      {syntaxtag::Uo, ""},
       // clang-format on
   };
 
diff --git a/include/components/types.hpp b/include/components/types.hpp
index 3108a985..091a514d 100644
--- a/include/components/types.hpp
+++ b/include/components/types.hpp
@@ -8,7 +8,7 @@ POLYBAR_NS
 
 enum class edge : uint8_t { NONE = 0, TOP, BOTTOM, LEFT, RIGHT, ALL };
 enum class alignment : uint8_t { NONE = 0, LEFT, CENTER, RIGHT };
-enum class syntaxtag : uint8_t { NONE = 0, A, B, F, T, U, O, R, o, u };
+enum class syntaxtag : uint8_t { NONE = 0, A, B, F, T, U, Uu, Uo, O, R, o, u };
 enum class attribute : uint8_t { NONE = 0, o = 1 << 0, u = 1 << 1 };
 enum class mousebtn : uint8_t { NONE = 0, LEFT, MIDDLE, RIGHT, SCROLL_UP, SCROLL_DOWN };
 enum class gc : uint8_t { NONE = 0, BG, FG, OL, UL, BT, BB, BL, BR };
@@ -54,6 +54,11 @@ struct border_settings {
   uint16_t size{0};
 };
 
+struct line_settings {
+  uint32_t color{0xFF000000};
+  uint16_t size{0};
+};
+
 struct bar_settings {
   monitor_t monitor;
 
@@ -70,11 +75,12 @@ struct bar_settings {
 
   uint32_t background{0xFFFFFFFF};
   uint32_t foreground{0xFF0000FF};
-  uint32_t linecolor{0xFF000000};
+
+  line_settings underline;
+  line_settings overline;
 
   map<edge, border_settings> borders;
 
-  int8_t lineheight{0};
   int8_t spacing{1};
   string separator;
 
diff --git a/include/x11/winspec.hpp b/include/x11/winspec.hpp
index 68ef431e..c1298923 100644
--- a/include/x11/winspec.hpp
+++ b/include/x11/winspec.hpp
@@ -116,16 +116,21 @@ class winspec {
   }
 
   xcb_window_t operator<<(cw_flush f) {
-    if (m_window == XCB_NONE) {
+    if (!m_window) {
       m_window = m_connection.generate_id();
     }
 
+    uint32_t values[16]{0};
+    if (m_params != nullptr) {
+      xutils::pack_values(m_mask, m_params, values);
+    }
+
     if (f.checked) {
       m_connection.create_window_checked(
-          m_depth, m_window, m_parent, m_x, m_y, m_width, m_height, m_border, m_class, m_visual, m_mask, m_params);
+          m_depth, m_window, m_parent, m_x, m_y, m_width, m_height, m_border, m_class, m_visual, m_mask, values);
     } else {
       m_connection.create_window(
-          m_depth, m_window, m_parent, m_x, m_y, m_width, m_height, m_border, m_class, m_visual, m_mask, m_params);
+          m_depth, m_window, m_parent, m_x, m_y, m_width, m_height, m_border, m_class, m_visual, m_mask, values);
     }
 
     return m_window;
@@ -134,18 +139,17 @@ class winspec {
  protected:
   connection& m_connection;
   uint32_t m_window;
-
-  uint8_t m_depth;
-  xcb_window_t m_parent;
+  uint32_t m_parent;
   int16_t m_x;
   int16_t m_y;
   uint16_t m_width;
   uint16_t m_height;
-  uint16_t m_border;
-  uint16_t m_class;
+  uint8_t m_depth{XCB_COPY_FROM_PARENT};
+  uint16_t m_border{XCB_COPY_FROM_PARENT};
+  uint16_t m_class{XCB_COPY_FROM_PARENT};
   xcb_visualid_t m_visual;
   uint32_t m_mask;
-  const xcb_params_cw_t* m_params;
+  const xcb_params_cw_t* m_params{nullptr};
 };
 
 POLYBAR_NS_END
diff --git a/src/components/bar.cpp b/src/components/bar.cpp
index e092083d..fb4cbe0b 100644
--- a/src/components/bar.cpp
+++ b/src/components/bar.cpp
@@ -8,14 +8,10 @@
 #include "utils/color.hpp"
 #include "utils/math.hpp"
 #include "utils/string.hpp"
-#include "x11/draw.hpp"
-#include "x11/draw.hpp"
 #include "x11/fonts.hpp"
 #include "x11/graphics.hpp"
 #include "x11/tray.hpp"
 #include "x11/wm.hpp"
-#include "x11/xlib.hpp"
-#include "x11/xutils.hpp"
 
 #if ENABLE_I3
 #include "utils/i3.hpp"
@@ -94,7 +90,23 @@ void bar::bootstrap(bool nodraw) {
 
   m_opts.background = color::parse(m_conf.get<string>(bs, "background", color_util::hex<uint16_t>(m_opts.background)));
   m_opts.foreground = color::parse(m_conf.get<string>(bs, "foreground", color_util::hex<uint16_t>(m_opts.foreground)));
-  m_opts.linecolor = color::parse(m_conf.get<string>(bs, "linecolor", color_util::hex<uint16_t>(m_opts.linecolor)));
+
+  auto linecolor = color::parse(m_conf.get<string>(bs, "linecolor", "#f00"));
+  auto lineheight = m_conf.get<int>(bs, "lineheight", 0);
+
+  try {
+    m_opts.overline.size = m_conf.get<int16_t>(bs, "overline-size", lineheight);
+    m_opts.overline.color = color::parse(m_conf.get<string>(bs, "overline-color"));
+  } catch (const key_error& err) {
+    m_opts.overline.color = linecolor;
+  }
+
+  try {
+    m_opts.underline.size = m_conf.get<uint16_t>(bs, "underline-size", lineheight);
+    m_opts.underline.color = color::parse(m_conf.get<string>(bs, "underline-color"));
+  } catch (const key_error& err) {
+    m_opts.underline.color = linecolor;
+  }
 
   // }}}
   // Set border values {{{
@@ -128,7 +140,6 @@ void bar::bootstrap(bool nodraw) {
 
   GET_CONFIG_VALUE(bs, m_opts.force_docking, "dock");
   GET_CONFIG_VALUE(bs, m_opts.spacing, "spacing");
-  GET_CONFIG_VALUE(bs, m_opts.lineheight, "lineheight");
   GET_CONFIG_VALUE(bs, m_opts.padding.left, "padding-left");
   GET_CONFIG_VALUE(bs, m_opts.padding.right, "padding-right");
   GET_CONFIG_VALUE(bs, m_opts.module_margin.left, "module-margin-left");
diff --git a/src/components/builder.cpp b/src/components/builder.cpp
index e3c3c7a2..af3ea064 100644
--- a/src/components/builder.cpp
+++ b/src/components/builder.cpp
@@ -16,6 +16,8 @@ string builder::flush() {
     while (m_counters[syntaxtag::B] > 0) background_close(true);
     while (m_counters[syntaxtag::F] > 0) color_close(true);
     while (m_counters[syntaxtag::T] > 0) font_close(true);
+    while (m_counters[syntaxtag::Uo] > 0) overline_color_close(true);
+    while (m_counters[syntaxtag::Uu] > 0) underline_color_close(true);
     while (m_counters[syntaxtag::U] > 0) line_color_close(true);
     while (m_counters[syntaxtag::u] > 0) underline_close(true);
     while (m_counters[syntaxtag::o] > 0) overline_close(true);
@@ -80,6 +82,22 @@ void builder::node(string str, bool add_space) {
       line_color_close(true);
       s.erase(0, 5);
 
+    } else if ((n = s.find("%{Uu-}")) == 0) {
+      underline_color_close(true);
+      s.erase(0, 5);
+
+    } else if ((n = s.find("%{Uo-}")) == 0) {
+      overline_color_close(true);
+      s.erase(0, 5);
+
+    } else if ((n = s.find("%{Uu#")) == 0 && (m = s.find("}")) != string::npos) {
+      underline_color(s.substr(n + 4, m - 4));
+      s.erase(n, m + 1);
+
+    } else if ((n = s.find("%{Uo#")) == 0 && (m = s.find("}")) != string::npos) {
+      overline_color(s.substr(n + 4, m - 4));
+      s.erase(n, m + 1);
+
     } else if ((n = s.find("%{U#")) == 0 && (m = s.find("}")) != string::npos) {
       line_color(s.substr(n + 3, m - 3));
       s.erase(n, m + 1);
@@ -347,9 +365,53 @@ void builder::line_color_close(bool force) {
   tag_close('U');
 }
 
+void builder::overline_color(string color) {
+  if (color.empty() && m_counters[syntaxtag::Uo] > 0)
+    overline_color_close(true);
+  if (color.empty() || color == m_colors[syntaxtag::Uo])
+    return;
+  if (m_lazy && m_counters[syntaxtag::Uo] > 0)
+    overline_color_close(true);
+
+  m_counters[syntaxtag::Uo]++;
+  m_colors[syntaxtag::Uo] = color;
+  append("%{Uo" + color + "}");
+}
+
+void builder::overline_color_close(bool force) {
+  if ((!force && m_lazy) || m_counters[syntaxtag::Uo] <= 0)
+    return;
+
+  m_counters[syntaxtag::Uo]--;
+  m_colors[syntaxtag::Uo] = "";
+  append("%{Uu-}");
+}
+
+void builder::underline_color(string color) {
+  if (color.empty() && m_counters[syntaxtag::Uu] > 0)
+    underline_color_close(true);
+  if (color.empty() || color == m_colors[syntaxtag::Uu])
+    return;
+  if (m_lazy && m_counters[syntaxtag::Uu] > 0)
+    underline_color_close(true);
+
+  m_counters[syntaxtag::Uu]++;
+  m_colors[syntaxtag::Uu] = color;
+  append("%{Uu" + color + "}");
+}
+
+void builder::underline_color_close(bool force) {
+  if ((!force && m_lazy) || m_counters[syntaxtag::Uu] <= 0)
+    return;
+
+  m_counters[syntaxtag::Uu]--;
+  m_colors[syntaxtag::Uu] = "";
+  append("%{Uu-}");
+}
+
 void builder::overline(string color) {
   if (!color.empty())
-    line_color(color);
+    overline_color(color);
   if (m_counters[syntaxtag::o] > 0)
     return;
 
@@ -367,7 +429,7 @@ void builder::overline_close(bool force) {
 
 void builder::underline(string color) {
   if (!color.empty())
-    line_color(color);
+    underline_color(color);
   if (m_counters[syntaxtag::u] > 0)
     return;
 
diff --git a/src/components/parser.cpp b/src/components/parser.cpp
index 567373c7..cb25368b 100644
--- a/src/components/parser.cpp
+++ b/src/components/parser.cpp
@@ -63,10 +63,15 @@ void parser::codeblock(string data) {
         break;
 
       case 'U':
-        // Ignore tag if it occurs again later in the same block
-        if (data.find(" U") == string::npos && g_signals::parser::color_change) {
-          g_signals::parser::color_change(gc::UL, parse_color(value, m_bar.linecolor));
-          g_signals::parser::color_change(gc::OL, parse_color(value, m_bar.linecolor));
+        if (g_signals::parser::color_change) {
+          if (value[0] == 'u' && data.find(" Uu") == string::npos) {
+            g_signals::parser::color_change(gc::UL, parse_color(value.substr(1), m_bar.underline.color));
+          } else if (value[0] == 'o' && data.find(" Uo") == string::npos) {
+            g_signals::parser::color_change(gc::OL, parse_color(value.substr(1), m_bar.overline.color));
+          } else if (data.find(" U") == string::npos) {
+            g_signals::parser::color_change(gc::UL, parse_color(value, m_bar.underline.color));
+            g_signals::parser::color_change(gc::OL, parse_color(value, m_bar.overline.color));
+          }
         }
         break;
 
diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp
index 19728b5f..68083dbe 100644
--- a/src/components/renderer.cpp
+++ b/src/components/renderer.cpp
@@ -3,7 +3,8 @@
 #include "x11/connection.hpp"
 #include "x11/draw.hpp"
 #include "x11/fonts.hpp"
-#include "x11/winspec.hpp"
+#include "x11/xlib.hpp"
+#include "x11/xutils.hpp"
 
 POLYBAR_NS
 
@@ -64,8 +65,8 @@ renderer::renderer(connection& conn, const logger& logger, unique_ptr<font_manag
     vector<uint32_t> colors {
       m_bar.background,
       m_bar.foreground,
-      m_bar.linecolor,
-      m_bar.linecolor,
+      m_bar.overline.color,
+      m_bar.underline.color,
       m_bar.borders.at(edge::TOP).color,
       m_bar.borders.at(edge::BOTTOM).color,
       m_bar.borders.at(edge::LEFT).color,
@@ -278,19 +279,17 @@ void renderer::fill_border(const map<edge, border_settings>& borders, edge borde
 }
 
 void renderer::fill_overline(int16_t x, uint16_t w) {
-  if (!m_bar.lineheight || !(m_attributes & static_cast<int>(attribute::o)))
+  if (!m_bar.overline.size || !(m_attributes & static_cast<int>(attribute::o)))
     return;
-
-  draw_util::fill(
-      m_connection, m_pixmap, m_gcontexts.at(gc::OL), x, m_bar.borders.at(edge::TOP).size, w, m_bar.lineheight);
+  int16_t y{static_cast<int16_t>(m_bar.borders.at(edge::TOP).size)};
+  draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::OL), x, y, w, m_bar.overline.size);
 }
 
 void renderer::fill_underline(int16_t x, uint16_t w) {
-  if (!m_bar.lineheight || !(m_attributes & static_cast<int>(attribute::u)))
+  if (!m_bar.underline.size || !(m_attributes & static_cast<int>(attribute::u)))
     return;
-
-  draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::UL), x,
-      m_bar.size.h - m_bar.borders.at(edge::BOTTOM).size - m_bar.lineheight, w, m_bar.lineheight);
+  int16_t y{static_cast<int16_t>(m_bar.size.h - m_bar.borders.at(edge::BOTTOM).size - m_bar.underline.size)};
+  draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::UL), x, y, w, m_bar.underline.size);
 }
 
 void renderer::draw_character(uint16_t character) {