Skip to content
intermediate Phase 24 · Module Frontend

Adding CSS and JS to Modules

Guide to adding CSS, Less, JavaScript, and mixins to Magento 2 modules: requirejs-config.js, Less compilation, JS components, and asset deployment

45m
0 problems
Topic Progress 0%

CSS and Less Files

File Location

Place Less files in:

view/frontend/web/css/source/_module.less

The _module.less file is automatically compiled and included.

Basic Less Structure

// view/frontend/web/css/source/_module.less

// Variables
@prep-primary-color: #1979c3;
@prep-secondary-color: #f5f5f5;
@prep-border-color: #cccccc;

// Common styles (all contexts)
& when (@media-common = true) {
    .prep-container {
        max-width: 1200px;
        margin: 0 auto;
        padding: 20px;
    }

    .prep-header {
        background-color: @prep-primary-color;
        color: white;
        padding: 15px;
        border-radius: 4px;

        &__title {
            font-size: 24px;
            font-weight: 600;
        }

        &__subtitle {
            font-size: 14px;
            margin-top: 5px;
            opacity: 0.9;
        }
    }

    .prep-item {
        border: 1px solid @prep-border-color;
        padding: 15px;
        margin-bottom: 10px;
        background: @prep-secondary-color;

        &__name {
            font-weight: 600;
            font-size: 16px;
        }

        &__price {
            color: @prep-primary-color;
            font-size: 18px;
        }
    }
}

// Mobile styles
.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
    .prep-container {
        padding: 10px;
    }

    .prep-header {
        &__title {
            font-size: 18px;
        }
    }
}

// Desktop styles
.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) {
    .prep-container {
        padding: 30px;
    }
}

Multiple Less Files

Create additional files and import them:

// view/frontend/web/css/source/_module.less
@import 'components/_header.less';
@import 'components/_items.less';
@import 'components/_buttons.less';

Custom CSS (Without Less)

If you prefer plain CSS, place files in:

view/frontend/web/css/custom.css

Add to layout:

<page>
    <head>
        <css src="Amazon_Prep::css/custom.css"/>
    </head>
</page>

JavaScript Configuration

requirejs-config.js

// view/frontend/requirejs-config.js
var config = {
    map: {
        '*': {
            'prepComponent': 'Amazon_Prep/js/component'
        }
    },
    paths: {
        'amazonPrep': 'Amazon_Prep/js'
    },
    deps: [
        'Amazon_Prep/js/init'
    ]
};

Map vs Paths

Type Purpose Example
map Alias a module name 'prepComponent': 'Amazon_Prep/js/component'
paths Define a base path 'amazonPrep': 'Amazon_Prep/js'

JavaScript Component

// view/frontend/web/js/component.js
define([
    'jquery',
    'uiComponent'
], function ($, Component) {
    'use strict';

    return Component.extend({
        defaults: {
            template: 'Amazon_Prep/component',
            title: 'Default Title'
        },

        initialize: function () {
            this._super();
            console.log('Component initialized');
            return this;
        },

        getTitle: function () {
            return this.title;
        },

        setTitle: function (title) {
            this.title = title;
            return this;
        }
    });
});

jQuery Widget

// view/frontend/web/js/widget.js
define([
    'jquery',
    'jquery/ui'
], function ($) {
    'use strict';

    $.widget('amazon.prepWidget', {
        options: {
            container: null,
            animate: true
        },

        _create: function () {
            this.container = this.options.container || this.element;
            this._bindEvents();
        },

        _bindEvents: function () {
            var self = this;
            this.container.on('click', '.prep-btn', function () {
                self._handleClick($(this));
            });
        },

        _handleClick: function ($btn) {
            if (this.options.animate) {
                $btn.fadeOut(200).fadeIn(200);
            }
        },

        destroy: function () {
            this._super();
        }
    });

    return $.amazon.prepWidget;
});

JS Mixins

What are Mixins?

Mixins allow you to extend or modify existing JavaScript components without rewriting them. They're Magento's way of applying the Open/Closed principle to JS.

Creating a Mixin

// view/frontend/web/js/mixin/component-mixin.js
define([
    'jquery'
], function ($) {
    'use strict';

    return function (targetModule) {
        // Extend the target module
        return targetModule.extend({

            // Override a method
            getTitle: function () {
                var originalTitle = this._super();
                return originalTitle + ' (Modified by Mixin)';
            },

            // Add a new method
            customMethod: function () {
                return 'This is a new method added by mixin';
            },

            // Modify initialize
            initialize: function () {
                this._super();
                console.log('Mixin initialized after parent');
                return this;
            }
        });
    };
});

Register Mixin

// view/frontend/requirejs-config.js
var config = {
    config: {\        mixins: {
            'Amazon_Original/js/component': {
                'Amazon_Prep/js/mixin/component-mixin': true
            }
        }
    }
};

Conditional Mixins

var config = {
    config: {
        mixins: {
            'Magento_Catalog/js/product/list': {
                'Amazon_Prep/js/mixin/product-list-mixin': true
            }
        }
    }
};

Mixin Best Practices

  1. Always call _super() — preserves parent behavior
  2. Don't modify data directly — use methods
  3. Keep mixins focused — one change per mixin
  4. Test thoroughly — mixins affect all instances
  5. Document dependencies — note which module is being extended

Using Mixins in Templates

<!-- In .phtml template -->
<div data-mage-init='{"prepComponent": {"title": "Custom Title"}}'>
    <h1 data-bind="text: getTitle()"></h1>
</div>

<!-- Or with script tag -->
<script type="text/x-magento-init">
{
    "*": {
        "prepComponent": {
            "title": "Custom Title"
        }
    }
}
</script>

Asset Deployment

Static Content Deployment

After adding CSS/JS files, deploy static content:

# Development
bin/magento setup:static-content:deploy -f

# Specific theme
bin/magento setup:static-content:deploy -f Magento/luma en_US

# With language
bin/magento setup:static-content:deploy -f en_US de_DE

Clear Cache

bin/magento cache:clean
bin/magento cache:flush

Remove Old Static Files

# Remove compiled Less
rm -rf pub/static/frontend/Amazon/Theme/

# Re-deploy
bin/magento setup:static-content:deploy -f

View File URL Helper

// In PHP
$url = $this->getViewFileUrl('Amazon_Prep::images/logo.png');

// In template
$url = $block->getViewFileUrl('Amazon_Prep::css/custom.css');

// In layout XML
<css src="Amazon_Prep::css/custom.css"/>
<img src="<?= $block->getViewFileUrl('Amazon_Prep::images/logo.png') ?>"/>

Head Assets in Layout

<page>
    <head>
        <!-- CSS -->
        <css src="Amazon_Prep::css/custom.css"/>

        <!-- JS -->
        <script src="Amazon_Prep::js/custom.js"/>

        <!-- RequireJS config -->
        <script type="text/x-magento-init">
        {
            "*": {
                "prepComponent": {
                    "title": "Custom"
                }
            }
        }
        </script>

        <!-- Inline styles -->
        <style>
            .custom { "color": red; }
        </style>

        <!-- Meta tags -->
        <meta name="description" content="Custom description"/>
    </head>
</page>

Asset Compilation Pipeline

Less → CSS (compiled)
JS Modules → Bundled (if enabled)
Templates → Compiled (Knockout.js)

Pipeline commands:

# Compile Less to CSS
bin/magento setup:static-content:deploy -f

# Compile DI (generates proxy/factory classes)
bin/magento setup:di:compile

# Compile everything (production)
bin/magento deploy:mode:set production

Quiz

1. Where should Less files be placed in a module?

Question 1 options

2. What does requirejs-config.js configure?

Question 2 options

3. What is a JS mixin in Magento?

Question 3 options

Flashcards

Question

Where do Less files go?

Answer

view/frontend/web/css/source/_module.less

Question

What does requirejs-config.js do?

Answer

Configures JS module aliases, paths, and dependencies

Question

How do you create a JS mixin?

Answer

Return a function that takes the target module and calls .extend() on it

Question

How do you add CSS via layout XML?

Answer

<css src="Vendor_Module::css/file.css"/>

Question

What command deploys static content?

Answer

bin/magento setup:static-content:deploy -f

Revision Notes

Key Takeaways

  • 1. Less files go in view/frontend/web/css/source/ and are auto-compiled
  • 2. requirejs-config.js maps module names to file paths
  • 3. JS mixins extend existing components via .extend() and _super()
  • 4. Run static-content:deploy after adding or changing assets
  • 5. Use getViewFileUrl() helper to generate asset URLs

Interview Tips

  • Explain the Less compilation pipeline in Magento
  • Know how to create and register JS mixins
  • Be ready to add custom CSS and JS to a module
  • Discuss static content deployment strategies

Cheat Sheet

Less: view/frontend/web/css/source/_module.less
JS:  view/frontend/web/js/
Config: view/frontend/requirejs-config.js

Mixin:
  define([], function() {
    return function(target) {
      return target.extend({ "method": function() { this._super(); } });
    };
  });

Deploy: bin/magento setup:static-content:deploy -f