if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
use Elementor\Core\Kits\Documents\Tabs\Global_Colors;
use Elementor\Core\Kits\Documents\Tabs\Global_Typography;
* Elementor progress widget.
* Elementor widget that displays an escalating progress bar.
class Widget_Progress extends Widget_Base {
* Retrieve progress widget name.
* @return string Widget name.
public function get_name() {
* Retrieve progress widget title.
* @return string Widget title.
public function get_title() {
return esc_html__( 'Progress Bar', 'elementor' );
* Retrieve progress widget icon.
* @return string Widget icon.
public function get_icon() {
return 'eicon-skill-bar';
* Retrieve the list of keywords the widget belongs to.
* @return array Widget keywords.
public function get_keywords() {
return [ 'progress', 'bar' ];
protected function is_dynamic_content(): bool {
* Get style dependencies.
* Retrieve the list of style dependencies the widget requires.
* @return array Widget style dependencies.
public function get_style_depends(): array {
return [ 'widget-progress' ];
public function has_widget_inner_wrapper(): bool {
return ! Plugin::$instance->experiments->is_feature_active( 'e_optimized_markup' );
* Register progress widget controls.
* Adds different input fields to allow the user to change and customize the widget settings.
protected function register_controls() {
$this->start_controls_section(
'label' => esc_html__( 'Progress Bar', 'elementor' ),
'label' => esc_html__( 'Title', 'elementor' ),
'type' => Controls_Manager::TEXT,
'placeholder' => esc_html__( 'Enter your title', 'elementor' ),
'default' => esc_html__( 'My Skill', 'elementor' ),
'label' => esc_html__( 'Title HTML Tag', 'elementor' ),
'type' => Controls_Manager::SELECT,
'label' => esc_html__( 'Display Title', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Show', 'elementor' ),
'label_off' => esc_html__( 'Hide', 'elementor' ),
'label' => esc_html__( 'Type', 'elementor' ),
'type' => Controls_Manager::SELECT,
'' => esc_html__( 'Default', 'elementor' ),
'info' => esc_html__( 'Info', 'elementor' ),
'success' => esc_html__( 'Success', 'elementor' ),
'warning' => esc_html__( 'Warning', 'elementor' ),
'danger' => esc_html__( 'Danger', 'elementor' ),
'progress_type!' => '', // a workaround to hide the control, unless it's in use (not default).
'label' => esc_html__( 'Percentage', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'label' => esc_html__( 'Display Percentage', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Show', 'elementor' ),
'label_off' => esc_html__( 'Hide', 'elementor' ),
'return_value' => 'show',
'label' => esc_html__( 'Inner Text', 'elementor' ),
'type' => Controls_Manager::TEXT,
'placeholder' => esc_html__( 'e.g. Web Designer', 'elementor' ),
'default' => esc_html__( 'Web Designer', 'elementor' ),
$this->end_controls_section();
$this->start_controls_section(
'section_progress_style',
'label' => esc_html__( 'Progress Bar', 'elementor' ),
'tab' => Controls_Manager::TAB_STYLE,
'label' => esc_html__( 'Title', 'elementor' ),
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Text Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'{{WRAPPER}} .elementor-title' => 'color: {{VALUE}};',
'default' => Global_Colors::COLOR_PRIMARY,
$this->add_group_control(
Group_Control_Typography::get_type(),
'selector' => '{{WRAPPER}} .elementor-title',
'default' => Global_Typography::TYPOGRAPHY_TEXT,
$this->add_group_control(
Group_Control_Text_Shadow::get_type(),
'name' => 'title_shadow',
'selector' => '{{WRAPPER}} .elementor-title',
'label' => esc_html__( 'Percentage', 'elementor' ),
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'default' => Global_Colors::COLOR_PRIMARY,
'{{WRAPPER}} .elementor-progress-wrapper .elementor-progress-bar' => 'background-color: {{VALUE}};',
'label' => esc_html__( 'Background Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'{{WRAPPER}} .elementor-progress-wrapper' => 'background-color: {{VALUE}};',
'label' => esc_html__( 'Height', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'{{WRAPPER}} .elementor-progress-bar' => 'height: {{SIZE}}{{UNIT}}; line-height: {{SIZE}}{{UNIT}};',
'label' => esc_html__( 'Border Radius', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
'{{WRAPPER}} .elementor-progress-wrapper' => 'border-radius: {{SIZE}}{{UNIT}}; overflow: hidden;',
'label' => esc_html__( 'Inner Text', 'elementor' ),
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'{{WRAPPER}} .elementor-progress-bar' => 'color: {{VALUE}};',
$this->add_group_control(
Group_Control_Typography::get_type(),
'name' => 'bar_inner_typography',
'selector' => '{{WRAPPER}} .elementor-progress-bar',
$this->add_group_control(
Group_Control_Text_Shadow::get_type(),
'name' => 'bar_inner_shadow',
'selector' => '{{WRAPPER}} .elementor-progress-bar',
$this->end_controls_section();
* Render progress widget output on the frontend.
* Make sure value does no exceed 100%.
* Written in PHP and used to generate the final HTML.
protected function render() {
$settings = $this->get_settings_for_display();
if ( empty( $settings['title'] ) && empty( $settings['percent']['size'] ) ) {
$progressbar_id = 'elementor-progress-bar-' . $this->get_id();
$progress_percentage = is_numeric( $settings['percent']['size'] ) ? $settings['percent']['size'] : '0';
if ( 100 < $progress_percentage ) {
$progress_percentage = 100;
if ( ! Utils::is_empty( $settings['title'] ) ) {
if ( 'yes' === $settings['title_display'] ) {
$this->add_render_attribute(
'class' => 'elementor-title',
$this->add_inline_editing_attributes( 'title' );
$this->add_render_attribute( 'wrapper', 'aria-labelledby', $progressbar_id );
$this->add_render_attribute( 'wrapper', 'aria-label', $settings['title'] );
$this->add_render_attribute(
'class' => 'elementor-progress-wrapper',
'aria-valuemax' => '100',
'aria-valuenow' => $progress_percentage,
if ( ! empty( $settings['inner_text'] ) ) {
$this->add_render_attribute( 'wrapper', 'aria-valuetext', "{$progress_percentage}% ({$settings['inner_text']})" );
if ( ! empty( $settings['progress_type'] ) ) {
$this->add_render_attribute( 'wrapper', 'class', 'progress-' . $settings['progress_type'] );
$this->add_render_attribute(
'class' => 'elementor-progress-bar',
'data-max' => $progress_percentage,
$this->add_render_attribute( 'inner_text', 'class', 'elementor-progress-text' );
$this->add_inline_editing_attributes( 'inner_text' );
if ( ! Utils::is_empty( $settings['title'] ) && 'yes' === $settings['title_display'] ) { ?>
<<?php Utils::print_validated_html_tag( $settings['title_tag'] ); ?> <?php $this->print_render_attribute_string( 'title' ); ?>>
<?php echo wp_kses_post( $settings['title'] ); ?>
</<?php Utils::print_validated_html_tag( $settings['title_tag'] ); ?>>
<div <?php $this->print_render_attribute_string( 'wrapper' ); ?>>
<div <?php $this->print_render_attribute_string( 'progress-bar' ); ?>>
<span <?php $this->print_render_attribute_string( 'inner_text' ); ?>><?php echo wp_kses_post( $settings['inner_text'] ); ?></span>
<?php if ( 'show' === $settings['display_percentage'] ) { ?>
<span class="elementor-progress-percentage"><?php echo $progress_percentage; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>%</span>