How do you style LVGL for tiny screens?


Hello, we have a 128x64 pixel OLED display, the first 16 pixel rows are yellow, and the rest are blue. Note that the colors are not controlled in software, they are physically yellow or blue according to position.

The top 16 pixel rows are intend as a yellow status bar, and the rest are blue for general application use.

What MCU/Processor/Board and compiler are you using?


What LVGL version are you using?


What do you want to achieve?

We are trying to make LVGL provide a status bar at the top in yellow, with a menu below that which is as compact as possible.

What have you tried so far?

We tried to use a grid with 1 column and 2 rows, where the first row is 16 pixels high, and the second row is the remainder of the screen. We then put a label in grid 0,0 and a menu in grid 1,0. Even though we have set the grid to these constraints, it is not placing the text properly: the status bar is below the 16-pixel row, and there appears to be padding around each grid even though we set the borders to 0.

Code to reproduce

I just hacked this into main.c in lv_port_pc_eclipse and it builds and shows the same problem we are having on the ESP32. This snippet replaces the main() function in main.c:

void menu_item(lv_group_t *group, lv_obj_t *main_page, lv_style_t *style, char *item_name)
	lv_obj_t *cont;
	lv_obj_t *label;

	cont = lv_menu_cont_create(main_page);
	lv_obj_add_style(cont, style, LV_STATE_FOCUS_KEY);

	lv_group_add_obj(group, cont);
	lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE);
	lv_obj_add_flag(cont, LV_OBJ_FLAG_SCROLL_ON_FOCUS);

	label = lv_label_create(cont);
	lv_label_set_text(label, item_name);

void lvgl_menu()
	// Lock the mutex due to the LVGL APIs are not thread-safe
	//if (lvgl_port_lock(0))
		lv_obj_t *scr = lv_disp_get_scr_act(NULL);
		lv_obj_set_size(scr, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));

		lv_group_t *group = lv_group_get_default();
		//lv_indev_set_group(indev_keypad, group);

		static lv_style_t style;
		lv_style_set_bg_color(&style, lv_color_hex(0x000000));
		lv_style_set_border_width(&style, 0);
		lv_style_set_border_color(&style, lv_color_hex(0xff0000));

		static lv_style_t no_border;
		lv_style_set_border_width(&no_border, 0);

		lv_obj_add_style(scr, &no_border, LV_STATE_DEFAULT);

		static lv_coord_t col[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
		static lv_coord_t row[] = {16, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};

		lv_obj_t *grid = lv_obj_create(scr);
		lv_obj_add_style(grid, &no_border, LV_STATE_DEFAULT);

		lv_obj_set_size(grid, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
		lv_obj_set_grid_dsc_array(grid, col, row);

		lv_obj_t *status_bar_label = lv_label_create(grid);
		lv_label_set_text(status_bar_label, "bar");

		lv_obj_add_style(status_bar_label, &no_border, LV_STATE_DEFAULT);
		lv_obj_set_grid_cell(status_bar_label, LV_GRID_ALIGN_STRETCH, 0, 1,

		lv_obj_t *menu = lv_menu_create(grid);
		lv_obj_set_grid_cell(menu, LV_GRID_ALIGN_STRETCH, 0, 1,
		lv_obj_add_style(menu, &no_border, LV_STATE_DEFAULT);

		// Create a main page
		lv_obj_t *main_page = lv_menu_page_create(menu, NULL);

		menu_item(group, main_page, &style, "zeke 1 2 3");
		menu_item(group, main_page, &style, "zeke 1 2 3");
		menu_item(group, main_page, &style, "zeke 1 2 3");

		lv_menu_set_page(menu, main_page);

		// Release the mutex

int main(int argc, char **argv)
  /*Initialize LVGL*/

  /*Initialize the display, and the input devices*/
  hal_init(128, 64);


  /*To hide the memory and performance indicators in the corners
   *disable `LV_USE_MEM_MONITOR` and `LV_USE_PERF_MONITOR` in `lv_conf.h`*/

  while(1) {
      /* Periodically call the lv_task handler.
       * It could be done in a timer interrupt or an OS task too.*/
      usleep(10* 1000);

  return 0;

Screenshot and/or video

This is what it looks like with the grid. The text “bar” should be all yellow, we will use it for static symbols (signal strength, etc):


If we remove the grid and only place the menu then it fills the screen, but the menuitem rows are very large even though we set border_width to 0. We would like to achive about 3 menu rows in the remaining 48 pixel rows below yellow section:



I think this may have to do something with the pad property that objects have by default.
try adding this to your no_border style:

lv_style_set_pad_all(&no_border, 0)
Furthermore, objects may by default have rounding on them depending on your lv_conf settings,
so set
lv_style_set_radius(&no_border, 0) if you want to have straight edges.

Set the font for the menu. It uses a function

lv_obj_set_style_text_font(ui_Menu, &lv_font_montserrat_30, 0);

Get the height of the font with a function:

const lv_font_t * font = lv_obj_get_style_text_font(menu, LV_PART_ITEMS); // or LV_PART_MAIN
lv_coord_t menuList_h = lv_font_get_line_height(font)

Set the menu list size:

lv_obj_set_size(menuList, LV_PCT(100), menuList_h);

Roughly, these are guidelines

Thank you for your great answers, Now the screen is working well!

Both of your answers we’re helpful, so I do not know who to mark as the solution.

…looking to do almost exact same template. If you see this and don’t mind uploading your working code would be great…