Index: uspace/app/uidemo/uidemo.c
===================================================================
--- uspace/app/uidemo/uidemo.c	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/app/uidemo/uidemo.c	(revision ca2680d04b96dc8439f39dce7618923428b11279)
@@ -539,5 +539,5 @@
 	}
 
-	rc = ui_menu_create(demo.mbar, "File", &demo.mfile);
+	rc = ui_menu_create(demo.mbar, "~F~ile", &demo.mfile);
 	if (rc != EOK) {
 		printf("Error creating menu.\n");
@@ -593,5 +593,5 @@
 	ui_menu_entry_set_cb(mexit, uidemo_file_exit, (void *) &demo);
 
-	rc = ui_menu_create(demo.mbar, "Edit", &demo.medit);
+	rc = ui_menu_create(demo.mbar, "~E~dit", &demo.medit);
 	if (rc != EOK) {
 		printf("Error creating menu.\n");
@@ -607,11 +607,11 @@
 	ui_menu_entry_set_cb(mmodify, uidemo_edit_modify, (void *) &demo);
 
-	rc = ui_menu_create(demo.mbar, "Preferences", &demo.mpreferences);
-	if (rc != EOK) {
-		printf("Error creating menu.\n");
-		return rc;
-	}
-
-	rc = ui_menu_create(demo.mbar, "Help", &demo.mhelp);
+	rc = ui_menu_create(demo.mbar, "~P~references", &demo.mpreferences);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_create(demo.mbar, "~H~elp", &demo.mhelp);
 	if (rc != EOK) {
 		printf("Error creating menu.\n");
Index: uspace/lib/ui/include/types/ui/paint.h
===================================================================
--- uspace/lib/ui/include/types/ui/paint.h	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/lib/ui/include/types/ui/paint.h	(revision ca2680d04b96dc8439f39dce7618923428b11279)
@@ -36,4 +36,7 @@
 #ifndef _UI_TYPES_PAINT_H
 #define _UI_TYPES_PAINT_H
+
+#include <gfx/color.h>
+#include <gfx/font.h>
 
 /** Filled circle parts */
@@ -81,4 +84,20 @@
 } ui_box_style_t;
 
+/** UI text formatting */
+typedef struct {
+	/** Text font */
+	gfx_font_t *font;
+	/** Standard color */
+	gfx_color_t *color;
+	/** Highlight color */
+	gfx_color_t *hgl_color;
+	/** Horizontal alignment */
+	gfx_halign_t halign;
+	/** Justification width (for gfx_halign_justify) */
+	gfx_coord_t justify_width;
+	/** Vertical alignment */
+	gfx_valign_t valign;
+} ui_text_fmt_t;
+
 #endif
 
Index: uspace/lib/ui/include/ui/paint.h
===================================================================
--- uspace/lib/ui/include/ui/paint.h	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/lib/ui/include/ui/paint.h	(revision ca2680d04b96dc8439f39dce7618923428b11279)
@@ -59,4 +59,7 @@
 extern errno_t ui_paint_text_hbrace(ui_resource_t *, gfx_rect_t *,
     ui_box_style_t, gfx_color_t *);
+extern void ui_text_fmt_init(ui_text_fmt_t *);
+extern gfx_coord_t ui_text_width(gfx_font_t *, const char *);
+extern errno_t ui_paint_text(gfx_coord2_t *, ui_text_fmt_t *, const char *);
 
 #endif
Index: uspace/lib/ui/private/resource.h
===================================================================
--- uspace/lib/ui/private/resource.h	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/lib/ui/private/resource.h	(revision ca2680d04b96dc8439f39dce7618923428b11279)
@@ -77,6 +77,10 @@
 	/** Window text color */
 	gfx_color_t *wnd_text_color;
+	/** Window text highlight color */
+	gfx_color_t *wnd_text_hgl_color;
 	/** Window selected text color */
 	gfx_color_t *wnd_sel_text_color;
+	/** Window selected text highlight color */
+	gfx_color_t *wnd_sel_text_hgl_color;
 	/** Window selected text background color */
 	gfx_color_t *wnd_sel_text_bg_color;
Index: uspace/lib/ui/src/menubar.c
===================================================================
--- uspace/lib/ui/src/menubar.c	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/lib/ui/src/menubar.c	(revision ca2680d04b96dc8439f39dce7618923428b11279)
@@ -150,5 +150,5 @@
 {
 	ui_resource_t *res;
-	gfx_text_fmt_t fmt;
+	ui_text_fmt_t fmt;
 	gfx_coord2_t pos;
 	gfx_coord2_t tpos;
@@ -184,5 +184,5 @@
 	pos = mbar->rect.p0;
 
-	gfx_text_fmt_init(&fmt);
+	ui_text_fmt_init(&fmt);
 	fmt.font = res->font;
 	fmt.halign = gfx_halign_left;
@@ -192,5 +192,5 @@
 	while (menu != NULL) {
 		caption = ui_menu_caption(menu);
-		width = gfx_text_width(res->font, caption) + 2 * hpad;
+		width = ui_text_width(res->font, caption) + 2 * hpad;
 		tpos.x = pos.x + hpad;
 		tpos.y = pos.y + vpad;
@@ -202,7 +202,9 @@
 		if (menu == mbar->selected) {
 			fmt.color = res->wnd_sel_text_color;
+			fmt.hgl_color = res->wnd_sel_text_hgl_color;
 			bg_color = res->wnd_sel_text_bg_color;
 		} else {
 			fmt.color = res->wnd_text_color;
+			fmt.hgl_color = res->wnd_text_hgl_color;
 			bg_color = res->wnd_face_color;
 		}
@@ -216,5 +218,5 @@
 			goto error;
 
-		rc = gfx_puttext(&tpos, &fmt, caption);
+		rc = ui_paint_text(&tpos, &fmt, caption);
 		if (rc != EOK)
 			goto error;
@@ -416,5 +418,5 @@
 	while (menu != NULL) {
 		caption = ui_menu_caption(menu);
-		width = gfx_text_width(res->font, caption) + 2 * hpad;
+		width = ui_text_width(res->font, caption) + 2 * hpad;
 
 		rect.p0 = pos;
@@ -467,5 +469,5 @@
 	while (cur != NULL) {
 		caption = ui_menu_caption(cur);
-		width = gfx_text_width(res->font, caption) + 2 * hpad;
+		width = ui_text_width(res->font, caption) + 2 * hpad;
 
 		rect.p0 = pos;
Index: uspace/lib/ui/src/paint.c
===================================================================
--- uspace/lib/ui/src/paint.c	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/lib/ui/src/paint.c	(revision ca2680d04b96dc8439f39dce7618923428b11279)
@@ -554,4 +554,151 @@
 }
 
+/** Initialize UI text formatting structure.
+ *
+ * UI text formatting structure must always be initialized using this function
+ * first.
+ *
+ * @param fmt UI text formatting structure
+ */
+void ui_text_fmt_init(ui_text_fmt_t *fmt)
+{
+	memset(fmt, 0, sizeof(ui_text_fmt_t));
+}
+
+/** Process text with '~' markup.
+ *
+ * Parse text with tilde markup into a list of strings. @a *rbuf is set
+ * to point to a newly allocated buffer containing consecutive null-terminated
+ * strings.
+ *
+ * Each part between two '~' becomes one string. '~~' is translated into
+ * a literal '~' character. @a *endptr is set to point to the first character
+ * beyond the end of the list.
+ *
+ * @param str String with tilde markup
+ * @param rbuf Place to store pointer to newly allocated buffer.
+ * @param endptr Place to store end pointer (just after last character)
+ * @return EOK on success or an error code
+ */
+static int ui_text_process(const char *str, char **rbuf, char **endptr)
+{
+	const char *sp;
+	char *dp;
+	char *buf;
+
+	buf = malloc(str_size(str) + 1);
+	if (buf == NULL)
+		return ENOMEM;
+
+	/* Break down string into list of (non)highlighted parts */
+	sp = str;
+	dp = buf;
+	while (*sp != '\0') {
+		if (*sp == '~') {
+			if (sp[1] == '~') {
+				sp += 2;
+				*dp++ = '~';
+			} else {
+				++sp;
+				*dp++ = '\0';
+			}
+		} else {
+			*dp++ = *sp++;
+		}
+	}
+
+	*dp++ = '\0';
+	*rbuf = buf;
+	*endptr = dp;
+
+	return EOK;
+}
+
+/** Compute UI text width.
+ *
+ * @param font Font
+ * @param str String
+ * @return Text width
+ */
+gfx_coord_t ui_text_width(gfx_font_t *font, const char *str)
+{
+	gfx_coord_t w;
+	gfx_coord_t tw;
+	const char *sp;
+
+	/* Text including tildes */
+	w = gfx_text_width(font, str);
+	tw = gfx_text_width(font, "~");
+
+	/* Subtract width of singular tildes */
+	sp = str;
+	while (*sp != '\0') {
+		if (*sp == '~' && sp[1] != '~')
+			w -= tw;
+		++sp;
+	}
+
+	return w;
+}
+
+/** Paint text (with highlighting markup).
+ *
+ * Paint some text with highlighted sections enclosed with tilde characters
+ * ('~'). A literal tilde can be printed with "~~". Text can be highlighted
+ * with an underline or different color, based on display capabilities.
+ *
+ * @param pos Anchor position
+ * @param fmt Text formatting
+ * @param str String containing '~' markup
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ui_paint_text(gfx_coord2_t *pos, ui_text_fmt_t *fmt, const char *str)
+{
+	gfx_coord2_t tpos;
+	gfx_text_fmt_t tfmt;
+	char *buf;
+	char *endptr;
+	const char *sp;
+	errno_t rc;
+
+	/* Break down string into list of (non)highlighted parts */
+	rc = ui_text_process(str, &buf, &endptr);
+	if (rc != EOK)
+		return rc;
+
+	gfx_text_fmt_init(&tfmt);
+	tfmt.font = fmt->font;
+	tfmt.color = fmt->color;
+	tfmt.halign = fmt->halign;
+	tfmt.justify_width = fmt->justify_width;
+	tfmt.valign = fmt->valign;
+	tfmt.underline = false;
+
+	tpos = *pos;
+	sp = buf;
+	while (sp != endptr) {
+		/* Print the current string */
+		rc = gfx_puttext(&tpos, &tfmt, sp);
+		if (rc != EOK)
+			goto error;
+
+		gfx_text_cont(&tpos, &tfmt, sp, &tpos, &tfmt);
+		tfmt.underline = !tfmt.underline;
+		tfmt.color = tfmt.underline ? fmt->hgl_color : fmt->color;
+
+		/* Skip to the next string */
+		while (*sp != '\0')
+			++sp;
+		++sp;
+	}
+
+	free(buf);
+	return EOK;
+error:
+	free(buf);
+	return rc;
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/resource.c
===================================================================
--- uspace/lib/ui/src/resource.c	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/lib/ui/src/resource.c	(revision ca2680d04b96dc8439f39dce7618923428b11279)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -67,5 +67,7 @@
 	gfx_color_t *wnd_face_color = NULL;
 	gfx_color_t *wnd_text_color = NULL;
+	gfx_color_t *wnd_text_hgl_color = NULL;
 	gfx_color_t *wnd_sel_text_color = NULL;
+	gfx_color_t *wnd_sel_text_hgl_color = NULL;
 	gfx_color_t *wnd_sel_text_bg_color = NULL;
 	gfx_color_t *wnd_frame_hi_color = NULL;
@@ -131,5 +133,14 @@
 		goto error;
 
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &wnd_text_hgl_color);
+	if (rc != EOK)
+		goto error;
+
 	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &wnd_sel_text_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff,
+	    &wnd_sel_text_hgl_color);
 	if (rc != EOK)
 		goto error;
@@ -210,5 +221,7 @@
 	resource->wnd_face_color = wnd_face_color;
 	resource->wnd_text_color = wnd_text_color;
+	resource->wnd_text_hgl_color = wnd_text_hgl_color;
 	resource->wnd_sel_text_color = wnd_sel_text_color;
+	resource->wnd_sel_text_hgl_color = wnd_sel_text_hgl_color;
 	resource->wnd_sel_text_bg_color = wnd_sel_text_bg_color;
 	resource->wnd_frame_hi_color = wnd_frame_hi_color;
@@ -246,6 +259,10 @@
 	if (wnd_text_color != NULL)
 		gfx_color_delete(wnd_text_color);
+	if (wnd_text_hgl_color != NULL)
+		gfx_color_delete(wnd_text_hgl_color);
 	if (wnd_sel_text_color != NULL)
 		gfx_color_delete(wnd_sel_text_color);
+	if (wnd_sel_text_hgl_color != NULL)
+		gfx_color_delete(wnd_sel_text_hgl_color);
 	if (wnd_sel_text_bg_color != NULL)
 		gfx_color_delete(wnd_sel_text_bg_color);
@@ -304,5 +321,7 @@
 	gfx_color_t *wnd_face_color = NULL;
 	gfx_color_t *wnd_text_color = NULL;
+	gfx_color_t *wnd_text_hgl_color = NULL;
 	gfx_color_t *wnd_sel_text_color = NULL;
+	gfx_color_t *wnd_sel_text_hgl_color = NULL;
 	gfx_color_t *wnd_sel_text_bg_color = NULL;
 	gfx_color_t *wnd_frame_hi_color = NULL;
@@ -362,5 +381,13 @@
 		goto error;
 
+	rc = gfx_color_new_ega(0x74, &wnd_text_hgl_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(0x04, &wnd_sel_text_hgl_color);
 	if (rc != EOK)
 		goto error;
@@ -435,5 +462,7 @@
 	resource->wnd_face_color = wnd_face_color;
 	resource->wnd_text_color = wnd_text_color;
+	resource->wnd_text_hgl_color = wnd_text_hgl_color;
 	resource->wnd_sel_text_color = wnd_sel_text_color;
+	resource->wnd_sel_text_hgl_color = wnd_sel_text_hgl_color;
 	resource->wnd_sel_text_bg_color = wnd_sel_text_bg_color;
 	resource->wnd_frame_hi_color = wnd_frame_hi_color;
@@ -471,6 +500,10 @@
 	if (wnd_text_color != NULL)
 		gfx_color_delete(wnd_text_color);
+	if (wnd_text_hgl_color != NULL)
+		gfx_color_delete(wnd_text_hgl_color);
 	if (wnd_sel_text_color != NULL)
 		gfx_color_delete(wnd_sel_text_color);
+	if (wnd_sel_text_hgl_color != NULL)
+		gfx_color_delete(wnd_sel_text_hgl_color);
 	if (wnd_sel_text_bg_color != NULL)
 		gfx_color_delete(wnd_sel_text_bg_color);
