Styles
The LVGL style system: properties, states, inheritance, and transitions.
An lv_style_t holds a set of property values (color, padding, border, font, and so on) that you attach to a widget, a specific part of a widget, and a state such as LV_STATE_PRESSED or LV_STATE_FOCUSED. Styles cascade through the parent/child tree, and transitions let property changes animate between states. This section covers property groups, state handling, and common composition patterns.
Arc stroke
Style the arc's ring and indicator into a modern progress dial.
arc_width and arc_rounded="true" give both parts a thick, round-capped
stroke. On selector="main" arc_color is a light neutral track; on
selector="indicator" it is the accent, so the same three properties
produce a clean circular-progress look.
Background gradient
Blend two colors vertically and shift where the blend happens.
bg_color and bg_grad_color are the two stops; bg_grad_dir="ver"
blends top-to-bottom. bg_main_stop="80" keeps the indigo solid only
briefly and bg_grad_stop="220" stretches the blend almost to the
bottom, so most of the card is transition rather than flat color.
Backdrop blur
Frost the text sitting behind a translucent card.
A paragraph fills the screen and the glass card is centered over its
middle. The card's blur_backdrop="true" with blur_radius="14" blurs
only what is behind it, and bg_opa="40%" keeps the card translucent so
the blurred text shows through — sharp text around it, soft text under it.
Border
Frame a card with a colored, partially transparent border.
border_width="4" and border_color draw the frame and
border_opa="60%" lets the surface show through it, so the border reads
as a soft accent instead of a hard line. radius="20" rounds the corners
and the border follows them.
Drop shadow
Unlike shadow_* (always a rectangle), drop_shadow_* blurs the actual
shape of the part. Added to LV_PART_INDICATOR, the active arc gets a
red shadow with drop_shadow_radius = 16 and an offset of (5, 10) that
follows the curve. drop_shadow_* has no XML attribute yet, so this is C.
Four gradient button backgrounds
Stack four buttons using horizontal, vertical, linear, and radial gradients.
Two styles prepare complex gradients: a linear gradient from
(0%, 0%) to (20%, 100%) with LV_GRAD_EXTEND_REFLECT, and a
radial gradient centered at (30%, 30%) extending to (100%, 100%)
with the same reflect mode. Four 150 by 50 buttons are aligned on
LV_ALIGN_CENTER: the first two use local bg_grad_dir set to
LV_GRAD_DIR_HOR and LV_GRAD_DIR_VER, and the last two apply the
linear and radial gradient styles. A fallback label reports when
LV_USE_DRAW_SW_COMPLEX_GRADIENTS is disabled.
Conical gradient metallic knob
Fill a circular object with a reflected conical gradient to mimic brushed metal.
A fully rounded radius = 500 style sets a black drop shadow and a
background lv_grad_dsc_t built with lv_grad_conical_init centered
on the object with LV_GRAD_EXTEND_REFLECT. The gradient uses up to
eight grey stops depending on LV_GRADIENT_MAX_STOPS. The styled
200 by 200 object is centered on the active screen; when
LV_USE_DRAW_SW_COMPLEX_GRADIENTS is disabled, a scrolling label
announces the missing feature instead.
Radial gradient background
Fill the screen with a purple-to-black radial gradient.
A style's background lv_grad_dsc_t is built with
lv_grad_radial_init, centered on the object and extending to the
bottom-right corner with LV_GRAD_EXTEND_PAD. The stops run from
0x9B1842 to black. An object sized to the display resolution is
created on the active screen and centered. When
LV_USE_DRAW_SW_COMPLEX_GRADIENTS is disabled, a scrolling label
reports the missing feature instead.
Image recolor and rotation
Tint and rotate an image purely through style properties.
image_recolor with image_recolor_opa="50%" mixes a blue tint into the
bitmap without touching the source, and transform_rotation="300" turns
the widget 30° (the unit is 0.1°). The radius, bg_color, and border_*
on the same style transform together with the image.
Line stroke
line_color and line_width set the stroke and line_rounded rounds
the end caps and vertices so the bends look smooth instead of mitred.
The point array is fixed; only the stroke style changes the look. Line
points are a C lv_point_precise_t array, which is why this is C.
Local style override
Local properties — including state selectors — beat the shared style.
All three buttons share style_shared (indigo, dark border, radius).
"Override" adds a plain local style_bg_color, so it is always pink.
"On press" adds a state-scoped local style_bg_color-pressed, so it
turns green only while held. Both keep the shared border, showing local
styles — and local state selectors — have the highest precedence.
Margin
Reserve space around one item on top of the layout's own gaps.
The row sets style_pad_column="8" between chips. The middle chip adds
style_margin_left="24" and style_margin_right="24", so it sits
visibly further from its neighbors than the 8 px flex gap — margin
reserves space around a Widget without changing the Widget's size.
Modal overlay timing
Compare a full-screen dim layer against a recolor overlay for modal dialogs.
The scene from lv_example_style_multiple is reused as the background,
then a modal overlay is drawn over it. By default a semi-transparent
black background is set on lv_layer_top(); toggling the #if 0
branch switches to lv_obj_set_style_recolor on the active screen
instead. A slider is added to lv_layer_top() and centered, then
lv_refr_now and lv_tick_elaps print the render cost of the chosen
approach through LV_LOG_USER.
Cascading styles
Stack a second style that overrides only a few properties of a shared base.
style_base defines the whole card — fill, border, shadow, radius,
padding, text color. style_warning sets only bg_color,
border_color, and text_color. The right card has both: the later
style wins for the properties it sets, the base shows through for the
rest, so one small style re-themes the card without redefining it.
Opacity and transform
Fade and geometrically transform a Widget via style properties.
Three identical buttons: the first is untouched, the second sets
style_opa="128" (50%), the third also rotates 15° with
style_transform_rotation="150" and scales to 1.25× with
style_transform_scale_x/y="320". opa and the transforms render the
button to a layer first, so its label fades and transforms with it.
Outline
Add a focus-style ring that sits outside the card and ignores layout.
An outline is painted outside the box, so unlike a border it never
resizes the Widget. outline_color + outline_width="3" draw the ring
and outline_pad="6" is the gap between the card edge and the ring —
the look of a modern keyboard-focus halo.
Padding
Inset a Widget's content per side with the directional pad_* properties.
Padding shrinks the content area. The left card pads only top and left
(style_pad_top/style_pad_left = 26, the opposite sides 8); the right
card pads every side by 18. An accent block fills each card at
100% × 100%, so the offset makes asymmetric vs uniform inset obvious.
Parts and states
Style one part normally and again for a state with selector.
selector="indicator" gives the slider's filled part a violet→pink
gradient. selector="pressed|indicator" adds a glow that applies only
while the indicator is pressed, so the same part carries one base look
plus a state-specific overlay — the core of LVGL's part/state model.
Box shadow
Lift a card off the page with a soft, offset shadow.
shadow_width="30" sets the blur and shadow_color tints it.
shadow_offset_y="12" drops the shadow below the card so it reads as
elevation, and shadow_opa="80" keeps it soft. With both offsets at 0
the same blur becomes an even glow instead.
Size and padding
Set a Widget's width and padding; its height follows the content.
Both cards share one style but set a different width (140 vs 240) and
style_pad_all (12 vs 28). height="content" lets each grow to exactly
fit its label plus that padding, so the same style yields a tight chip
and a roomy panel — size comes only from these properties.
Text styling
Color text, widen letter/line spacing, and underline it.
text_color recolors the glyphs, text_letter_space="4" and
text_line_space="14" open up tracking and leading, and
text_decor="underline" underlines every line — the spacing makes the
two lines read as a styled heading rather than body text.
Extending the current theme
Create a child theme that adds a green style to every button.
A first button labeled Original theme is added to the active screen
under the system theme. The helper new_theme_init_and_set clones
the current theme via lv_theme_copy, reparents it with
lv_theme_set_parent, and registers an apply callback that attaches
style_btn to any lv_button_class object. The new theme is
assigned with lv_display_set_theme, and a second button labeled
New theme renders with the green background and darker border. A
LV_EVENT_DELETE handler on the display frees the theme.
Transform a card with arc and slider
Rotate and scale a styled profile card using an arc and a slider.
Separate styles configure a grid-based card with shadow and rounded
corners, a circular avatar with shadow, and a gradient like-button.
The helper card_create assembles the card from an avatar image, a
name label, and a like-button. Two cards are centered on the active
screen; the back one is faded with LV_OPA_50. A large arc and a
bottom-aligned slider drive the front card through
LV_EVENT_VALUE_CHANGED: the arc writes transform_rotation from
lv_arc_get_angle_end * 10, while the slider (range 128 to 300,
initial 256) sets transform_scale_x and transform_scale_y.
Style transitions on press
Smoothly animate color and border changes when an object is pressed.
Two lv_style_transition_dsc_t instances animate LV_STYLE_BG_COLOR,
LV_STYLE_BORDER_COLOR, and LV_STYLE_BORDER_WIDTH: the default
transition runs for 100 ms with a 200 ms delay, while the pressed
transition runs for 500 ms with no delay. A red pressed style is
attached to LV_STATE_PRESSED on a centered base object, so pressing
it eases to red and releasing eases back.
Last updated on