Token导航 LogoToken导航

Agent Skill · 前端设计

magento2-widget-creation(magento2 小部件创建)

magento2-widget-creation 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

magento2-widget-creation 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 9,985。

总安装 188周安装 8GitHub Stars 11下载 66核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:magento2-widget-creation(magento2 小部件创建)
来源仓库:https://github.com/proxiblue/claude-skills
仓库路径:skills/magento2-widget-creation
安装命令:
npx skills add https://github.com/proxiblue/claude-skills --skill magento2-widget-creation
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/proxiblue/claude-skills --skill magento2-widget-creation

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

magento2-widget-creation 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。适合在 Codex、Claude、Cursor、Gemini CLI 中围绕仓库状态、代码变更或协作事项进行整理时使用。

通过 npx skills add 命令从指定 GitHub 仓库安装,需结合原始 README 核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写操作。该技能适用于 Magento 2 小部件创建与开发相关的协作支持场景。

来源仓库内路径:skills/magento2-widget-creation

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/proxiblue/claude-skills --skill magento2-widge…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

magento2-widget-creation 怎么安装?

在终端执行 npx skills add https://github.com/proxiblue/claude-skills --skill magento2-widget-creation。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

magento2-widget-creation 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

magento2-widget-creation 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

magento2-widget-creation 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

magento2-widget-creation 用的人多吗?

公开数据是累计安装 188 次,近一周安装 8 次,下载 66 次,GitHub 11 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

magento2-widget-creation 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
magento2-widget-creation

magento2 小部件创建 · 本页

18811需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Magento 2 Widget Creation for CMS Pages

Purpose

This skill provides comprehensive guidance on creating custom widget modules in Magento 2 (standard Luma/Blank themes, not Hyvä-based) that can be inserted into CMS pages, CMS blocks, or any content area using the widget system.

When to Use This Skill

Use this skill when you need to:

  • Create a reusable component that can be inserted into CMS pages
  • Build interactive elements (buttons, forms, modals) for content editors
  • Develop custom functionality that non-technical users can add to pages
  • Create widgets with configurable parameters that appear in the admin panel
  • Implement widgets that work with standard Magento themes (Luma/Blank)

Do NOT use this skill for:

  • Hyvä theme widgets (use hyva-tailwind-integration skill instead)
  • Backend admin widgets
  • UI components or admin grids

Prerequisites

  • Existing vendor namespace or willingness to create one
  • Basic understanding of Magento 2 module structure
  • Knowledge of XML configuration
  • Familiarity with Magento templates and blocks
  • Understanding of JavaScript widget pattern (optional, for interactive widgets)

Widget Module Structure

A complete widget module requires these components:

app/code/Vendor/ModuleName/
├── registration.php                          # Module registration
├── etc/
│   ├── module.xml                           # Module configuration
│   ├── widget.xml                           # Widget definition
│   ├── email_templates.xml                  # (Optional) Email templates
│   └── frontend/
│       └── routes.xml                       # (Optional) For form submissions
├── Block/
│   └── Widget/
│       └── WidgetName.php                   # Widget block class
├── Controller/                              # (Optional) For form handlers
│   └── Index/
│       └── Submit.php
└── view/frontend/
    ├── templates/
    │   └── widget/
    │       └── template.phtml               # Widget template
    ├── layout/
    │   └── default.xml                      # (Optional) Load CSS/JS globally
    ├── web/
    │   ├── js/
    │   │   └── widget-script.js             # (Optional) Custom JS
    │   └── css/
    │       └── widget-style.css             # (Optional) Custom CSS
    ├── requirejs-config.js                  # (Optional) JS module mapping
    └── email/                               # (Optional) Email templates
        └── template.html

Step-by-Step Widget Creation

Step 1: Create Module Registration

File: registration.php

<?php
/**
 * Copyright © Vendor. All rights reserved.
 */

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Vendor_ModuleName',
    __DIR__
);

Step 2: Create Module Configuration

File: etc/module.xml

<?xml version="1.0"?>
<!--
/**
 * Copyright © Vendor. All rights reserved.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_ModuleName" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Cms"/>
            <module name="Magento_Widget"/>
        </sequence>
    </module>
</config>

Key Points:

  • setup_version is legacy but still commonly used
  • Add dependencies in <sequence> - Magento_Cms and Magento_Widget are required for widgets
  • Add other modules your widget depends on (e.g., Magento_Email, Magento_Catalog)

Step 3: Create Widget Configuration

File: etc/widget.xml

This is where you define your widget's metadata and configurable parameters.

<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
 * Copyright © Vendor. All rights reserved.
 */
-->
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
    <widget id="widget_unique_id" class="Vendor\ModuleName\Block\Widget\WidgetName">
        <label translate="true">Widget Display Name</label>
        <description translate="true">Brief description of what the widget does</description>
        <parameters>
            <!-- Text Parameter -->
            <parameter name="text_param" xsi:type="text" required="false" visible="true">
                <label translate="true">Text Label</label>
                <description translate="true">Description shown in admin</description>
            </parameter>

            <!-- Select/Dropdown Parameter -->
            <parameter name="select_param" xsi:type="select" required="false" visible="true">
                <label translate="true">Select Option</label>
                <options>
                    <option name="option1" value="value1">
                        <label>Option 1</label>
                    </option>
                    <option name="option2" value="value2">
                        <label>Option 2</label>
                    </option>
                </options>
            </parameter>

            <!-- Yes/No Parameter -->
            <parameter name="enabled" xsi:type="select" required="false" visible="true"
                       source_model="Magento\Config\Model\Config\Source\Yesno">
                <label translate="true">Enable Feature</label>
            </parameter>

            <!-- CMS Block Chooser -->
            <parameter name="block_id" xsi:type="block" visible="true" required="false">
                <label translate="true">CMS Block</label>
                <block class="Magento\Cms\Block\Adminhtml\Block\Widget\Chooser">
                    <data>
                        <item name="button" xsi:type="array">
                            <item name="open" xsi:type="string">Select Block...</item>
                        </item>
                    </data>
                </block>
            </parameter>

            <!-- Category Chooser -->
            <parameter name="category_id" xsi:type="select" visible="true"
                       source_model="Magento\Catalog\Model\Category\Attribute\Source\Categories">
                <label translate="true">Category</label>
            </parameter>
        </parameters>
    </widget>
</widgets>

Widget Parameter Types:

  • text - Simple text input
  • select - Dropdown selection
  • multiselect - Multiple selections
  • block - CMS block picker
  • page - CMS page picker
  • conditions - Product/category conditions (advanced)

Important Attributes:

  • id - Unique identifier for the widget
  • class - Full namespaced path to your block class
  • required - Whether parameter is mandatory
  • visible - Whether parameter shows in admin

Step 4: Create Block Class

File: Block/Widget/WidgetName.php

The block class handles the widget's logic and data.

<?php
/**
 * Copyright © Vendor. All rights reserved.
 */
declare(strict_types=1);

namespace Vendor\ModuleName\Block\Widget;

use Magento\Framework\View\Element\Template;
use Magento\Widget\Block\BlockInterface;
use Magento\Framework\View\Element\Template\Context;

class WidgetName extends Template implements BlockInterface
{
    /**
     * Template path relative to view/frontend/templates/
     *
     * @var string
     */
    protected $_template = 'Vendor_ModuleName::widget/template.phtml';

    /**
     * @param Context $context
     * @param array $data
     */
    public function __construct(
        Context $context,
        array $data = []
    ) {
        parent::__construct($context, $data);
    }

    /**
     * Get widget parameter with default value
     *
     * @return string
     */
    public function getParameterValue(): string
    {
        return $this->getData('text_param') ?: 'default value';
    }

    /**
     * Get widget select parameter
     *
     * @return string
     */
    public function getSelectValue(): string
    {
        return $this->getData('select_param') ?: 'value1';
    }

    /**
     * Check if feature is enabled
     *
     * @return bool
     */
    public function isEnabled(): bool
    {
        return (bool)$this->getData('enabled');
    }

    /**
     * Get URL for AJAX or form submission
     *
     * @return string
     */
    public function getActionUrl(): string
    {
        return $this->getUrl('modulename/index/submit');
    }
}

Best Practices:

  • Always declare(strict_types=1);
  • Implement BlockInterface
  • Use $this->getData('param_name') to access widget parameters
  • Provide default values with ?: operator
  • Add type hints and return types
  • Keep business logic out of templates - put it in block methods

Step 5: Create Template File

File: view/frontend/templates/widget/template.phtml

Templates render the HTML output. Always escape data for security.

<?php
/**
 * Copyright © Vendor. All rights reserved.
 *
 * @var $block Vendor\ModuleName\Block\Widget\WidgetName
 * @var $escaper Magento\Framework\Escaper
 */

$paramValue = $block->getParameterValue();
$selectValue = $block->getSelectValue();
$isEnabled = $block->isEnabled();
$uniqueId = uniqid('widget_');
?>

<?php if ($isEnabled): ?>
<div class="custom-widget" id="<?= $escaper->escapeHtmlAttr($uniqueId) ?>">
    <div class="widget-content">
        <h3><?= $escaper->escapeHtml(__('Widget Title')) ?></h3>
        <p><?= $escaper->escapeHtml($paramValue) ?></p>

        <?php if ($selectValue === 'value1'): ?>
            <div class="option-one-content">
                <!-- Content for option 1 -->
            </div>
        <?php endif; ?>

        <button type="button"
                class="action primary widget-button"
                data-mage-init='{"widgetName": {"elementId": "<?= $escaper->escapeJs($uniqueId) ?>"}}'>
            <?= $escaper->escapeHtml(__('Click Me')) ?>
        </button>
    </div>
</div>
<?php endif; ?>

Template Best Practices:

  • Always use $escaper->escapeHtml() for text content
  • Use $escaper->escapeHtmlAttr() for HTML attributes
  • Use $escaper->escapeJs() for JavaScript strings
  • Use $escaper->escapeUrl() for URLs
  • Use __() for translatable strings
  • Generate unique IDs to avoid conflicts (use uniqid())
  • Add proper @var comments for IDE support

Common Escaping Methods:

// Text content
<?= $escaper->escapeHtml($text) ?>

// HTML attributes
<div class="<?= $escaper->escapeHtmlAttr($class) ?>">

// JavaScript strings
data-value="<?= $escaper->escapeJs($value) ?>"

// URLs
<a href="<?= $escaper->escapeUrl($url) ?>">

// CSS
<div style="<?= $escaper->escapeCss($style) ?>">

Step 6: Add JavaScript (Optional)

If your widget needs interactivity, add JavaScript using Magento's widget pattern.

File: view/frontend/requirejs-config.js

/**
 * Copyright © Vendor. All rights reserved.
 */
var config = {
    map: {
        '*': {
            widgetName: 'Vendor_ModuleName/js/widget-script'
        }
    }
};

File: view/frontend/web/js/widget-script.js

/**
 * Copyright © Vendor. All rights reserved.
 */
define([
    'jquery',
    'jquery-ui-modules/widget'
], function ($) {
    'use strict';

    /**
     * Widget initialization pattern
     */
    $.widget('vendor.widgetName', {
        options: {
            elementId: '',
            ajaxUrl: ''
        },

        /**
         * Widget creation
         * @private
         */
        _create: function () {
            this._bind();
        },

        /**
         * Bind event handlers
         * @private
         */
        _bind: function () {
            var self = this;

            this.element.on('click', function (e) {
                e.preventDefault();
                self._handleClick();
            });
        },

        /**
         * Handle click event
         * @private
         */
        _handleClick: function () {
            console.log('Widget clicked!');
            console.log('Element ID:', this.options.elementId);

            // Example AJAX call
            if (this.options.ajaxUrl) {
                this._makeAjaxRequest();
            }
        },

        /**
         * Make AJAX request
         * @private
         */
        _makeAjaxRequest: function () {
            var self = this;

            $.ajax({
                url: this.options.ajaxUrl,
                type: 'POST',
                dataType: 'json',
                data: {
                    // Your data here
                },
                success: function (response) {
                    self._handleResponse(response);
                },
                error: function () {
                    console.error('Request failed');
                }
            });
        },

        /**
         * Handle AJAX response
         * @private
         */
        _handleResponse: function (response) {
            if (response.success) {
                console.log('Success:', response.message);
            } else {
                console.error('Error:', response.message);
            }
        }
    });

    return $.vendor.widgetName;
});

Initialization in Template:

<button type="button"
        data-mage-init='{"widgetName": {
            "elementId": "<?= $escaper->escapeJs($uniqueId) ?>",
            "ajaxUrl": "<?= $escaper->escapeUrl($block->getActionUrl()) ?>"
        }}'>
    Click Me
</button>

Alternative Initialization with data-bind (Knockout.js):

<!-- For Knockout.js integration -->
<div data-bind="scope: 'widget-scope'">
    <!-- content -->
</div>

<script type="text/x-magento-init">
{
    "[data-bind]": {
        "Magento_Ui/js/core/app": {
            "components": {
                "widget-scope": {
                    "component": "Vendor_ModuleName/js/widget-component"
                }
            }
        }
    }
}
</script>

Step 7: Add CSS Styling (Optional)

File: view/frontend/layout/default.xml

Load your CSS globally across all pages.

<?xml version="1.0"?>
<!--
/**
 * Copyright © Vendor. All rights reserved.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <css src="Vendor_ModuleName::css/widget-style.css"/>
    </head>
</page>

File: view/frontend/web/css/widget-style.css

/**
 * Copyright © Vendor. All rights reserved.
 */

/* Widget Container */
.custom-widget {
    padding: 20px;
    margin: 10px 0;
}

.custom-widget .widget-content {
    background: #f5f5f5;
    padding: 15px;
    border-radius: 5px;
}

.custom-widget h3 {
    margin: 0 0 10px;
    font-size: 18px;
    font-weight: 600;
}

.custom-widget .widget-button {
    margin-top: 10px;
}

/* Responsive Design */
@media (max-width: 768px) {
    .custom-widget {
        padding: 15px;
    }

    .custom-widget .widget-content {
        padding: 10px;
    }
}

CSS Best Practices:

  • Use specific class names to avoid conflicts
  • Follow mobile-first approach with media queries
  • Respect existing theme styles
  • Use CSS variables for theming (if supported)
  • Avoid !important unless absolutely necessary

Advanced: Adding Controllers for Form Submission

For widgets that need to process data (forms, AJAX requests), add a controller.

Step 1: Create Frontend Routes

File: etc/frontend/routes.xml

<?xml version="1.0"?>
<!--
/**
 * Copyright © Vendor. All rights reserved.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="modulename" frontName="modulename">
            <module name="Vendor_ModuleName" />
        </route>
    </router>
</config>

Route URL Pattern:

  • URL: https://yourstore.com/modulename/index/submit
  • modulename = frontName
  • index = controller directory
  • submit = action file name

Step 2: Create Controller Action

File: Controller/Index/Submit.php

<?php
/**
 * Copyright © Vendor. All rights reserved.
 */
declare(strict_types=1);

namespace Vendor\ModuleName\Controller\Index;

use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Exception\LocalizedException;
use Psr\Log\LoggerInterface;

/**
 * Handle form submission
 */
class Submit implements HttpPostActionInterface
{
    /**
     * @var RequestInterface
     */
    private $request;

    /**
     * @var JsonFactory
     */
    private $resultJsonFactory;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @param RequestInterface $request
     * @param JsonFactory $resultJsonFactory
     * @param LoggerInterface $logger
     */
    public function __construct(
        RequestInterface $request,
        JsonFactory $resultJsonFactory,
        LoggerInterface $logger
    ) {
        $this->request = $request;
        $this->resultJsonFactory = $resultJsonFactory;
        $this->logger = $logger;
    }

    /**
     * Execute action
     *
     * @return \Magento\Framework\Controller\Result\Json
     */
    public function execute()
    {
        $resultJson = $this->resultJsonFactory->create();

        if (!$this->request->isPost()) {
            return $resultJson->setData([
                'success' => false,
                'message' => __('Invalid request method.')
            ]);
        }

        try {
            $postData = $this->request->getPostValue();

            // Validate data
            $this->validateData($postData);

            // Process your data here
            // Example: Save to database, send email, etc.

            return $resultJson->setData([
                'success' => true,
                'message' => __('Your request has been submitted successfully.')
            ]);
        } catch (LocalizedException $e) {
            $this->logger->error('Widget form error: ' . $e->getMessage());
            return $resultJson->setData([
                'success' => false,
                'message' => $e->getMessage()
            ]);
        } catch (\Exception $e) {
            $this->logger->error('Widget form error: ' . $e->getMessage());
            return $resultJson->setData([
                'success' => false,
                'message' => __('An error occurred. Please try again later.')
            ]);
        }
    }

    /**
     * Validate form data
     *
     * @param array $data
     * @throws LocalizedException
     */
    private function validateData(array $data): void
    {
        if (empty($data['field_name'])) {
            throw new LocalizedException(__('Field name is required.'));
        }

        if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            throw new LocalizedException(__('Please enter a valid email address.'));
        }
    }
}

Controller Best Practices:

  • Use HttpPostActionInterface for POST requests
  • Use HttpGetActionInterface for GET requests
  • Return proper result objects (JsonFactory, PageFactory, RedirectFactory)
  • Always validate input data
  • Use try-catch blocks for error handling
  • Log errors for debugging
  • Return user-friendly error messages

Advanced: Modal Popup Widget

For complex interactions like modal forms:

Template with Modal:

<?php
$modalId = 'modal-' . uniqid();
?>

<div class="widget-container">
    <button type="button"
            class="action primary"
            data-mage-init='{"widgetModal": {"modalId": "<?= $escaper->escapeJs($modalId) ?>"}}'>
        <?= $escaper->escapeHtml(__('Open Modal')) ?>
    </button>
</div>

<!-- Modal Structure -->
<div id="<?= $escaper->escapeHtmlAttr($modalId) ?>"
     class="widget-modal"
     style="display: none;"
     data-role="widget-modal">
    <div class="widget-modal-overlay"></div>
    <div class="widget-modal-content">
        <div class="widget-modal-header">
            <h2><?= $escaper->escapeHtml(__('Modal Title')) ?></h2>
            <button type="button" class="widget-modal-close" aria-label="Close">
                <span>&times;</span>
            </button>
        </div>
        <div class="widget-modal-body">
            <form id="widget-form" method="post">
                <!-- Form fields -->
                <input type="text" name="field_name" required />

                <button type="submit" class="action primary">
                    <?= $escaper->escapeHtml(__('Submit')) ?>
                </button>
            </form>
        </div>
    </div>
</div>

Modal JavaScript:

IMPORTANT:

  1. Do NOT use Magento's Magento_Ui/js/modal/modal component when you have custom modal HTML structure - it creates conflicts with z-index, positioning, and double overlays
  2. ALWAYS move the modal element to <body> on initialization to prevent parent container constraints (overflow, positioning, z-index)
define([
    'jquery'
], function ($) {
    'use strict';

    $.widget('vendor.widgetModal', {
        options: {
            modalId: ''
        },

        _create: function () {
            this._moveModalToBody();
            this._bind();
        },

        /**
         * Move modal element to body to prevent parent container constraints
         * This is CRITICAL - without this, modal will be trapped inside widget container
         */
        _moveModalToBody: function () {
            var modalElement = $('#' + this.options.modalId);

            if (modalElement.length && modalElement.parent()[0].tagName !== 'BODY') {
                // Move modal to body so it's not constrained by parent positioning
                modalElement.appendTo('body');
            }
        },

        _bind: function () {
            var self = this;

            // Open modal on button click
            this.element.on('click', function (e) {
                e.preventDefault();
                self.openModal();
            });
        },

        openModal: function () {
            var modalElement = $('#' + this.options.modalId);

            if (modalElement.length) {
                // Show modal with fade effect
                modalElement.fadeIn(300);
                $('body').addClass('modal-open');

                // Bind close button (only once)
                modalElement.find('.widget-modal-close, .action.cancel').off('click').on('click', function (e) {
                    e.preventDefault();
                    modalElement.fadeOut(300);
                    $('body').removeClass('modal-open');
                });

                // Bind overlay click (only once)
                modalElement.find('.widget-modal-overlay').off('click').on('click', function (e) {
                    e.preventDefault();
                    modalElement.fadeOut(300);
                    $('body').removeClass('modal-open');
                });

                // Bind ESC key
                $(document).off('keyup.widgetModal').on('keyup.widgetModal', function (e) {
                    if (e.key === 'Escape' || e.keyCode === 27) {
                        modalElement.fadeOut(300);
                        $('body').removeClass('modal-open');
                        $(document).off('keyup.widgetModal');
                    }
                });
            }
        }
    });

    return $.vendor.widgetModal;
});

Modal CSS Styling:

/**
 * Modal Styling
 * IMPORTANT:
 * - Use very high z-index (999999) with !important to ensure modal appears above all content
 * - Many themes use high z-index values for headers, menus, etc. (10000+)
 * - Modal container and all children use position: fixed to escape parent containers
 * - Overlay uses darker background (0.7 opacity) to clearly indicate blocked content
 */
.widget-modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 999999 !important;
}

.widget-modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    z-index: 999998 !important;
    cursor: pointer;
}

.widget-modal-content {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
    z-index: 999999 !important;
    max-width: 600px;
    width: 90%;
    max-height: 90vh;
    overflow-y: auto;
}

/* Prevent body scroll when modal is open */
body.modal-open {
    overflow: hidden;
}

/* Ensure modal container blocks all pointer events to elements below */
.widget-modal {
    pointer-events: auto;
}

.widget-modal * {
    pointer-events: auto;
}

.widget-modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20px 30px;
    border-bottom: 1px solid #e0e0e0;
}

.widget-modal-header h2 {
    margin: 0;
    font-size: 24px;
    font-weight: 600;
    color: #333;
}

.widget-modal-close {
    background: none;
    border: none;
    font-size: 32px;
    line-height: 1;
    color: #666;
    cursor: pointer;
    padding: 0;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: color 0.3s ease;
}

.widget-modal-close:hover {
    color: #000;
}

.widget-modal-body {
    padding: 30px;
}

/* Responsive Design */
@media (max-width: 768px) {
    .widget-modal-content {
        width: 95%;
        max-height: 95vh;
    }

    .widget-modal-header {
        padding: 15px 20px;
    }

    .widget-modal-header h2 {
        font-size: 20px;
    }

    .widget-modal-body {
        padding: 20px;
    }
}

Installation and Deployment

Installation Commands

# Enable the module
bin/magento module:enable Vendor_ModuleName

# Run setup upgrade
bin/magento setup:upgrade

# Compile dependency injection (production mode)
bin/magento setup:di:compile

# Deploy static content (production mode)
bin/magento setup:static-content:deploy -f

# Clear cache
bin/magento cache:flush

Deployment Checklist

  1. ✅ Module enabled
  2. ✅ Database schema updated (setup:upgrade)
  3. ✅ DI compiled (setup:di:compile)
  4. ✅ Static content deployed
  5. ✅ Cache cleared
  6. ✅ Permissions checked (www-data ownership)

Using the Widget

Method 1: Admin Panel (CMS Editor)

  1. Navigate to Content > Pages or Content > Blocks
  2. Edit the desired page or block
  3. Place cursor where widget should appear
  4. Click Insert Widget button in editor toolbar
  5. Select Widget Type: Your widget name
  6. Configure widget parameters
  7. Click Insert Widget
  8. Save the page/block

Method 2: Direct Code in CMS Content

Add widget code directly in CMS content:

{{widget type="Vendor\ModuleName\Block\Widget\WidgetName"
         text_param="My Value"
         select_param="value1"
         enabled="1"}}

Method 3: XML Layout Files

Add widget programmatically in layout XML:

<referenceContainer name="content">
    <block class="Vendor\ModuleName\Block\Widget\WidgetName"
           name="custom.widget"
           template="Vendor_ModuleName::widget/template.phtml">
        <arguments>
            <argument name="text_param" xsi:type="string">My Value</argument>
            <argument name="select_param" xsi:type="string">value1</argument>
            <argument name="enabled" xsi:type="boolean">true</argument>
        </arguments>
    </block>
</referenceContainer>

Method 4: Programmatically in Template

Create widget block in any template:

<?= $block->getLayout()
    ->createBlock(\Vendor\ModuleName\Block\Widget\WidgetName::class)
    ->setData('text_param', 'My Value')
    ->setData('enabled', true)
    ->toHtml() ?>

Real-World Example: Quote Request Form Widget

Based on the ItTools_QuoteForm module created for LCD Screen Repair:

Features:

  • Modal popup form
  • File upload (max 3 images, 5MB each)
  • Client-side validation
  • AJAX submission
  • Email with attachments
  • Success/error messages

Key Files:

app/code/ItTools/QuoteForm/
├── Block/Widget/QuoteButton.php
├── Controller/Index/Submit.php
├── etc/widget.xml
├── etc/email_templates.xml
├── view/frontend/templates/widget/quotebutton.phtml
├── view/frontend/web/js/quote-modal.js
├── view/frontend/web/js/quote-form.js
└── view/frontend/web/css/quote-form.css

Usage:

{{widget type="ItTools\QuoteForm\Block\Widget\QuoteButton"
         button_text="Get a Free Quote"
         button_class="action primary"}}

Common Widget Use Cases

  1. Contact Forms - Custom contact/inquiry forms
  2. Quote Request Buttons - Lead generation forms
  3. Product Sliders - Featured product carousels
  4. CTAs (Call-to-Action) - Promotional buttons/banners
  5. Newsletter Signup - Custom subscription forms
  6. Social Media Feeds - Display social content
  7. Store Locators - Find nearest store widget
  8. Calculators - Price/shipping calculators
  9. Reviews/Testimonials - Customer feedback display
  10. Search Boxes - Custom search functionality

Best Practices Summary

Security

  1. ✅ Always escape output in templates
  2. ✅ Validate and sanitize all user inputs
  3. ✅ Use form keys for POST requests
  4. ✅ Implement CSRF protection
  5. ✅ Check file types and sizes for uploads
  6. ✅ Use parameterized queries (avoid SQL injection)
  7. ✅ Validate email addresses properly
  8. ✅ Add rate limiting for form submissions

Performance

  1. ✅ Minimize database queries in blocks
  2. ✅ Use caching where appropriate
  3. ✅ Lazy-load JavaScript when possible
  4. ✅ Optimize CSS (remove unused styles)
  5. ✅ Compress images and assets
  6. ✅ Use CDN for static assets
  7. ✅ Avoid blocking JavaScript

Code Quality

  1. ✅ Use strict types (declare(strict_types=1);)
  2. ✅ Add type hints and return types
  3. ✅ Follow Magento coding standards
  4. ✅ Use dependency injection (no ObjectManager)
  5. ✅ Add proper PHPDoc comments
  6. ✅ Keep methods small and focused
  7. ✅ Use constants for magic values
  8. ✅ Implement proper error handling
  9. ✅ Log errors appropriately
  10. ✅ Write unit/integration tests

User Experience

  1. ✅ Make widgets responsive (mobile-friendly)
  2. ✅ Provide clear success/error messages
  3. ✅ Add loading indicators for AJAX
  4. ✅ Validate forms client-side and server-side
  5. ✅ Use accessibility attributes (aria-*)
  6. ✅ Test keyboard navigation
  7. ✅ Provide clear labels and instructions
  8. ✅ Handle edge cases gracefully

Maintainability

  1. ✅ Use meaningful variable/method names
  2. ✅ Keep templates clean (logic in blocks)
  3. ✅ Document complex logic
  4. ✅ Use configuration for settings
  5. ✅ Follow single responsibility principle
  6. ✅ Make code testable
  7. ✅ Version control properly
  8. ✅ Add README with usage instructions

Troubleshooting

Widget Not Appearing in Admin

Symptoms: Widget doesn't show in "Insert Widget" dropdown

Solutions:

  1. Check widget.xml syntax (validate XML)
  2. Verify module is enabled: bin/magento module:status
  3. Clear cache: bin/magento cache:flush
  4. Clear generated code: rm -rf generated/code/*
  5. Check file permissions
  6. Review system.log for errors

Widget Not Rendering on Frontend

Symptoms: Widget code shows but no output

Solutions:

  1. Verify template path in block class
  2. Check template file exists at specified path
  3. Clear cache: bin/magento cache:flush
  4. Check for PHP errors in template
  5. Review exception.log and system.log
  6. Enable developer mode to see detailed errors

JavaScript Not Loading

Symptoms: Widget functionality not working

Solutions:

  1. Verify requirejs-config.js syntax
  2. Check JS file path is correct
  3. Clear static content: bin/magento setup:static-content:deploy -f
  4. Check browser console for 404 errors
  5. Verify file permissions
  6. Check for JavaScript errors in console
  7. Ensure jQuery and dependencies are loaded

CSS Styles Not Applied

Symptoms: Widget appears unstyled

Solutions:

  1. Verify CSS path in layout/default.xml
  2. Check CSS file exists
  3. Deploy static content: bin/magento setup:static-content:deploy -f
  4. Clear browser cache
  5. Check for CSS file 404 in network tab
  6. Verify CSS selector specificity
  7. Check for conflicting styles

Form Submission Failing

Symptoms: AJAX returns errors or no response

Solutions:

  1. Check controller route configuration
  2. Verify controller implements correct interface
  3. Add form key to form if using POST
  4. Check network tab for actual error response
  5. Review exception.log for server errors
  6. Verify AJAX URL is correct
  7. Check request/response format (JSON)
  8. Ensure proper content type headers

File Upload Issues

Symptoms: Files not uploading or validation fails

Solutions:

  1. Check PHP upload_max_filesize and post_max_size
  2. Verify file input has enctype="multipart/form-data"
  3. Check file permissions on upload directory
  4. Validate file mime types server-side
  5. Check for JavaScript file validation logic
  6. Review file size limits (client and server)
  7. Check $_FILES array in controller

Modal Popup Issues

Symptoms: Modal is half-hidden, trapped inside section/div, has z-index issues, double overlays, or positioning problems

Root Causes:

  1. Conflict between Magento's Magento_Ui/js/modal/modal component and custom modal HTML structure
  2. Modal element is constrained by parent container (overflow, position, z-index)

Solutions:

  1. CRITICAL: Move modal to body - Add modalElement.appendTo('body') in widget initialization to escape parent containers
  2. DO NOT use Magento's modal component when you already have custom modal HTML with overlay and content divs
  3. Use simple jQuery fadeIn()/fadeOut() instead of modal('openModal')
  4. Set very high z-index (999999) with!important on the modal container (themes often use 10000+ for headers/menus)
  5. Use position: fixed on BOTH overlay and content (not absolute)
  6. Use darker overlay background rgba(0, 0, 0, 0.7) to clearly block content
  7. Add body.modal-open {overflow: hidden;} to prevent background scroll
  8. Add pointer-events: auto to modal and children to block clicks
  9. Clear static content after changes: rm -rf pub/static/frontend/*
  10. Clear browser cache and test in incognito mode
  11. Inspect competing elements with browser DevTools to find their z-index values

Example Fix:

// WRONG - causes conflicts
define(['jquery', 'Magento_Ui/js/modal/modal'], function ($, modal) {
    modalElement.modal({ ... });
});

// CORRECT - simple and works
define(['jquery'], function ($) {
    modalElement.fadeIn(300);
    $('body').addClass('modal-open');
});

Testing Checklist

Before deploying your widget:

Functional Testing

  • Widget appears in admin "Insert Widget" dropdown
  • All parameters show correctly in admin
  • Widget renders on frontend
  • All parameter variations work correctly
  • Form submission works (if applicable)
  • Validation works client-side and server-side
  • Success/error messages display correctly
  • Email sending works (if applicable)

Browser/Device Testing

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)
  • Mobile iOS
  • Mobile Android
  • Tablet

Performance Testing

  • Page load time acceptable
  • No JavaScript errors in console
  • No CSS conflicts with theme
  • Caching works properly
  • AJAX requests complete quickly

Security Testing

  • All outputs escaped properly
  • SQL injection prevention
  • XSS prevention
  • CSRF protection
  • File upload validation
  • Input validation/sanitization

Accessibility Testing

  • Keyboard navigation works
  • Screen reader friendly
  • Proper ARIA labels
  • Focus indicators visible
  • Color contrast sufficient

Reference: ItTools Module Examples

Real working examples from this codebase:

  1. ItTools_QuoteForm (app/code/ItTools/QuoteForm/)

- Modal popup widget - Form with file uploads - Email functionality - AJAX submission

  1. ItTools_CategorySearch (app/code/ItTools/CategorySearch/)

- Simple search widget - Extends existing functionality - Custom placeholder text parameter

Use these as reference implementations.

Additional Resources

Magento DevDocs

Code Examples

  • Magento core widgets: vendor/magento/module-widget/
  • Magento CMS widgets: vendor/magento/module-cms/Block/Widget/
  • Catalog widgets: vendor/magento/module-catalog/Block/Widget/

Version Compatibility

This skill is compatible with:

  • Magento Open Source 2.3.x - 2.4.x
  • Adobe Commerce 2.3.x - 2.4.x
  • Mage-OS (Magento fork)

Not compatible with:

  • Hyvä themes (use hyva-tailwind-integration skill instead)
  • Magento 1.x

平台分布

Codex

34.28%
按下载量换算23

Claude

28.88%
按下载量换算19

Cursor

18.84%
按下载量换算12

Gemini CLI

8.51%
按下载量换算6

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills