agentsclimarketplace

Wp elementor skill

Skill guramzhgamadze/WordPress-Elementor-Skill/wp-elementor-skill

This is WordPress and Elementor skill.md made for Claude.ai

Install
npx -y skills add guramzhgamadze/WordPress-Elementor-Skill --skill wp-elementor-skill

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 19 stars19 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Expert WordPress and Elementor Pro development skill. Use this whenever the user asks about WordPress theme development, Elementor Pro customization, plugin architecture, custom post types, hooks/filters, Loop Grids, Dynamic Tags, Theme Builder templates, off-canvas UIs, custom CSS/JS injection, performance optimization, or any PHP/JS/CSS code targeting a WordPress or Elementor environment. Trigger even for general questions like "how do I add a custom field in Elementor", "what hook should I use for X in WordPress", "how do I override a WooCommerce template", or "how do I register a custom widget" — do not wait for the user to explicitly say "use the skill." Also trigger for ACF integration, REST API endpoints, child theme setup, WooCommerce customization, WPML/multilingual setups, and any debugging of WordPress/Elementor issues.

SKILL.md

30.0 KB, as published. Nobody here has run it

WordPress & Elementor Pro — Skill Router

A complete procedural guide for producing production-grade WordPress and Elementor Pro code. This file is the router — read it first, then load the relevant sub-file(s) for the task at hand.


Sub-file Map — Read the Right File for Each Task

Task typeRead this file
Plugin scaffold, child theme, code placement, CPT, AJAX handlerscaffolding.md
PHP standards, sanitization, escaping, nonces, WP_Error, transients, password hashingphp-standards.md
JavaScript standards, enqueue API, defer/async, wp_add_inline_scriptjs-css-standards.md
CSS standards, BEM, design tokens, Elementor CSS selectorsjs-css-standards.md
Elementor custom widget, Dynamic Tags, Loop Grid, Form actions, Theme Builder conditionselementor-patterns.md
Elementor extension points — custom form fields, theme locations, injecting controls into native widgets, Finder/context-menu, hooks reference, deprecationselementor-extending.md
WooCommerce HPOS, order API, template overrides, Loop Grid for productswoocommerce.md
REST API endpoints, schema, permission callbacksrest-api.md
Off-canvas UI, off-canvas accessibility, focus trapoffcanvas-ui.md
Performance checklists (frontend + backend), speculative loading, IE conditional commentsperformance.md
Accessibility checklist, WCAG 2.2 AA, ARIA patternsperformance.md
Hard-won production gotchas — widget lifecycle fatals, content_template() escaping, CSS-in-Elementor footguns, transactional email, wp.org review/packaging, embedding appsfield-notes.md
wordpress.org submission — the 18 Directory Guidelines, Plugin Check 2.0.0 categories/usage, review process, required headers/readmewp-org-guidelines.md
Subversion (SVN) — deploying/tagging a release on the wordpress.org plugin/theme SVN (trunk/tags/assets), the daily work cycle, branching/merging, properties, repo adminsvn/svn.md (self-contained sub-bundle; svn/references/ goes deeper)
Debugging & static analysis — PHPCS+WPCS, PHPStan, Plugin Check, WP_DEBUG/Query Monitor, Elementor Safe Mode/cache, symptom→cause tabledebugging.md
Common WordPress APIs — admin settings page (Settings + Options API), register_meta, roles/capabilities, WP-Cron, internationalization (i18n)wordpress-apis.md

Widget Boilerplates — Load when building a widget of that type

Widget typeRead this file
Button / CTA with icon and linkwidget-button.md
Container / layout wrapper / section / card shellwidget-container.md
Image with caption, link, lightboxwidget-image.md
Heading / title / HTML tag selectorwidget-heading.md
Rich text / WYSIWYG body contentwidget-text-editor.md
Video embed (YouTube, Vimeo, self-hosted)widget-video.md
Widget that renders a saved Elementor template by IDwidget-elementor-template.md (SELECT2, get_builder_content_for_display(), Dynamic Tags context, CSS timing)
Widget whose markup lives in a separate PHP template filewidget-php-template.md (Strategies A/B/C, load_template(), ob_start, path-traversal safety)
Divider / horizontal rule with optional text or iconwidget-divider.md
Spacer / vertical gapwidget-spacer.md
Single standalone icon with optional linkwidget-icon.md
Icon + title + description boxwidget-icon-box.md
Image + title + description boxwidget-image-box.md
Image grid / gallerywidget-image-gallery.md
Image slider / carouselwidget-image-carousel.md
Bullet list with icons per itemwidget-icon-list.md
Animated number counterwidget-counter.md
Percentage progress barwidget-progress.md
Customer quote / testimonialwidget-testimonial.md
Tabbed content panelswidget-tabs.md
Accordion — one panel open at a timewidget-accordion.md
Toggle — multiple panels open simultaneouslywidget-toggle.md
Social media icon links rowwidget-social-icons.md
Colored alert / notice boxwidget-alert.md
Audio player (SoundCloud / self-hosted)widget-audio.md
WordPress shortcode outputwidget-shortcode.md
Raw custom HTML / JS / CSS embedwidget-html.md
Named anchor for in-page navigationwidget-menu-anchor.md
WordPress registered sidebar outputwidget-sidebar.md
WordPress <!--more--> read more tagwidget-read-more.md
Google Maps embedwidget-google-maps.md
Decorative star rating displaywidget-star-rating.md
Schema-ready structured ratingwidget-rating.md
Text following a curved / custom SVG pathwidget-text-path.md
Nested Tabs / Nested Accordion (each panel is a Container holding any widgets)widget-nested.md

Always read the relevant sub-file before writing code. For tasks that span multiple areas (e.g. a WooCommerce widget with custom REST endpoint), read all relevant sub-files. For a widget task, read BOTH the widget boilerplate file AND elementor-patterns.md. For any custom widget, plugin, or wp.org-bound work, also skim field-notes.md — it catches the lint-passing, review-failing, site-down mistakes the topic files don't dwell on.


0. Golden Rules (Never Violate)

These override everything in all sub-files:

  1. Native APIs first — WordPress core hook before plugin; Elementor API before template override.
  2. Sanitize in, escape out — Every input sanitized. Every output escaped. No exceptions.
  3. Prefix everything — All functions, classes, constants, hooks, and CSS classes use a project-specific prefix.
  4. State your placement — Every code response must declare exactly where the code lives.
  5. No over-clarifying — Only ask a clarification question if the missing info would materially change the code output. Otherwise, state your assumption and proceed.
  6. NEVER hardcode visual settings in widgets — Every visual property (colors, fonts, sizes, spacing, backgrounds, borders, shadows, alignment) MUST be exposed as a standard Elementor control in the editor panel. Users control appearance via the toolbar — not by editing code. See §5 "Mandatory Widget Controls" below for the required controls checklist.
  7. Name for the directory from day one — Naming is decided first and is effectively irreversible, so get it right before writing code. The plugin slug/name must NOT start with a trademark you don't own"CRM for WooCommerce", never "WooCommerce CRM" (Directory Guideline 17). The text domain must exactly equal the plugin slug. The wp.org slug is permanent, and a widget's get_name() is sticky (stored in every page's _elementor_data — renaming it breaks placed widgets). Choose the public name, slug, text domain, and code prefix (Rule #3) together, once. This is distinct from Rule #3: that governs internal code symbols; this governs the public identity. See wp-org-guidelines.md (Guidelines 12/16/17) and field-notes.md §1 (sticky get_name()).

1. Default Assumptions

Quickly assess — only ask if the answer would change the code:

Info neededAsk only if...
Scope (plugin vs snippet vs child theme)Context doesn't make it obvious
Elementor tier (Free / Pro / custom widget)Pro-only APIs are involved
PHP versionCode uses PHP 8.3+ features like typed class constants, or 8.4+ features like property hooks
WooCommerce / ACF / WPML presentIntegration with those systems is required

Default stack when not stated (full release-by-release history lives in CHANGELOG.md — keep volatile version-tracking out of this router):

ComponentVersionNotes
WordPress7.0+"Armstrong", released May 20, 2026; current point release 7.0.2 (security, Jul 17, 2026). Minimum PHP raised to 7.4 (7.2/7.3 dropped — sites still on them stay pinned to 6.9.x). No multisite assumed. WP 7.1 is scheduled for Aug 19, 2026.
PHP8.3 recommended7.4 = WP 7.0 minimum. 8.4 / 8.5 = "beta support" (possible deprecation notices). 8.2 fully compatible but no longer the recommended default.
Elementor (free + Pro)4.2+Separate plugins with independent version numbers. 4.0.0 (Mar 30, 2026) made the Atomic Editor stable + default for new installs; free and Pro both reached 4.2.0 on Jul 20, 2026 (free: Atomic Grid; Pro: Atomic Loop). V3 Widget_Base remains fully supported — all skill code targets V3 and is production-safe.
WooCommerce10.9+HPOS default-on since 8.2. 10.7 (Apr 14, 2026) disabled HPOS "sync on read" by default; 10.9 (Jun 23, 2026) defers Store API draft-order creation — see woocommerce.md.

Note: Elementor core and Elementor Pro have independent version numbers — always check both when diagnosing compatibility issues.

WordPress 7.0 — what changed for plugin / Elementor devs

WP 7.0 "Armstrong" shipped May 20, 2026 (delayed from the original April 9 target while the RTC storage layer was redesigned — see below). Everything below is opt-in and non-breaking; most plugin/Elementor work is unaffected.

  • Minimum PHP is now 7.4 (7.2/7.3 dropped). The skill's recommended baseline stays PHP 8.3. Bump your plugin's Requires PHP header to 7.4 only once you target WP 7.0+ exclusively. No new DB minimum is enforced; wordpress.org/about/requirements/ recommends MariaDB 10.6+ or MySQL 8.0+.
  • Real-Time Collaboration (RTC): simultaneous multi-author block editing (CRDT-based, via an HTTP-polling sync provider — not WebRTC). Data is stored in a dedicated core database table; an earlier wp_post_meta / wp_sync_storage design was rejected, and building the table is what pushed the release from April to May. Plugin impact: scope every WP_Query / get_posts() with an explicit post_type so internal core post types never leak into your results — do not hardcode any internal RTC type name. The WP_ALLOW_COLLABORATION constant lets hosts swap the sync transport.
  • WP AI Client: provider-agnostic PHP + JS AI API — wp_ai_client_prompt( $prompt )->generate_text(). Guard with function_exists( 'wp_ai_client_prompt' ).
  • Abilities API: wp_register_ability() (PHP, since WP 6.9) plus a JS counterpart in 7.0. Use 'meta' => ['show_in_rest' => true] to expose via REST. Register ONLY inside the wp_abilities_api_init hook (core renamed the feature plugin's abilities_api_init; categories go on wp_abilities_api_categories_init) or you get _doing_it_wrong; a category is required and must already be registered. Calling it also sets your real Requires at least floor to 6.9 — Plugin Check errors on the header and function_exists() guards don't count (see debugging.md §1).
  • Connectors UI (Settings → Connectors) for managing AI provider credentials, and a Command Palette in wp-admin.
  • Iframed editor remains punted to a later release — prepare with "apiVersion": 3 in block.json.

Sources: make.wordpress.org/core/2026/01/09/dropping-support-for-php-7-2-and-7-3/ · make.wordpress.org/core/2026/04/22/wordpress-7-0-release-party-updated-schedule/ · wordpress.org/about/requirements/

📌 PHP support labels (WP 7.0): PHP 7.4–8.3 fully compatible; 8.3 recommended; 8.4 (WP 6.7+) and 8.5 (WP 6.9+) carry a "beta support" label (possible deprecation notices). Source: make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/

Elementor 4.x status (current: 4.2.0 free AND Pro, July 20, 2026): Elementor 4.0.0 (Mar 30, 2026, free + Pro) made the Atomic Editor stable and the default for new installs and added Atomic Forms, Pro Interactions, and Component creation. Updating to 4.x leaves existing sites untouched — V3 widgets and V4 Atomic Elements coexist on the same page; Atomic features are toggled at WP Admin → Elementor → Editor → Settings. The V4 Atomic Element PHP extension API is stable, but third-party extension docs are still being finalized (re-verified July 22, 2026: developers.elementor.com still documents only V3) — so continue using V3 Widget_Base for all third-party widgets. It is the correct, production-safe API and all skill code targets it.

V4 Atomic Elements that now ship by default (awareness only — not third-party-buildable yet): Div Block & Flexbox Container (layout); Atomic Grid — advanced row/column layouts (free 4.2.0, Jul 20, 2026); Atomic Heading, Paragraph, Image, Button, Video, SVG; Atomic Tabs; Atomic Forms (Pro) with composable fields — Label, Input, Textarea, Checkbox, Submit, plus Radio, Select, Date Picker, Time Picker, and File Upload (added in Pro 4.1.0, May 26, 2026); and Atomic Loop — dynamic content layouts, the V4 counterpart of the Pro Loop Grid (Pro 4.2.0, Jul 20, 2026). These are end-user elements; building custom atomic elements still awaits the finalized V4 extension docs — keep targeting V3 Widget_Base until then. Source: elementor.com/products/website-builder/v4-faq/ · developers.elementor.com/elementor-editor-4-0-developers-update/ · elementor.com/pro/changelog/ · github.com/elementor/elementor/releases

🗓️ Release-by-release history (betas, RCs, point releases) lives in CHANGELOG.md. Keep this router focused on durable guidance; update version facts in the table above and in CHANGELOG.md, not scattered across the sub-files.


2. Architecture Decision Tree

Run through this mentally before writing a single line:

Does a WordPress core hook (add_action/add_filter) solve it?
  YES → Use the hook. No plugin needed. Place in child theme functions.php
        or Elementor Custom Code.
  NO  → Does Elementor's PHP/JS API solve it?
          YES → Extend via Elementor hooks, Dynamic Tags, or Widget_Base.
                Register via elementor/widgets/register or elementor/dynamic_tags/register.
          NO  → Is this logic reusable across themes or sites?
                  YES → Scaffold a dedicated plugin (see scaffolding.md).
                  NO  → Child theme functions.php or Elementor Custom Code block.

Is a WooCommerce override needed?
  → Use Elementor Loop Grid + custom query filter BEFORE touching template files.
  → Only override woocommerce/ templates as an absolute last resort.

Is this a REST API endpoint?
  → Always register via register_rest_route() inside a plugin, never in functions.php.

3. Mandatory Output Format

Every single code response must follow this structure — no exceptions:

📍 PLACEMENT
Exact file path or Elementor hook location.
e.g. /wp-content/plugins/myplugin/includes/class-myplugin-hooks.php
     Elementor → Site Settings → Custom Code → wp_footer

⚙️ REQUIRES
WordPress X.X+ | PHP X.X+ | Elementor Pro X.X+ | ACF X.X+ | WooCommerce X.X+
(list only what the code actually depends on)

💡 WHY THIS APPROACH
One paragraph: which branch of the §2 decision tree was taken and why.

📋 CODE
Complete, commented, deployment-ready code block — no truncation, no omissions.

🔧 INTEGRATION NOTES (include when relevant)
Any manual steps required: flush rewrite rules, set Query ID in Elementor editor,
activate plugin, clear Elementor cache, etc.

4. Quick Reference — Pattern Index

TaskApproachSub-file
Plugin scaffoldSingleton + hooks class + assets class + HPOS declarationscaffolding.md
Child theme setupwp_enqueue_style parent + child in functions.phpscaffolding.md
Custom Post Type + Taxonomyregister_post_type() + register_taxonomy() in pluginscaffolding.md
Secure AJAX handlerwp_ajax_ hooks + nonce verify + wp_send_json_*scaffolding.md
PHP sanitization / escapingwp_unslash() + sanitize_* + esc_* patternsphp-standards.md
Transient cachingget_transient / set_transientphp-standards.md
External API call + WP_Errorwp_remote_get() + WP_Error patternphp-standards.md
Admin settings pageSettings API + register_setting + sanitize_callback + settings_fieldswordpress-apis.md
Store plugin optionsOptions API + explicit boolean autoload (WP 6.6+)wordpress-apis.md
Custom field exposed to REST / Elementorregister_post_meta + show_in_restwordpress-apis.md
Scheduled / background taskWP-Cron (wp_schedule_event) + Action Scheduler for heavy jobswordpress-apis.md
Make a plugin translatablei18n functions + text-domain = slug + WP 6.7 init-timing rulewordpress-apis.md
Deploy / tag a plugin release on wp.org SVNsvn co → copy to trunk/svn cp trunk tags/X.Y.Zsvn ci (assets/ is a sibling)svn/svn.md
Any SVN task (checkout, commit, branch, merge, conflicts, repo admin)Subversion sub-bundlesvn/svn.md
WP 6.8 password hashingwp_check_password() + wp_password_needs_rehash()php-standards.md
WP 6.8 app password / key hashingwp_fast_hash() + wp_verify_fast_hash() (BLAKE2b)php-standards.md
JS standards + enqueue defer/asyncIIFE + WP 6.3+ enqueue APIjs-css-standards.md
PHP → JS data passingwp_add_inline_script() with wp_json_encode()js-css-standards.md
CSS BEM + design tokensScoped tokens, 8pt spacing, fluid typejs-css-standards.md
Custom Elementor widgetWidget_Base + all required methodselementor-patterns.md
Elementor Dynamic TagTag class + elementor/dynamic_tags/registerelementor-patterns.md
Elementor Loop Grid queryelementor/query/ filterelementor-patterns.md
ACF field in ElementorDynamic Tag extending \Elementor\Core\DynamicTags\Tagelementor-patterns.md
Elementor Pro Form actionAction_Base + field iterationelementor-patterns.md
Theme Builder custom conditionCondition_Base + elementor/theme/register_conditionselementor-patterns.md
Elementor Pro custom form fieldField_Base + elementor_pro/forms/fields/registerelementor-extending.md
Theme Builder custom locationregister_location + elementor_theme_do_location()elementor-extending.md
Add a control to a NATIVE Elementor widgetelementor/element/{el}/{section}/before_section_endelementor-extending.md
Filter a native widget's outputelementor/widget/render_content filterelementor-extending.md
WooCommerce HPOS compatibilityFeaturesUtil::declare_compatibility() + wc_get_order()woocommerce.md
WooCommerce loopLoop Grid + elementor/query/ filterwoocommerce.md
Custom REST endpointregister_rest_route() + schema callback in pluginrest-api.md
Off-canvas filter panelBEM CSS + ARIA JS + Custom Code blockoffcanvas-ui.md
Performance checklistFrontend + backend checklistsperformance.md
Accessibility checklistWCAG 2.2 AA patternsperformance.md
Elementor dependency checkRequires Plugins: elementor header + did_action fallbackscaffolding.md
Widget with button/CTAControls + render matching native Button widgetwidget-button.md
Widget with container/layoutControls + render matching native Containerwidget-container.md
Widget with imageGroup_Control_Image_Size + get_attachment_image_html()widget-image.md
Widget with headingTEXTAREA + header_size tag selectorwidget-heading.md
Widget with rich textWYSIWYG + wp_kses_post outputwidget-text-editor.md
Widget with video embedSource SELECT + overlay + aspect ratiowidget-video.md
Widget rendering saved Elementor templateget_builder_content_for_display() + SELECT2widget-elementor-template.md
Widget with PHP template fileload_template() + locate_template() strategies A/B/Cwidget-php-template.md
Widget with divider lineStyle + width + optional text/icon elementwidget-divider.md
Widget with spacer gapSingle responsive SLIDERwidget-spacer.md
Widget with single iconICONS control + size + color tabswidget-icon.md
Widget with icon + text boxIcon + title + description patternwidget-icon-box.md
Widget with image + text boxImage + title + description patternwidget-image-box.md
Widget with image gridGALLERY control + Group_Control_Image_Sizewidget-image-gallery.md
Widget with image sliderGALLERY + Swiper + navigation controlswidget-image-carousel.md
Widget with icon bullet listREPEATER + icon + text + optional linkwidget-icon-list.md
Widget with animated counterNumber + prefix/suffix + durationwidget-counter.md
Widget with progress barPercentage SLIDER + bar stylingwidget-progress.md
Widget with testimonial quoteContent + image + name + job titlewidget-testimonial.md
Widget with tabbed panelsREPEATER tabs + horizontal/vertical typewidget-tabs.md
Widget with accordionREPEATER + single-open collapse patternwidget-accordion.md
Widget with toggle panelsREPEATER + multi-open toggle patternwidget-toggle.md
Widget with social iconsREPEATER + brand icons + linkswidget-social-icons.md
Widget with alert/notice boxType SELECT + title + description + dismisswidget-alert.md
Widget with audio playerSoundCloud URL + autoplay optionswidget-audio.md
Widget outputting shortcodeTEXTAREA + do_shortcode()widget-shortcode.md
Widget with raw HTML embedCODE control + unescaped outputwidget-html.md
Widget as named anchorTEXT ID + sanitize_html_class()widget-menu-anchor.md
Widget outputting sidebarRegistered sidebar SELECT + dynamic_sidebar()widget-sidebar.md
Widget with read more tagNo controls — WordPress $more globalwidget-read-more.md
Widget with Google MapsAddress TEXT + zoom SLIDER + iframewidget-google-maps.md
Widget with star rating displayScale + rating number + icon stylewidget-star-rating.md
Widget with schema ratingIcon count + fractional rating + gapwidget-rating.md
Widget with curved/path text<svg> + <textPath> + unique path idwidget-text-path.md
Nested Tabs / Accordion widgetWidget_Nested_Base + print_child() + container panelswidget-nested.md

5. Mandatory Widget Controls — No Hardcoded Visuals (NEVER VIOLATE)

This section is MANDATORY for every custom Elementor widget. Whenever you build a widget, every visual property must be an Elementor control — NEVER a hardcoded CSS value. Users control appearance from the editor panel/toolbar, not by editing source code.

The Rule

NEVER hardcode any of the following in PHP render(), in static CSS, or in content_template() output:

  • Colors (text, background, border, shadow)
  • Typography (font family, size, weight, line-height, letter-spacing, transform)
  • Spacing (padding, margin, gap)
  • Sizing (width, height, min/max values)
  • Borders (style, width, color, radius)
  • Shadows (box-shadow, text-shadow)
  • Backgrounds (color, gradient, image)
  • Alignment / positioning
  • Opacity, transitions, hover effects

ALL of the above must use Elementor controls with selectors that inject CSS dynamically. The only exceptions are structural CSS (display, position, overflow) required for the widget layout to function at all — and even these should use controls when there is a user-facing choice (e.g. flex-direction toggle).

Required Controls Checklist — Apply to Every Widget

When building a widget, include ALL controls that apply to its visual elements. Use this checklist as a mandatory gate:

Visual propertyRequired Elementor controlTab
Text contentTEXT, TEXTAREA, or WYSIWYG + 'dynamic' => ['active' => true]TAB_CONTENT
Typography (any text element)add_group_control( Group_Control_Typography::get_type() )TAB_STYLE
Text colorCOLOR control with selectorsTAB_STYLE
Text alignmentadd_responsive_control() with CHOOSE (left/center/right/justify)TAB_STYLE or TAB_CONTENT
Backgroundadd_group_control( Group_Control_Background::get_type() )TAB_STYLE
Borderadd_group_control( Group_Control_Border::get_type() )TAB_STYLE
Border radiusadd_responsive_control() with DIMENSIONS + 'selectors'TAB_STYLE
Box shadowadd_group_control( Group_Control_Box_Shadow::get_type() )TAB_STYLE
Text shadowadd_group_control( Group_Control_Text_Shadow::get_type() )TAB_STYLE
Paddingadd_responsive_control() with DIMENSIONSTAB_STYLE
Marginadd_responsive_control() with DIMENSIONSTAB_STYLE
Width / Heightadd_responsive_control() with SLIDERTAB_STYLE
Spacing / Gapadd_responsive_control() with SLIDERTAB_STYLE
ImageMEDIA + add_group_control( Group_Control_Image_Size::get_type() )TAB_CONTENT
CSS Filters (if image/element)add_group_control( Group_Control_Css_Filter::get_type() )TAB_STYLE
Hover stateDuplicate color/background/shadow controls inside 'section_style_hover' with selectors targeting :hoverTAB_STYLE
Transition durationSLIDER (seconds) with selectors => ['transition-duration']TAB_STYLE
LinkURL control with 'dynamic' => ['active' => true]TAB_CONTENT
IconICONS control with fa4compatibilityTAB_CONTENT
HTML tagSELECT (h1–h6, div, span, p)TAB_CONTENT

Example — Correct vs Incorrect

// ❌ WRONG — hardcoded color and font-size
protected function render(): void {
    $settings = $this->get_settings_for_display();
    echo '<h2 style="color: #e94560; font-size: 24px;">'
        . esc_html( $settings['title'] ) . '</h2>';
}

// ✅ CORRECT — all visuals controlled from the panel via selectors
protected function register_controls(): void {
    // ... Content section with title TEXT control ...

    $this->start_controls_section( 'section_title_style', [
        'label' => esc_html__( 'Title Style', 'myplugin' ),
        'tab'   => \Elementor\Controls_Manager::TAB_STYLE,
    ] );

    $this->add_control( 'title_color', [
        'label'     => esc_html__( 'Color', 'myplugin' ),
        'type'      => \Elementor\Controls_Manager::COLOR,
        'selectors' => [
            '{{WRAPPER}} .myplugin-widget__title' => 'color: {{VALUE}};',
        ],
    ] );

    $this->add_group_control(
        \Elementor\Group_Control_Typography::get_type(),
        [
            'name'     => 'title_typography',
            'selector' => '{{WRAPPER}} .myplugin-widget__title',
        ]
    );

    $this->add_responsive_control( 'title_align', [
        'label'   => esc_html__( 'Alignment', 'myplugin' ),
        'type'    => \Elementor\Controls_Manager::CHOOSE,
        'options' => [
            'left'   => [ 'title' => esc_html__( 'Left',   'myplugin' ), 'icon' => 'eicon-text-align-left' ],
            'center' => [ 'title' => esc_html__( 'Center', 'myplugin' ), 'icon' => 'eicon-text-align-center' ],
            'right'  => [ 'title' => esc_html__( 'Right',  'myplugin' ), 'icon' => 'eicon-text-align-right' ],
        ],
        'selectors' => [
            '{{WRAPPER}} .myplugin-widget__title' => 'text-align: {{VALUE}};',
        ],
    ] );

    $this->add_responsive_control( 'title_spacing', [
        'label'      => esc_html__( 'Bottom Spacing', 'myplugin' ),
        'type'       => \Elementor\Controls_Manager::SLIDER,
        'size_units' => [ 'px', 'em', 'rem' ],
        'range'      => [ 'px' => [ 'min' => 0, 'max' => 100 ] ],
        'selectors'  => [
            '{{WRAPPER}} .myplugin-widget__title' => 'margin-bottom: {{SIZE}}{{UNIT}};',
        ],
    ] );

    $this->end_controls_section();
}

protected function render(): void {
    $settings = $this->get_settings_for_display();
    // ✅ No inline styles — all visuals come from Elementor's selectors
    $this->add_render_attribute( 'title', 'class', 'myplugin-widget__title' );
    $this->add_inline_editing_attributes( 'title' );
    echo '<h2 ' . $this->get_render_attribute_string( 'title' ) . '>'
        . esc_html( $settings['title'] ) . '</h2>';
}

add_render_attribute() and add_inline_editing_attributes() — Always Use

The official Elementor API for building HTML attributes is $this->add_render_attribute(). Always use it instead of manually concatenating class/id/aria attributes in render(). Pair with $this->add_inline_editing_attributes() for any text field that supports live editing in the Elementor editor panel.

Outputting the built attributes — pick by context: use get_render_attribute_string( 'key' ) when you are concatenating into a string (as in the echo '<h2 ' . ... . '>' example above), and print_render_attribute_string( 'key' ) when you are echoing directly inside a ?> … <?php HTML block (e.g. <h2 <?php $this->print_render_attribute_string( 'title' ); ?>>). Both are correct Elementor APIs — print_* simply echoes what get_* returns. The widget sub-files use the print_* form inside their HTML templates.

Source: developers.elementor.com/docs/widgets/rendering-html-attribute/ Source: developers.elementor.com/docs/widgets/rendering-inline-editing/

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.