Install
$ agentstack add skill-ddtcorex-dev-skills-hub-magento2-hyva-dev Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Magento 2 Hyvä Developer
Hyvä is a modern Magento 2 frontend framework with dramatically simplified JavaScript and CSS. This skill covers Hyvä-specific patterns.
Related Skills
REQUIRED BACKGROUND: Load magento2-dev-core first — it defines the PHP/backend patterns (DI, escaping, repositories) this skill assumes for any ViewModel or backend code behind a Hyvä template.
Hyvä and Luma (magento2-frontend-dev) are mutually exclusive theme stacks — check the theme's theme.xml parent (Hyva/default/Hyva/reset vs Magento/blank) and composer.json for hyva-themes/* packages before assuming either applies. Pair with govard-magento for the container/CLI side.
Detect the project's actual setup first
Hyvä/Tailwind conventions vary a lot by project age — check before applying a pattern:
- Tailwind version: v4 uses CSS-based config and
hyva.config.jsondesign tokens; v2/v3 usetailwind.config.js. Checkweb/tailwind/package.json. - CSP build:
Hyva/default-cspvs the plainHyva/default/Hyva/resetparent intheme.xml. Applying CSP-only nonce patterns to a non-CSP theme (or vice versa) wastes effort. - Parent theme:
Hyva/reset(built from scratch) vsHyva/default(full starter) changes how much markup/CSS already exists to extend rather than rewrite.
Hyvä vs Luma Comparison
| Aspect | Luma | Hyvä | |--------|------|------| | JavaScript | ~200 resources (RequireJS/Knockout) | 2 resources (Alpine.js) | | CSS | LESS-based | Tailwind CSS | | Bundle Size | 500KB+ | //web cp -r vendor/hyva-themes/magento2-default-theme/web/* app/design/frontend///web/
For a CSP theme, copy from magento2-default-theme-csp instead
Then add `registration.php`, `theme.xml` (parent: `Hyva/default`, `Hyva/reset`, or `Hyva/default-csp`), and `composer.json`, install Tailwind deps and build, then `bin/magento setup:upgrade && bin/magento cache:flush` to pick up the new theme.
app/design/frontend/Vendor/Theme/ ├── registration.php ├── theme.xml ├── composer.json ├── package.json ├── tailwind.config.js ├── package.json ├── web/ │ ├── tailwind/ │ │ ├── base/ # Preflight, resets │ │ ├── components/ # Reusable components │ │ │ ├── buttons.css │ │ │ ├── forms.css │ │ │ └── messages.css │ │ ├── utilities/ # Custom utilities │ │ └── theme/ # Page-specific │ └── js/ │ └── alpinejs/ # Alpine components ├── layout/ │ └── default.xml └── templates/ └── ...
## CSP (Content Security Policy) Compliance
### Critical: PCI-DSS 4.0 (Required since April 2025)
Payment pages MUST NOT use:
- `unsafe-eval` CSP directive
- `unsafe-inline` CSP directive
### CSP Nonce Registration
**Every inline script MUST register with CSP:**
```php
registerInlineScript() ?>
">
// CSP-compliant code
CSP-Compatible Alpine.js Patterns
WRONG (CSP violations):
Add
Ready
CORRECT (CSP-compliant):
Add
Ready
function initComponent() {
return {
count: 0,
loading: true,
name: '',
increment() {
this.count++;
},
isNotLoading() {
return !this.loading;
},
updateName(event) {
this.name = event.target.value;
}
}
}
window.addEventListener('alpine:init', () => {
Alpine.data('initComponent', initComponent);
}, {once: true})
Registering Alpine Components
function initProductSlider() {
return {
products: [],
currentIndex: 0,
init() {
// Initialization
},
next() {
this.currentIndex = (this.currentIndex + 1) % this.products.length;
},
prev() {
this.currentIndex = (this.currentIndex - 1 + this.products.length) % this.products.length;
}
}
}
window.addEventListener('alpine:init', () => Alpine.data('initProductSlider', initProductSlider), {once: true})
registerInlineScript() ?>
Alpine.js Component Structure
Basic Component
// web/js/alpinejs/Example.js
function initExample() {
return {
// Observable state
isOpen: false,
items: [],
selectedId: null,
// Computed (reactive)
get hasItems() {
return this.items.length > 0;
},
// Methods
toggle() {
this.isOpen = !this.isOpen;
},
select(id) {
this.selectedId = id;
},
// Lifecycle
init() {
// Called when component initializes
this.loadData();
},
loadData() {
fetch('/api/data')
.then(res => res.json())
.then(data => this.items = data);
}
}
}
window.addEventListener('alpine:init', () => Alpine.data('initExample', initExample), {once: true})
Template Usage
Toggle
function initExample() {
// ... component logic
}
window.addEventListener('alpine:init', () => Alpine.data('initExample', initExample), {once: true})
registerInlineScript() ?>
Passing Data from PHP
escapeHtmlAttr($productsJson) ?>">
function initProductList() {
return {
products: [],
init() {
this.products = JSON.parse(this.$root.dataset.products || '[]');
}
}
}
window.addEventListener('alpine:init', () => Alpine.data('initProductList', initProductList), {once: true})
registerInlineScript() ?>
Hyvä Utilities
Hyvä provides global utilities via the hyva object:
Form Handling
// Get form key
hyva.getFormKey()
// Submit form via POST
hyva.postForm({
action: '/checkout',
data: { product_id: 123, qty: 1 }
})
// Alternative with fetch
async function submitForm(url, data) {
const formKey = hyva.getFormKey();
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ ...data, form_key: formKey })
});
return response.json();
}
Cookies
hyva.getCookie('customer_segment')
hyva.setCookie('recent_viewed', productId, 30)
Formatting
hyva.formatPrice(price, showSign)
hyva.str('Hello {0}', name)
hyva.safeParseNumber(value)
DOM Manipulation
hyva.replaceDomElement('#target', 'New content')
hyva.trapFocus(modalElement)
Events
// After Alpine initialization
hyva.alpineInitialized(function() {
console.log('Alpine ready');
})
View Models
Prefer view models (Hyva\Theme\Model\ViewModelInterface / Magento's ArgumentInterface) over blocks for passing data to templates — they keep PHP logic out of the theme directory (which should hold only templates, layout, i18n, and web/ assets) and in a proper app/code module where Magento's DI can autoload the class.
// app/code/Vendor/Module/ViewModel/ProductInfo.php
declare(strict_types=1);
namespace Vendor\Module\ViewModel;
use Hyva\Theme\Model\ViewModelInterface;
use Magento\Framework\View\LayoutInterface;
class ProductInfo implements ViewModelInterface
{
public function __construct(
private readonly LayoutInterface $layout
) {}
public function isInStock(): bool
{
$product = $this->layout->getBlock('product.info')->getProduct();
return $product && $product->isInStock();
}
}
Vendor\Module\ViewModel\ProductInfo
isInStock()): ?>
Add to Cart
Tailwind CSS
Tailwind v4 (CSS-based config)
Newer Hyvä themes use Tailwind v4, which drops tailwind.config.js for a CSS-based config plus a hyva.config.json design-token file — check web/tailwind/package.json first, the two configs are not interchangeable.
/* web/tailwind/tailwind-source.css */
@import "tailwindcss";
@theme {
--color-primary: oklch(46% 0.2 265);
--spacing-xs: 0.5rem;
}
@layer components {
.btn-primary {
@apply bg-primary text-white px-4 py-2 rounded;
}
}
// hyva.config.json
{
"tokens": {
"src": "hyva.design.tokens.json",
"format": "default",
"cssSelector": "@theme"
}
}
Generate tokens/sources with npx hyva-sources / npx hyva-tokens rather than hand-rolling them.
Directory Structure
web/tailwind/
├── base/
│ └── _styles.pcss # Preflight, typography
├── components/
│ ├── _buttons.pcss
│ ├── _forms.pcss
│ └── _messages.pcss
├── utilities/
│ └── _custom-utilities.pcss
├── theme/
│ ├── _header.pcss
│ └── _footer.pcss
└── app.css # Main entry
Build Commands
# Development with watch
npm run watch
# Production build
npm run build
# PurgeCSS config (auto-included)
# Tailwind automatically removes unused classes
Common Classes
Add to Cart
Responsive Design
Layout XML
Hyvä-Specific Handles
Override Template
Migration from Luma
Step 1: Analyze Dependencies
# List jQuery dependencies
grep -r "require.*jquery" app/design/frontend/Vendor/Theme/web/js/
# Check Knockout bindings
grep -r "data-bind=" app/design/frontend/Vendor/Theme/templates/
Step 2: Replace JavaScript
// Luma Knockout
define(['ko'], function(ko) {
return {
items: ko.observableArray([]),
addItem: function(item) {
this.items.push(item);
}
};
});
// Hyvä Alpine
function initComponent() {
return {
items: [],
addItem(item) {
this.items.push(item);
}
}
}
Step 3: Replace LESS with Tailwind
// Luma LESS
.product-card {
.lib-card();
.lib-respond-to(@mobile, { width: 100%; });
}
// Hyvä Tailwind
Step 4: Update Templates
// Luma (Knockout)
// Hyvä (Alpine)
Third-Party Compatibility Modules
A Luma-built third-party extension needs a Hyvä compatibility module to override its templates and JS — check the vendor's GitHub for an existing one (many ship under hyva-themes/*) before writing your own.
To build one: create a module that requires the original module, copy only the .phtml templates you need to override, replace any jQuery/Knockout JS with CSP-compatible Alpine, and sequence it after both the original module and Hyva_Theme in module.xml. Then register it so Hyvä actually picks it up:
Vendor_Module
Vendor_ModuleHyva
Without this CompatModuleRegistry registration, Hyvä has no way to know the compat module should override the original's frontend output — the templates get copied but never actually take effect.
Hyvä UI & CMS Components
- UI components (
hyva-themes/hyva-ui): prebuilt, template-based components installed into a theme — copysrc/*into the theme, merge any layout XML, and add config toetc/view.xml. - CMS components: custom Hyvä CMS blocks live in a module depending on
Hyva_CmsBase, declared in acomponents.jsonschema. Key gotchas:childrenis a root-level property (not a field type), validation lives underattributes, and the default-value key isdefault_value, notdefault.
Responsive Images
Use Hyva\Theme\ViewModel\Media::getResponsivePictureHtml() to generate ` markup instead of hand-rolling srcset. Set loading="eager" fetchpriority="high" on the LCP image (hero/first product image) and loading="lazy"` on everything below the fold — getting this backwards is a common, easy-to-miss LCP regression.
Testing with Playwright
Hyvä pages scatter hidden x-show elements around the DOM — always scope message assertions to #messages, never a bare .message.error selector, or the test will match a hidden element and give a false pass/fail. Prefer getByRole / getByLabel / scoped getByText over raw CSS selectors, and use web-first assertions with a longer timeout to account for Alpine's reactive re-render delay after form submits.
Official Hyvä AI Tools
Hyvä provides official AI skills for various assistants:
| Tool | Purpose | Install | |------|---------|---------| | hyva-alpine-component | CSP-compatible Alpine components | .opencode/skills/ | | hyva-child-theme | Theme creation | .opencode/skills/ | | hyva-cms-component | CMS blocks | .opencode/skills/ | | hyva-ui-component | UI component installation | .opencode/skills/ |
# Install Hyvä AI tools
curl -fsSL https://raw.githubusercontent.com/hyva-themes/hyva-ai-tools/main/install.sh | sh -s opencode
Verification
# Build CSS
cd web/tailwind && npm run build
# Clear cache
bin/magento cache:clean layout block_html full_page
# Static content deploy
bin/magento setup:static-content:deploy -f
# Test in browser
# Check console for CSP errors
# Test with CSP headers enabled
Pitfalls recap
- Hyvä replaces Luma entirely: no RequireJS, Knockout, UI-component JS, jQuery, or LESS in Hyvä templates.
- Every inline `
needs$hyvaCsp->registerInlineScript()`, or it silently fails under CSP. - Prefer the CSP-safe Alpine pattern (named
Alpine.data()functions, no inline expression logic like@click="count++") even on a non-CSP build — it's the cleaner default and avoids a rewrite if the project later enables CSP. - Rebuild Tailwind (
npm run build) after any style change, or the new classes won't be in the compiled CSS. - Always escape output in
.phtmlwith$escaper, same as any other Magento template.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ddtcorex
- Source: ddtcorex/dev-skills-hub
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.