Index: uspace/lib/ui/include/types/ui/pbutton.h
===================================================================
--- uspace/lib/ui/include/types/ui/pbutton.h	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/include/types/ui/pbutton.h	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -37,4 +37,7 @@
 #define _UI_TYPES_PBUTTON_H
 
+#include <errno.h>
+#include <gfx/coord.h>
+
 struct ui_pbutton;
 typedef struct ui_pbutton ui_pbutton_t;
@@ -47,4 +50,9 @@
 } ui_pbutton_cb_t;
 
+/** Push button decoration ops */
+typedef struct ui_pbutton_decor_ops {
+	errno_t (*paint)(ui_pbutton_t *, void *, gfx_coord2_t *);
+} ui_pbutton_decor_ops_t;
+
 #endif
 
Index: uspace/lib/ui/include/ui/paint.h
===================================================================
--- uspace/lib/ui/include/ui/paint.h	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/include/ui/paint.h	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -55,4 +55,12 @@
 extern errno_t ui_paint_filled_circle(gfx_context_t *, gfx_coord2_t *,
     gfx_coord_t, ui_fcircle_part_t);
+extern errno_t ui_paint_up_triangle(gfx_context_t *, gfx_coord2_t *,
+    gfx_coord_t);
+extern errno_t ui_paint_down_triangle(gfx_context_t *, gfx_coord2_t *,
+    gfx_coord_t);
+extern errno_t ui_paint_left_triangle(gfx_context_t *, gfx_coord2_t *,
+    gfx_coord_t);
+extern errno_t ui_paint_right_triangle(gfx_context_t *, gfx_coord2_t *,
+    gfx_coord_t);
 extern errno_t ui_paint_text_box(ui_resource_t *, gfx_rect_t *,
     ui_box_style_t, gfx_color_t *);
Index: uspace/lib/ui/include/ui/pbutton.h
===================================================================
--- uspace/lib/ui/include/ui/pbutton.h	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/include/ui/pbutton.h	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -51,4 +51,6 @@
 extern ui_control_t *ui_pbutton_ctl(ui_pbutton_t *);
 extern void ui_pbutton_set_cb(ui_pbutton_t *, ui_pbutton_cb_t *, void *);
+extern void ui_pbutton_set_decor_ops(ui_pbutton_t *, ui_pbutton_decor_ops_t *,
+    void *);
 extern void ui_pbutton_set_rect(ui_pbutton_t *, gfx_rect_t *);
 extern void ui_pbutton_set_default(ui_pbutton_t *, bool);
Index: uspace/lib/ui/private/pbutton.h
===================================================================
--- uspace/lib/ui/private/pbutton.h	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/private/pbutton.h	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -54,4 +54,8 @@
 	/** Callback argument */
 	void *arg;
+	/** Custom decoration ops or @c NULL */
+	struct ui_pbutton_decor_ops *decor_ops;
+	/** Decoration argument */
+	void *decor_arg;
 	/** Push button rectangle */
 	gfx_rect_t rect;
Index: uspace/lib/ui/src/paint.c
===================================================================
--- uspace/lib/ui/src/paint.c	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/src/paint.c	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -360,4 +360,108 @@
 }
 
+/** Paint upward pointing triangle.
+ *
+ * @param gc Graphic context
+ * @param pos Center position
+ * @param n Length of triangle side
+ */
+errno_t ui_paint_up_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
+    gfx_coord_t n)
+{
+	gfx_coord_t i;
+	errno_t rc;
+	gfx_rect_t rect;
+
+	for (i = 0; i < n; i++) {
+		rect.p0.x = pos->x - i;
+		rect.p0.y = pos->y - n / 2 + i;
+		rect.p1.x = pos->x + i + 1;
+		rect.p1.y = pos->y - n / 2 + i + 1;
+		rc = gfx_fill_rect(gc, &rect);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+/** Paint downward pointing triangle.
+ *
+ * @param gc Graphic context
+ * @param pos Center position
+ * @param n Length of triangle side
+ */
+errno_t ui_paint_down_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
+    gfx_coord_t n)
+{
+	gfx_coord_t i;
+	errno_t rc;
+	gfx_rect_t rect;
+
+	for (i = 0; i < n; i++) {
+		rect.p0.x = pos->x - i;
+		rect.p0.y = pos->y + n / 2 - i;
+		rect.p1.x = pos->x + i + 1;
+		rect.p1.y = pos->y + n / 2 - i + 1;
+		rc = gfx_fill_rect(gc, &rect);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+/** Paint left pointing triangle.
+ *
+ * @param gc Graphic context
+ * @param pos Center position
+ * @param n Length of triangle side
+ */
+errno_t ui_paint_left_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
+    gfx_coord_t n)
+{
+	gfx_coord_t i;
+	errno_t rc;
+	gfx_rect_t rect;
+
+	for (i = 0; i < n; i++) {
+		rect.p0.x = pos->x - n / 2 + i;
+		rect.p0.y = pos->y - i;
+		rect.p1.x = pos->x - n / 2 + i + 1;
+		rect.p1.y = pos->y + i + 1;
+		rc = gfx_fill_rect(gc, &rect);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+/** Paint right pointing triangle.
+ *
+ * @param gc Graphic context
+ * @param pos Center position
+ * @param n Length of triangle side
+ */
+errno_t ui_paint_right_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
+    gfx_coord_t n)
+{
+	gfx_coord_t i;
+	errno_t rc;
+	gfx_rect_t rect;
+
+	for (i = 0; i < n; i++) {
+		rect.p0.x = pos->x + n / 2 - i;
+		rect.p0.y = pos->y - i;
+		rect.p1.x = pos->x + n / 2 - i + 1;
+		rect.p1.y = pos->y + i + 1;
+		rc = gfx_fill_rect(gc, &rect);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
 /** Paint a text box.
  *
Index: uspace/lib/ui/src/pbutton.c
===================================================================
--- uspace/lib/ui/src/pbutton.c	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/src/pbutton.c	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -32,4 +32,7 @@
 /**
  * @file Push button
+ *
+ * Push button either uses text as decoration, or it can use a caller-provided
+ * function to paint the decoration.
  */
 
@@ -136,4 +139,17 @@
 }
 
+/** Set push button decoration ops.
+ *
+ * @param pbutton Push button
+ * @param ops Push button decoration callbacks
+ * @param arg Decoration ops argument
+ */
+void ui_pbutton_set_decor_ops(ui_pbutton_t *pbutton,
+    ui_pbutton_decor_ops_t *ops, void *arg)
+{
+	pbutton->decor_ops = ops;
+	pbutton->decor_arg = arg;
+}
+
 /** Set button rectangle.
  *
@@ -306,13 +322,22 @@
 	}
 
-	gfx_text_fmt_init(&fmt);
-	fmt.font = pbutton->res->font;
-	fmt.color = pbutton->res->btn_text_color;
-	fmt.halign = gfx_halign_center;
-	fmt.valign = gfx_valign_center;
-
-	rc = gfx_puttext(&pos, &fmt, pbutton->caption);
-	if (rc != EOK)
-		goto error;
+	if (pbutton->decor_ops != NULL && pbutton->decor_ops->paint != NULL) {
+		/* Custom decoration */
+		rc = pbutton->decor_ops->paint(pbutton, pbutton->decor_arg,
+		    &pos);
+		if (rc != EOK)
+			goto error;
+	} else {
+		/* Text decoration */
+		gfx_text_fmt_init(&fmt);
+		fmt.font = pbutton->res->font;
+		fmt.color = pbutton->res->btn_text_color;
+		fmt.halign = gfx_halign_center;
+		fmt.valign = gfx_valign_center;
+
+		rc = gfx_puttext(&pos, &fmt, pbutton->caption);
+		if (rc != EOK)
+			goto error;
+	}
 
 	rc = ui_pbutton_paint_frame(pbutton);
Index: uspace/lib/ui/src/scrollbar.c
===================================================================
--- uspace/lib/ui/src/scrollbar.c	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/src/scrollbar.c	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -78,5 +78,5 @@
 enum {
 	/** Scrollbar button width */
-	ui_scrollbar_btn_len = 20,
+	ui_scrollbar_btn_len = 21,
 	/** Scrollbar button width in text mode */
 	ui_scrollbar_btn_len_text = 1,
@@ -86,5 +86,5 @@
 	ui_scrollbar_thumb_bevel_width = 2,
 	/** Scrollbar default thumb length */
-	ui_scrollbar_def_thumb_len = 20,
+	ui_scrollbar_def_thumb_len = 21,
 	/** Scrollbar default thumb length in text mode */
 	ui_scrollbar_def_thumb_len_text = 1,
@@ -97,4 +97,8 @@
 static void ui_scrollbar_up_btn_down(ui_pbutton_t *, void *);
 static void ui_scrollbar_up_btn_up(ui_pbutton_t *, void *);
+static errno_t ui_scrollbar_up_btn_decor_paint(ui_pbutton_t *, void *,
+    gfx_coord2_t *);
+static errno_t ui_scrollbar_down_btn_decor_paint(ui_pbutton_t *, void *,
+    gfx_coord2_t *);
 static void ui_scrollbar_down_btn_down(ui_pbutton_t *, void *);
 static void ui_scrollbar_down_btn_up(ui_pbutton_t *, void *);
@@ -108,7 +112,15 @@
 };
 
+static ui_pbutton_decor_ops_t ui_scrollbar_up_btn_decor_ops = {
+	.paint = ui_scrollbar_up_btn_decor_paint
+};
+
 static ui_pbutton_cb_t ui_scrollbar_down_btn_cb = {
 	.down = ui_scrollbar_down_btn_down,
 	.up = ui_scrollbar_down_btn_up
+};
+
+static ui_pbutton_decor_ops_t ui_scrollbar_down_btn_decor_ops = {
+	.paint = ui_scrollbar_down_btn_decor_paint
 };
 
@@ -188,5 +200,8 @@
 
 	ui_pbutton_set_cb(scrollbar->up_btn, &ui_scrollbar_up_btn_cb,
-	    (void *) scrollbar);
+	    scrollbar);
+
+	ui_pbutton_set_decor_ops(scrollbar->up_btn,
+	    &ui_scrollbar_up_btn_decor_ops, (void *) scrollbar);
 
 	rc = ui_pbutton_create(resource, down_text, &scrollbar->down_btn);
@@ -196,4 +211,7 @@
 	ui_pbutton_set_cb(scrollbar->down_btn, &ui_scrollbar_down_btn_cb,
 	    (void *) scrollbar);
+
+	ui_pbutton_set_decor_ops(scrollbar->down_btn,
+	    &ui_scrollbar_down_btn_decor_ops, (void *) scrollbar);
 
 	scrollbar->thumb_len = resource->textmode ?
@@ -961,4 +979,48 @@
 }
 
+/** Paint up button decoration.
+ *
+ * @param pbutton Push button
+ * @param arg Argument (ui_scrollbar_t *)
+ * @param pos Center position
+ */
+static errno_t ui_scrollbar_up_btn_decor_paint(ui_pbutton_t *pbutton,
+    void *arg, gfx_coord2_t *pos)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+	errno_t rc;
+
+	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_text_color);
+	if (rc != EOK)
+		return rc;
+
+	if (scrollbar->dir == ui_sbd_horiz)
+		return ui_paint_left_triangle(pbutton->res->gc, pos, 5);
+	else
+		return ui_paint_up_triangle(pbutton->res->gc, pos, 5);
+}
+
+/** Paint down button decoration.
+ *
+ * @param pbutton Push button
+ * @param arg Argument (ui_scrollbar_t *)
+ * @param pos Center position
+ */
+static errno_t ui_scrollbar_down_btn_decor_paint(ui_pbutton_t *pbutton,
+    void *arg, gfx_coord2_t *pos)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+	errno_t rc;
+
+	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_text_color);
+	if (rc != EOK)
+		return rc;
+
+	if (scrollbar->dir == ui_sbd_horiz)
+		return ui_paint_right_triangle(pbutton->res->gc, pos, 5);
+	else
+		return ui_paint_down_triangle(pbutton->res->gc, pos, 5);
+}
+
 /** Scrollbar down button pressed.
  *
Index: uspace/lib/ui/test/paint.c
===================================================================
--- uspace/lib/ui/test/paint.c	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/test/paint.c	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -225,4 +225,7 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
+	center.x = 0;
+	center.y = 0;
+
 	/* Paint filled circle / upper-left half */
 	rc = ui_paint_filled_circle(gc, &center, 10, ui_fcircle_upleft);
@@ -235,4 +238,92 @@
 	/* Paint entire filled circle */
 	rc = ui_paint_filled_circle(gc, &center, 10, ui_fcircle_entire);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Paint up pointing triangle */
+PCUT_TEST(up_triangle)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_coord2_t center;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	center.x = 0;
+	center.y = 0;
+
+	rc = ui_paint_up_triangle(gc, &center, 5);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Paint down pointing triangle */
+PCUT_TEST(down_triangle)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_coord2_t center;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	center.x = 0;
+	center.y = 0;
+
+	rc = ui_paint_down_triangle(gc, &center, 5);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Paint left pointing triangle */
+PCUT_TEST(left_triangle)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_coord2_t center;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	center.x = 0;
+	center.y = 0;
+
+	rc = ui_paint_left_triangle(gc, &center, 5);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Paint right pointing triangle */
+PCUT_TEST(right_triangle)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_coord2_t center;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	center.x = 0;
+	center.y = 0;
+
+	rc = ui_paint_right_triangle(gc, &center, 5);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
Index: uspace/lib/ui/test/scrollbar.c
===================================================================
--- uspace/lib/ui/test/scrollbar.c	(revision 0d1d0ea5834bc4827a116522267acc391b94d31d)
+++ uspace/lib/ui/test/scrollbar.c	(revision d68239a1866f9eae07514ac5246d8925006600dd)
@@ -319,5 +319,5 @@
 
 	/* Total length minus buttons */
-	PCUT_ASSERT_INT_EQUALS(110 - 10 - 2 * 20, length);
+	PCUT_ASSERT_INT_EQUALS(110 - 10 - 2 * 21, length);
 
 	ui_scrollbar_destroy(scrollbar);
@@ -361,5 +361,5 @@
 
 	/* Total length minus buttons minus default thumb length */
-	PCUT_ASSERT_INT_EQUALS(110 - 10 - 2 * 20 - 20, length);
+	PCUT_ASSERT_INT_EQUALS(110 - 10 - 2 * 21 - 21, length);
 
 	ui_scrollbar_destroy(scrollbar);
@@ -492,6 +492,6 @@
 	ui_scrollbar_set_pos(scrollbar, 42);
 	pos = ui_scrollbar_get_pos(scrollbar);
-	/* The value is clipped to the maximum possible position (40) */
-	PCUT_ASSERT_INT_EQUALS(40, pos);
+	/* The value is clipped to the maximum possible position (37) */
+	PCUT_ASSERT_INT_EQUALS(37, pos);
 
 	ui_scrollbar_destroy(scrollbar);
