Index: uspace/app/gfxdemo/gfxdemo.c
===================================================================
--- uspace/app/gfxdemo/gfxdemo.c	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/app/gfxdemo/gfxdemo.c	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -73,4 +73,16 @@
 static gfx_coord_t vpad;
 
+/** Determine if we are running in text mode.
+ *
+ * @param w Screen width
+ * @param h Screen height
+ * @return @c true iff we are running in text mode
+ */
+static bool demo_is_text(gfx_coord_t w, gfx_coord_t h)
+{
+	// XXX Need a proper way to determine text mode
+	return w <= 80;
+}
+
 /** Clear screen.
  *
@@ -215,7 +227,13 @@
 
 	if (font != NULL) {
-		rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
-		if (rc != EOK)
-			goto error;
+		if (demo_is_text(w, h)) {
+			rc = gfx_color_new_ega(0x1e, &color);
+			if (rc != EOK)
+				goto error;
+		} else {
+			rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
+			if (rc != EOK)
+				goto error;
+		}
 
 		gfx_text_fmt_init(&fmt);
@@ -740,8 +758,14 @@
 
 	for (i = 0; i < 8; i++) {
-		rc = gfx_color_new_rgb_i16((i & 4) ? 0xffff : 0,
-		    (i & 2) ? 0xffff : 0, (i & 1) ? 0xffff : 0, &color);
-		if (rc != EOK)
-			goto error;
+		if (demo_is_text(w, h)) {
+			rc = gfx_color_new_ega(i, &color);
+			if (rc != EOK)
+				goto error;
+		} else {
+			rc = gfx_color_new_rgb_i16((i & 4) ? 0xffff : 0,
+			    (i & 2) ? 0xffff : 0, (i & 1) ? 0xffff : 0, &color);
+			if (rc != EOK)
+				goto error;
+		}
 
 		fmt.color = color;
Index: uspace/lib/congfx/src/console.c
===================================================================
--- uspace/lib/congfx/src/console.c	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/congfx/src/console.c	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -75,4 +75,41 @@
 };
 
+/** Convert pixel value to charfield.
+ *
+ * On the bottom of this function lies a big big hack. In the absence
+ * of support for different color formats (FIX ME!), here's a single
+ * format that can represent both 3x8bit RGB and 24-bit characters
+ * with 8-bit EGA attributes (i.e. we can specify the foreground and
+ * background colors individually).
+ *
+ * A    R   G   B
+ * 0    red grn blu  24-bit color
+ * attr c2  c1  c0   attribute + 24-bit character
+ */
+static void console_gc_pix_to_charfield(pixel_t clr, charfield_t *ch)
+{
+	uint8_t attr;
+
+	if ((clr >> 24) == 0) {
+		/* RGB (no text) */
+		ch->ch = 0;
+		ch->flags = CHAR_FLAG_DIRTY;
+		ch->attrs.type = CHAR_ATTR_RGB;
+		ch->attrs.val.rgb.fgcolor = clr ^ 0xffffff;
+		ch->attrs.val.rgb.bgcolor = clr;
+	} else {
+		/* EGA attributes (with text) */
+		attr = clr >> 24;
+		ch->ch = clr & 0xffffff;
+		ch->flags = CHAR_FLAG_DIRTY;
+		ch->attrs.type = CHAR_ATTR_INDEX;
+		ch->attrs.val.index.fgcolor = attr & 0x7;
+		ch->attrs.val.index.bgcolor = (attr >> 4) & 0x7;
+		ch->attrs.val.index.attr =
+		    ((attr & 0x8) ? CATTR_BRIGHT : 0) +
+		    ((attr & 0x80) ? CATTR_BLINK : 0);
+	}
+}
+
 /** Set clipping rectangle on console GC.
  *
@@ -107,5 +144,5 @@
 	console_gc_t *cgc = (console_gc_t *) arg;
 
-	cgc->clr = PIXEL(0, color->r >> 8, color->g >> 8, color->b >> 8);
+	cgc->clr = PIXEL(color->attr, color->r >> 8, color->g >> 8, color->b >> 8);
 	return EOK;
 }
@@ -131,9 +168,5 @@
 	cols = cgc->rect.p1.x - cgc->rect.p0.x;
 
-	ch.ch = cgc->clr >> 24;
-	ch.flags = CHAR_FLAG_DIRTY;
-	ch.attrs.type = CHAR_ATTR_RGB;
-	ch.attrs.val.rgb.fgcolor = cgc->clr ^ 0xffffff;
-	ch.attrs.val.rgb.bgcolor = cgc->clr;
+	console_gc_pix_to_charfield(cgc->clr, &ch);
 
 	for (y = crect.p0.y; y < crect.p1.y; y++) {
@@ -374,10 +407,5 @@
 				    y - offs.y - cbm->rect.p0.y);
 
-				ch.ch = clr >> 24;
-				ch.flags = CHAR_FLAG_DIRTY;
-				ch.attrs.type = CHAR_ATTR_RGB;
-				ch.attrs.val.rgb.fgcolor = clr ^ 0xffffff;
-				ch.attrs.val.rgb.bgcolor = clr;
-
+				console_gc_pix_to_charfield(clr, &ch);
 				cbm->cgc->buf[y * cols + x] = ch;
 			}
@@ -392,9 +420,5 @@
 				    y - offs.y - cbm->rect.p0.y);
 
-				ch.ch = clr >> 24;
-				ch.flags = CHAR_FLAG_DIRTY;
-				ch.attrs.type = CHAR_ATTR_RGB;
-				ch.attrs.val.rgb.fgcolor = clr ^ 0xffffff;
-				ch.attrs.val.rgb.bgcolor = clr;
+				console_gc_pix_to_charfield(clr, &ch);
 
 				if (clr != cbm->key_color)
@@ -404,9 +428,5 @@
 	} else {
 		/* Color key & colorize */
-		ch.ch = 0;
-		ch.flags = CHAR_FLAG_DIRTY;
-		ch.attrs.type = CHAR_ATTR_RGB;
-		ch.attrs.val.rgb.fgcolor = cbm->cgc->clr;
-		ch.attrs.val.rgb.bgcolor = cbm->cgc->clr;
+		console_gc_pix_to_charfield(cbm->cgc->clr, &ch);
 
 		for (y = crect.p0.y; y < crect.p1.y; y++) {
Index: uspace/lib/gfx/include/gfx/color.h
===================================================================
--- uspace/lib/gfx/include/gfx/color.h	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/gfx/include/gfx/color.h	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -43,7 +43,9 @@
 extern errno_t gfx_color_new_rgb_i16(uint16_t, uint16_t,
     uint16_t, gfx_color_t **);
+extern errno_t gfx_color_new_ega(uint8_t, gfx_color_t **);
 extern void gfx_color_delete(gfx_color_t *);
 extern void gfx_color_get_rgb_i16(gfx_color_t *, uint16_t *, uint16_t *,
     uint16_t *);
+extern void gfx_color_get_ega(gfx_color_t *, uint8_t *);
 
 #endif
Index: uspace/lib/gfx/private/color.h
===================================================================
--- uspace/lib/gfx/private/color.h	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/gfx/private/color.h	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,4 +48,5 @@
 	uint16_t g;
 	uint16_t b;
+	uint8_t attr;
 };
 
Index: uspace/lib/gfx/src/color.c
===================================================================
--- uspace/lib/gfx/src/color.c	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/gfx/src/color.c	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -69,4 +69,26 @@
 }
 
+/** Create new EGA color.
+ *
+ * @param attr EGA attributes
+ * @param rcolor Place to store pointer to new color
+ *
+ * @return EOK on success or an error code, ENOMEM if out of resources,
+ *         EIO if the graphic device connection was lost
+ */
+errno_t gfx_color_new_ega(uint8_t attr, gfx_color_t **rcolor)
+{
+	gfx_color_t *color;
+
+	color = calloc(1, sizeof(gfx_color_t));
+	if (color == NULL)
+		return ENOMEM;
+
+	color->attr = attr;
+
+	*rcolor = color;
+	return EOK;
+}
+
 /** Delete color.
  *
@@ -93,4 +115,14 @@
 }
 
+/** Convert color to EGA attributes.
+ *
+ * @param color Color
+ * @param attr Place to store EGA attributes
+ */
+void gfx_color_get_ega(gfx_color_t *color, uint8_t *attr)
+{
+	*attr = color->attr;
+}
+
 /** @}
  */
Index: uspace/lib/gfxfont/src/text.c
===================================================================
--- uspace/lib/gfxfont/src/text.c	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/gfxfont/src/text.c	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -110,8 +110,10 @@
 	gfx_bitmap_t *bitmap;
 	gfx_bitmap_alloc_t alloc;
-	uint16_t r, g, b;
+	uint8_t attr;
 	pixelmap_t pmap;
 	gfx_coord_t x;
 	pixel_t pixel;
+	char32_t c;
+	size_t off;
 	errno_t rc;
 
@@ -121,13 +123,5 @@
 	 */
 
-	gfx_color_get_rgb_i16(color, &r, &g, &b);
-
-	/*
-	 * We are setting the *background* color, the foreground color
-	 * will be set to its complement.
-	 */
-	r = 0xff ^ (r >> 8);
-	g = 0xff ^ (g >> 8);
-	b = 0xff ^ (b >> 8);
+	gfx_color_get_ega(color, &attr);
 
 	gfx_bitmap_params_init(&params);
@@ -156,6 +150,11 @@
 	pmap.data = alloc.pixels;
 
+	off = 0;
 	for (x = 0; x < params.rect.p1.x; x++) {
-		pixel = PIXEL(str[x], r, g, b);
+		c = str_decode(str, &off, STR_NO_LIMIT);
+		pixel = PIXEL(attr,
+		    (c >> 16) & 0xff,
+		    (c >> 8) & 0xff,
+		    c & 0xff);
 		pixelmap_put_pixel(&pmap, x, 0, pixel);
 	}
Index: uspace/lib/memgfx/src/memgc.c
===================================================================
--- uspace/lib/memgfx/src/memgc.c	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/memgfx/src/memgc.c	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -108,7 +108,9 @@
 	mem_gc_t *mgc = (mem_gc_t *) arg;
 	uint16_t r, g, b;
+	uint8_t attr;
 
 	gfx_color_get_rgb_i16(color, &r, &g, &b);
-	mgc->color = PIXEL(0, r >> 8, g >> 8, b >> 8);
+	gfx_color_get_ega(color, &attr);
+	mgc->color = PIXEL(attr, r >> 8, g >> 8, b >> 8);
 	return EOK;
 }
Index: uspace/lib/ui/src/pbutton.c
===================================================================
--- uspace/lib/ui/src/pbutton.c	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/ui/src/pbutton.c	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -366,5 +366,5 @@
 	rect.p1.y = pbutton->rect.p0.y + 1;
 
-	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_highlight_color);
+	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_face_color);
 	if (rc != EOK)
 		goto error;
Index: uspace/lib/ui/src/resource.c
===================================================================
--- uspace/lib/ui/src/resource.c	(revision de0c55aad2546db54f4c4e371de34886eb068742)
+++ uspace/lib/ui/src/resource.c	(revision bc52b5ba1a06b4bd3ca224def5b3861e608da5f5)
@@ -47,12 +47,11 @@
 static const char *ui_typeface_path = "/data/font/helena.tpf";
 
-/** Create new UI resource.
+/** Create new UI resource in graphics mode.
  *
  * @param gc Graphic context
- * @param textmode @c true if running in text mode
  * @param rresource Place to store pointer to new UI resource
  * @return EOK on success, ENOMEM if out of memory
  */
-errno_t ui_resource_create(gfx_context_t *gc, bool textmode,
+static errno_t ui_resource_create_gfx(gfx_context_t *gc,
     ui_resource_t **rresource)
 {
@@ -87,28 +86,17 @@
 		return ENOMEM;
 
-	if (textmode) {
-		/* Create dummy font for text mode */
-		rc = gfx_typeface_create(gc, &tface);
-		if (rc != EOK)
-			goto error;
-
-		rc = gfx_font_create_textmode(tface, &font);
-		if (rc != EOK)
-			goto error;
-	} else {
-		rc = gfx_typeface_open(gc, ui_typeface_path, &tface);
-		if (rc != EOK)
-			goto error;
-
-		finfo = gfx_typeface_first_font(tface);
-		if (finfo == NULL) {
-			rc = EIO;
-			goto error;
-		}
-
-		rc = gfx_font_open(finfo, &font);
-		if (rc != EOK)
-			goto error;
+	rc = gfx_typeface_open(gc, ui_typeface_path, &tface);
+	if (rc != EOK)
+		goto error;
+
+	finfo = gfx_typeface_first_font(tface);
+	if (finfo == NULL) {
+		rc = EIO;
+		goto error;
 	}
+
+	rc = gfx_font_open(finfo, &font);
+	if (rc != EOK)
+		goto error;
 
 	rc = gfx_color_new_rgb_i16(0, 0, 0, &btn_frame_color);
@@ -201,5 +189,5 @@
 	resource->tface = tface;
 	resource->font = font;
-	resource->textmode = textmode;
+	resource->textmode = false;
 
 	resource->btn_frame_color = btn_frame_color;
@@ -280,4 +268,230 @@
 }
 
+/** Create new UI resource in text mode.
+ *
+ * @param gc Graphic context
+ * @param rresource Place to store pointer to new UI resource
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static errno_t ui_resource_create_text(gfx_context_t *gc,
+    ui_resource_t **rresource)
+{
+	ui_resource_t *resource;
+	gfx_typeface_t *tface = NULL;
+	gfx_font_t *font = NULL;
+	gfx_color_t *btn_frame_color = NULL;
+	gfx_color_t *btn_face_color = NULL;
+	gfx_color_t *btn_text_color = NULL;
+	gfx_color_t *btn_highlight_color = NULL;
+	gfx_color_t *btn_shadow_color = NULL;
+	gfx_color_t *wnd_face_color = NULL;
+	gfx_color_t *wnd_text_color = NULL;
+	gfx_color_t *wnd_sel_text_color = NULL;
+	gfx_color_t *wnd_sel_text_bg_color = NULL;
+	gfx_color_t *wnd_frame_hi_color = NULL;
+	gfx_color_t *wnd_frame_sh_color = NULL;
+	gfx_color_t *wnd_highlight_color = NULL;
+	gfx_color_t *wnd_shadow_color = NULL;
+	gfx_color_t *tbar_act_bg_color = NULL;
+	gfx_color_t *tbar_inact_bg_color = NULL;
+	gfx_color_t *tbar_act_text_color = NULL;
+	gfx_color_t *tbar_inact_text_color = NULL;
+	gfx_color_t *entry_fg_color = NULL;
+	gfx_color_t *entry_bg_color = NULL;
+	gfx_color_t *entry_act_bg_color = NULL;
+	errno_t rc;
+
+	resource = calloc(1, sizeof(ui_resource_t));
+	if (resource == NULL)
+		return ENOMEM;
+
+	/* Create dummy font for text mode */
+	rc = gfx_typeface_create(gc, &tface);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_font_create_textmode(tface, &font);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x07, &btn_frame_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x20, &btn_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x20, &btn_text_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff,
+	    &btn_highlight_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x01, &btn_shadow_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x70, &wnd_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x70, &wnd_text_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x07, &wnd_sel_text_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x07, &wnd_sel_text_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x70, &wnd_frame_hi_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x01, &wnd_frame_sh_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x70, &wnd_highlight_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x01, &wnd_shadow_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x1f, &tbar_act_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x1e, &tbar_act_text_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x07, &tbar_inact_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x07, &tbar_inact_text_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x1b, &entry_fg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x1b, &entry_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_ega(0x37, &entry_act_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	resource->gc = gc;
+	resource->tface = tface;
+	resource->font = font;
+	resource->textmode = true;
+
+	resource->btn_frame_color = btn_frame_color;
+	resource->btn_face_color = btn_face_color;
+	resource->btn_text_color = btn_text_color;
+	resource->btn_highlight_color = btn_highlight_color;
+	resource->btn_shadow_color = btn_shadow_color;
+
+	resource->wnd_face_color = wnd_face_color;
+	resource->wnd_text_color = wnd_text_color;
+	resource->wnd_sel_text_color = wnd_sel_text_color;
+	resource->wnd_sel_text_bg_color = wnd_sel_text_bg_color;
+	resource->wnd_frame_hi_color = wnd_frame_hi_color;
+	resource->wnd_frame_sh_color = wnd_frame_sh_color;
+	resource->wnd_highlight_color = wnd_highlight_color;
+	resource->wnd_shadow_color = wnd_shadow_color;
+
+	resource->tbar_act_bg_color = tbar_act_bg_color;
+	resource->tbar_act_text_color = tbar_act_text_color;
+	resource->tbar_inact_bg_color = tbar_inact_bg_color;
+	resource->tbar_inact_text_color = tbar_inact_text_color;
+
+	resource->entry_fg_color = entry_fg_color;
+	resource->entry_bg_color = entry_bg_color;
+	resource->entry_act_bg_color = entry_act_bg_color;
+
+	*rresource = resource;
+	return EOK;
+error:
+	if (btn_frame_color != NULL)
+		gfx_color_delete(btn_frame_color);
+	if (btn_face_color != NULL)
+		gfx_color_delete(btn_face_color);
+	if (btn_text_color != NULL)
+		gfx_color_delete(btn_text_color);
+	if (btn_highlight_color != NULL)
+		gfx_color_delete(btn_highlight_color);
+	if (btn_shadow_color != NULL)
+		gfx_color_delete(btn_shadow_color);
+
+	if (wnd_face_color != NULL)
+		gfx_color_delete(wnd_face_color);
+	if (wnd_text_color != NULL)
+		gfx_color_delete(wnd_text_color);
+	if (wnd_sel_text_color != NULL)
+		gfx_color_delete(wnd_sel_text_color);
+	if (wnd_sel_text_bg_color != NULL)
+		gfx_color_delete(wnd_sel_text_bg_color);
+	if (wnd_frame_hi_color != NULL)
+		gfx_color_delete(wnd_frame_hi_color);
+	if (wnd_frame_sh_color != NULL)
+		gfx_color_delete(wnd_frame_sh_color);
+	if (wnd_highlight_color != NULL)
+		gfx_color_delete(wnd_highlight_color);
+	if (wnd_shadow_color != NULL)
+		gfx_color_delete(wnd_shadow_color);
+
+	if (tbar_act_bg_color != NULL)
+		gfx_color_delete(tbar_act_bg_color);
+	if (tbar_act_text_color != NULL)
+		gfx_color_delete(tbar_act_text_color);
+	if (tbar_inact_bg_color != NULL)
+		gfx_color_delete(tbar_inact_bg_color);
+	if (tbar_inact_text_color != NULL)
+		gfx_color_delete(tbar_inact_text_color);
+
+	if (entry_fg_color != NULL)
+		gfx_color_delete(entry_fg_color);
+	if (entry_bg_color != NULL)
+		gfx_color_delete(entry_bg_color);
+	if (entry_act_bg_color != NULL)
+		gfx_color_delete(entry_act_bg_color);
+
+	if (tface != NULL)
+		gfx_typeface_destroy(tface);
+	free(resource);
+	return rc;
+}
+
+/** Create new UI resource.
+ *
+ * @param gc Graphic context
+ * @param textmode @c true if running in text mode
+ * @param rresource Place to store pointer to new UI resource
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_resource_create(gfx_context_t *gc, bool textmode,
+    ui_resource_t **rresource)
+{
+	if (textmode)
+		return ui_resource_create_text(gc, rresource);
+	else
+		return ui_resource_create_gfx(gc, rresource);
+}
+
 /** Destroy UI resource.
  *
