A lightweight Elementor widget that checks the product’s stock status within a loop and displays a conditional badge (visible only when the product is out of stock, or configurable to display all statuses).
Two display modes configurable in the widget controls: either visible only when the product is out of stock or being restocked (default mode, ideal for product cards), or always visible with all 4 statuses (in stock, low stock, restocking, out of stock).
Smart low-stock detection: it cross-references `get_stock_status()` with `get_stock_quantity()` and the `woocommerce_notify_low_stock_amount` threshold to distinguish a product that is “in stock” from one with “low stock”—something WooCommerce does not do natively in its stock status.
Full Elementor controls in the Style tab: typography, text/background colors by status (via separate tabs), padding, border-radius, alignment, icon size, and spacing. Everything is controlled by Elementor selectors, so it works responsively and in the editor preview.
Loop Builder compatibility: the widget retrieves the current product via `wc_get_product( get_the_ID() )` as a fallback for the global `$product`, and displays an “Out of Stock” preview in the editor when there is no product in context.
To install it: place the file in wp-content/mu-plugins/.
The widget will appear in the “WooCommerce” category in Elementor under the name “Wycan – Stock Status,” ready to be dragged into the Loop Builder template.
start_controls_section( 'section_content', [
'label' => 'Contenu',
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
] );
$this->add_control( 'display_mode', [
'label' => 'Affichage',
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'out_of_stock_only',
'options' => [
'out_of_stock_only' => 'Uniquement si rupture de stock',
'always' => 'Toujours (tous les statuts)',
],
] );
$this->add_control( 'label_in_stock', [
'label' => 'Libellé – En stock',
'type' => \Elementor\Controls_Manager::TEXT,
'default' => 'En stock',
'condition' => [ 'display_mode' => 'always' ],
] );
$this->add_control( 'label_low_stock', [
'label' => 'Libellé – Stock faible',
'type' => \Elementor\Controls_Manager::TEXT,
'default' => 'Stock faible',
'condition' => [ 'display_mode' => 'always' ],
] );
$this->add_control( 'label_on_backorder', [
'label' => 'Libellé – En réapprovisionnement',
'type' => \Elementor\Controls_Manager::TEXT,
'default' => 'En réapprovisionnement',
] );
$this->add_control( 'label_out_of_stock', [
'label' => 'Libellé – Rupture de stock',
'type' => \Elementor\Controls_Manager::TEXT,
'default' => 'Rupture de stock',
] );
$this->add_control( 'show_icon', [
'label' => 'Afficher une icône',
'type' => \Elementor\Controls_Manager::SWITCHER,
'default' => 'yes',
'label_on' => 'Oui',
'label_off' => 'Non',
] );
$this->end_controls_section();
/* ---- Section : Style – Badge ---- */
$this->start_controls_section( 'section_style_badge', [
'label' => 'Badge',
'tab' => \Elementor\Controls_Manager::TAB_STYLE,
] );
$this->add_responsive_control( 'badge_alignment', [
'label' => 'Alignement',
'type' => \Elementor\Controls_Manager::CHOOSE,
'options' => [
'flex-start' => [ 'title' => 'Gauche', 'icon' => 'eicon-text-align-left' ],
'center' => [ 'title' => 'Centre', 'icon' => 'eicon-text-align-center' ],
'flex-end' => [ 'title' => 'Droite', 'icon' => 'eicon-text-align-right' ],
],
'default' => 'flex-start',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status' => 'justify-content: {{VALUE}};',
],
] );
$this->add_group_control( \Elementor\Group_Control_Typography::get_type(), [
'name' => 'badge_typography',
'selector' => '{{WRAPPER}} .wycan-stock-status__badge',
] );
/* -- Onglets couleurs par statut -- */
$this->start_controls_tabs( 'tabs_badge_colors' );
// En stock
$this->start_controls_tab( 'tab_in_stock', [
'label' => 'En stock',
'condition' => [ 'display_mode' => 'always' ],
] );
$this->add_control( 'color_in_stock', [
'label' => 'Couleur texte',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#16a34a',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--instock' => 'color: {{VALUE}};',
],
] );
$this->add_control( 'bg_in_stock', [
'label' => 'Couleur fond',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#dcfce7',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--instock' => 'background-color: {{VALUE}};',
],
] );
$this->end_controls_tab();
// Stock faible
$this->start_controls_tab( 'tab_low_stock', [
'label' => 'Stock faible',
'condition' => [ 'display_mode' => 'always' ],
] );
$this->add_control( 'color_low_stock', [
'label' => 'Couleur texte',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#ca8a04',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--lowstock' => 'color: {{VALUE}};',
],
] );
$this->add_control( 'bg_low_stock', [
'label' => 'Couleur fond',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#fef9c3',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--lowstock' => 'background-color: {{VALUE}};',
],
] );
$this->end_controls_tab();
// Réapprovisionnement
$this->start_controls_tab( 'tab_backorder', [ 'label' => 'Réappro.' ] );
$this->add_control( 'color_backorder', [
'label' => 'Couleur texte',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#2563eb',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--onbackorder' => 'color: {{VALUE}};',
],
] );
$this->add_control( 'bg_backorder', [
'label' => 'Couleur fond',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#dbeafe',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--onbackorder' => 'background-color: {{VALUE}};',
],
] );
$this->end_controls_tab();
// Rupture
$this->start_controls_tab( 'tab_out_of_stock', [ 'label' => 'Rupture' ] );
$this->add_control( 'color_out_of_stock', [
'label' => 'Couleur texte',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#dc2626',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--outofstock' => 'color: {{VALUE}};',
],
] );
$this->add_control( 'bg_out_of_stock', [
'label' => 'Couleur fond',
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#fee2e2',
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge--outofstock' => 'background-color: {{VALUE}};',
],
] );
$this->end_controls_tab();
$this->end_controls_tabs();
$this->add_responsive_control( 'badge_padding', [
'label' => 'Padding',
'type' => \Elementor\Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', 'em', '%' ],
'default' => [
'top' => '4',
'right' => '10',
'bottom' => '4',
'left' => '10',
'unit' => 'px',
],
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
'separator' => 'before',
] );
$this->add_control( 'badge_border_radius', [
'label' => 'Border Radius',
'type' => \Elementor\Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%' ],
'default' => [
'top' => '4',
'right' => '4',
'bottom' => '4',
'left' => '4',
'unit' => 'px',
],
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
] );
$this->end_controls_section();
/* ---- Section : Style – Icône ---- */
$this->start_controls_section( 'section_style_icon', [
'label' => 'Icône',
'tab' => \Elementor\Controls_Manager::TAB_STYLE,
'condition' => [ 'show_icon' => 'yes' ],
] );
$this->add_responsive_control( 'icon_size', [
'label' => 'Taille',
'type' => \Elementor\Controls_Manager::SLIDER,
'size_units' => [ 'px' ],
'range' => [ 'px' => [ 'min' => 8, 'max' => 32 ] ],
'default' => [ 'size' => 14, 'unit' => 'px' ],
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__icon svg' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};',
],
] );
$this->add_responsive_control( 'icon_gap', [
'label' => 'Espacement',
'type' => \Elementor\Controls_Manager::SLIDER,
'size_units' => [ 'px' ],
'range' => [ 'px' => [ 'min' => 0, 'max' => 20 ] ],
'default' => [ 'size' => 6, 'unit' => 'px' ],
'selectors' => [
'{{WRAPPER}} .wycan-stock-status__badge' => 'gap: {{SIZE}}{{UNIT}};',
],
] );
$this->end_controls_section();
}
/* =====================================================================
RENDU
===================================================================== */
protected function render() {
global $product;
// Compatibilité Loop Builder : récupérer le produit courant
if ( ! $product instanceof \WC_Product ) {
$product = wc_get_product( get_the_ID() );
}
if ( ! $product instanceof \WC_Product ) {
if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
echo 'Rupture de stock (aperçu)';
}
return;
}
$settings = $this->get_settings_for_display();
$stock_status = $product->get_stock_status(); // instock | outofstock | onbackorder
$stock_qty = $product->get_stock_quantity();
$low_amount = absint( get_option( 'woocommerce_notify_low_stock_amount', 2 ) );
// Détection stock faible
$is_low_stock = ( 'instock' === $stock_status && $product->managing_stock() && null !== $stock_qty && $stock_qty <= $low_amount && $stock_qty > 0 );
$resolved_status = $is_low_stock ? 'lowstock' : $stock_status;
// Mode "rupture uniquement" : ne rien afficher si en stock / stock faible
if ( 'out_of_stock_only' === $settings['display_mode'] && ! in_array( $resolved_status, [ 'outofstock', 'onbackorder' ], true ) ) {
return;
}
// Mapping libellé / classe CSS / icône SVG
$map = [
'instock' => [
'label' => $settings['label_in_stock'] ?? 'En stock',
'icon' => '',
],
'lowstock' => [
'label' => $settings['label_low_stock'] ?? 'Stock faible',
'icon' => '',
],
'onbackorder' => [
'label' => $settings['label_on_backorder'] ?? 'En réapprovisionnement',
'icon' => '',
],
'outofstock' => [
'label' => $settings['label_out_of_stock'] ?? 'Rupture de stock',
'icon' => '',
],
];
if ( ! isset( $map[ $resolved_status ] ) ) {
return;
}
$data = $map[ $resolved_status ];
$class = 'wycan-stock-status__badge wycan-stock-status__badge--' . esc_attr( $resolved_status );
echo '';
echo '';
if ( 'yes' === $settings['show_icon'] ) {
echo '' . $data['icon'] . '';
}
echo '' . esc_html( $data['label'] ) . '';
echo '';
echo '';
}
protected function content_template() {
// Rendu côté serveur uniquement (données WooCommerce dynamiques)
}
}
$widgets_manager->register( new Wycan_Stock_Status_Widget() );
} );
/**
* Styles de base (inline, pas de fichier CSS externe)
*/
add_action( 'wp_head', function () {
if ( ! class_exists( 'WooCommerce' ) ) {
return;
}
?>

