Skip to content
intermediate Phase 57 · Modern Frontend

Hyvä Customization

Extending Hyvä theme: custom modules, template overrides, and integration with third-party extensions

45m
0 problems
Topic Progress 0%

Theme Customization

Custom Theme from Hyvä

<!-- theme.xml -->
<theme xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/theme.xsd">
    <title>Custom Hyvä Theme</title>
    <parent>Hyva/Default</parent>
</theme>

Override Templates

app/design/frontend/Vendor/Custom/
└── Magento_Catalog/
    └── templates/
        └── product/
            ├── list.phtml
            └── view.phtml

Tailwind Customization

// web/tailwind.config.js
module.exports = {
    // ... Hyvä base config
    theme: {
        extend: {
            colors: {
                'brand': {
                    DEFAULT: '#ff6600',
                    dark: '#cc5200',
                    light: '#ff8533'
                }
            }
        }
    }
};

Custom CSS

/* web/src/css/input.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
    .btn-brand {
        @apply px-4 py-2 bg-brand text-white rounded hover:bg-brand-dark;
    }
}

Custom Modules

Hyvä-Compatible Module

app/code/Vendor/CustomModule/
├── registration.php
├── etc/module.xml
├── view/frontend/
│   ├── layout/
│   │   └── default.xml
│   ├── templates/
│   │   └── widget.phtml
│   └── web/
│       └── js/
│           └── widget.js
└── Block/
    └── Widget.php

Block Class

<?php
namespace Vendor\CustomModule\Block;

use Magento\Framework\View\Element\Template;

class Widget extends Template
{
    public function getData(): array
    {
        return ['item1', 'item2', 'item3'];
    }
}

Template with Alpine.js

<!-- widget.phtml -->
<div x-data="{ items: <?= $block->escapeHtml(json_encode($block->getData())) ?> }">
    <template x-for="item in items" :key="item">
        <div class="p-2 border rounded" x-text="item"></div>
    </template>
</div>

Layout XML

<!-- default.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Vendor\CustomModule\Block\Widget"
                   name="custom.widget"
                   template="widget.phtml"/>
        </referenceContainer>
    </body>
</page>

Template Overrides

Override Hyvä Templates

<!-- Override product list -->
<!-- app/design/frontend/Vendor/Custom/Magento_Catalog/templates/product/list.phtml -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
    <?php foreach ($block->getProductCollection() as $product): ?>
        <div class="border rounded p-4 hover:shadow-lg">
            <img src="<?= $block->escapeUrl($product->getImageUrl()) ?>"
                 alt="<?= $block->escapeHtmlAttr($product->getName()) ?>"
                 class="w-full h-48 object-cover">
            <h3 class="font-bold mt-2"><?= $block->escapeHtml($product->getName()) ?></h3>
            <p class="text-gray-600">$<?= number_format($product->getPrice(), 2) ?></p>
            <button class="mt-2 w-full bg-blue-500 text-white rounded py-2 hover:bg-blue-600">
                Add to Cart
            </button>
        </div>
    <?php endforeach; ?>
</div>

Override Header

<!-- app/design/frontend/Vendor/Custom/Magento_Theme/templates/html/header.phtml -->
<header class="bg-white shadow">
    <div class="container mx-auto px-4">
        <div class="flex items-center justify-between h-16">
            <div class="flex-shrink-0">
                <img src="<?= $block->escapeUrl($block->getViewFileUrl('images/logo.svg')) ?>" class="h-8">
            </div>
            <nav class="hidden md:flex space-x-4">
                <a href="<?= $block->escapeUrl($block->getUrl('')) ?>" class="text-gray-700 hover:text-blue-500">Home</a>
                <a href="<?= $block->escapeUrl($block->getUrl('catalog')) ?>" class="text-gray-700 hover:text-blue-500">Shop</a>
            </nav>
        </div>
    </div>
</header>

Third-Party Integration

Extension Compatibility

# Check if extension supports Hyvä
composer show vendor/extension | grep -i hyva

# Install compatibility module
composer require hyva-themes/magento2-compatibility-vendor

Create Compatibility Layer

<?php
namespace Vendor\HyvaCompat\Plugin;

class LegacyBlockPlugin
{
    public function afterToHtml(
        \Magento\Framework\View\Element\Template $subject,
        $result
    ): string {
        // Convert legacy output to Hyvä-compatible format
        return $this->convertToHyva($result);
    }
    
    private function convertToHyva(string $html): string
    {
        // Add Alpine.js directives
        // Add Tailwind classes
        return $html;
    }
}

Register Plugin

<!-- etc/di.xml -->
<config>
    <type name="Magento\Framework\View\Element\Template">
        <plugin name="hyva_compat" type="Vendor\HyvaCompat\Plugin\LegacyBlockPlugin"/>
    </type>
</config>

Testing Integration

# Test module with Hyvä
bin/magento module:enable Vendor_HyvaCompat
bin/magento setup:upgrade
bin/magento cache:clean

# Verify functionality
bin/magento dev:template-hints:enable

Quiz

1. How do you override a Hyvä template?

Question 1 options

2. How do you add Tailwind classes to custom CSS?

Question 2 options

3. How do you check extension compatibility?

Question 3 options

Flashcards

Question

How do you override Hyvä templates?

Answer

Create same path in your custom theme

Question

What is the @apply directive?

Answer

Applies Tailwind utility classes in custom CSS

Question

How do you check Hyvä compatibility?

Answer

composer show vendor/extension | grep hyva

Question

How do you add Alpine.js to templates?

Answer

Use x-data, @click, x-text directives

Question

What is the parent theme for custom Hyvä?

Answer

Hyva/Default

Revision Notes

Key Takeaways

  • 1. Override templates by creating same path in your theme
  • 2. Use @apply to apply Tailwind classes in custom CSS
  • 3. Check extension compatibility before integration
  • 4. Alpine.js provides interactivity without build step
  • 5. Create compatibility layers for legacy modules

Interview Tips

  • Explain Hyvä customization approach
  • Know how to override templates and add custom styles
  • Discuss extension compatibility strategies
  • Be ready to create Hyvä-compatible modules

Cheat Sheet

Override: Same path in your theme
CSS: @apply tailwind-classes
Alpine: x-data="{ ... }"

Module:
  Block extends Template
  Template uses x-data, x-text
  Layout adds block to container

Compatibility:
  composer show | grep hyva
  Create plugin for legacy blocks