Developers
Assets
A module that needs its own stylesheet, script, or images keeps them beside
its code and pushes them to the document from the class or the layout. The
helpers come from the AssetAware trait, which
Hubzero\Module\Module mixes
in, so they are available on $this anywhere in a module.
Push them; do not write a <link> tag in the layout. A module can appear
twice on a page, and the document collects each asset once no matter how many
instances ask for it. A hand-written tag in the layout is emitted once per
instance, in the middle of the body.
Keep the stylesheet small and scoped. Your module lands inside a template you
did not write, so a rule on ul or .item will reach into the rest of the
page. Scope everything under one class of your own.
Where assets live
app/modules/mod_upcoming_bookings/
assets/
css/mod_upcoming_bookings.css
img/
js/mod_upcoming_bookings.js
The assets directory is split by type, and the type directory name matches
the helper: css/ for css(), js/ for js(), img/ for img(). Files
directly under the module directory (mod_mygroups/css/…) are also found, but
core modules all use assets/.
The helpers
| Method | Signature |
|---|---|
css |
css($stylesheet = '', $extension = null, $attributes = array()) |
js |
js($asset = '', $extension = null, $attributes = array()) |
img |
img($asset, $extension = null) |
css() and js() add the file to the document and return $this, so calls
chain. img() adds nothing to the document; it returns a URL for use in an
src attribute.
When $extension is omitted it defaults, for a module, to
$this->module->module — the module's own element name. When the asset name
is omitted as well, the name defaults to the extension name too, which is why
mod_mygroups's stylesheet is called mod_mygroups.css and the layout can
load both of its assets with no arguments at all:
$this->css()
->js();
Name your files after the module and the no-argument form is all you ever
write: mod_upcoming_bookings/assets/css/mod_upcoming_bookings.css is found
by a bare $this->css().
Naming a file explicitly works the same way, with or without the extension:
$this->css('login.css', 'com_login')
->css('providers.css', 'com_login')
->js('login', 'com_login')
->js('jquery.hoverIntent', 'system');
The second argument accepts any extension name — com_login, mod_login,
plg_members_blog — and the literal system, which resolves against the
top-level core/assets directory. mod_login uses both, borrowing
com_login's stylesheets rather than duplicating them.
Where the file is looked for
For a module, Hubzero\Document\Asset\File builds this list and takes the
first path that exists:
app/modules/mod_upcoming_bookings/assets/css/name.css
app/modules/mod_upcoming_bookings/css/name.css
app/modules/upcoming_bookings/assets/css/name.css
app/modules/upcoming_bookings/css/name.css
core/modules/mod_upcoming_bookings/assets/css/name.css
core/modules/mod_upcoming_bookings/css/name.css
core/modules/upcoming_bookings/assets/css/name.css
core/modules/upcoming_bookings/css/name.css
Every app/ path is tried before any core/ path, so a hub overrides a
shipped module's stylesheet by dropping a file into app/modules. An active
template can override any of them with a file at
{template}/html/mod_upcoming_bookings/name.css, which is checked last and
wins when it exists.
The returned URL carries a cache-buster taken from the file's modification
time: /core/modules/mod_mygroups/assets/css/mod_mygroups.css?v=1568392841.
Attributes
The third argument tunes the tag. For css(), media and type and an
attribs array; for js(), type, defer, and async:
$this->css('print', null, array('media' => 'print'))
->js('widget', null, array('defer' => true));
Declarations
Passing a string that contains { or @ to css() makes it a declaration
rather than a file reference: the string is added to the document with
addStyleDeclaration() instead of being resolved to a path. This is a
convenience for a rule or two computed from a parameter, not a way to build a
stylesheet.
Images
img() returns the URL of a file under assets/img, and unlike the other two
it keeps the file extension you give it:
<img src="<?php echo $this->img('picture.png'); ?>" alt="A picture" />
Only png, gif, jpg, jpeg, and jpe are recognised. For an SVG or a
web font, build the path yourself from Request::root(true).
From the class or the layout
Both work, because both run with $this bound to the module object.
mod_login pushes its assets from display() in helper.php; mod_mygroups
does it at the top of tmpl/default.php.
Push from the layout. The stylesheet exists to style that markup, and a hub
that overrides the layout should get to drop your stylesheet along with it —
which happens automatically if the $this->css() call lives in the file they
replaced. Push from the class only for an asset the module needs whatever
layout is chosen.
Rewritten and checked against 2.4-main @ 91d03d0a23 on 2026-09-10.