stave.dev

Asset Management

CSS, JavaScript, and components auto-loaded by naming convention. No build step. No manifest file.

Directory Structure

Each module keeps its assets in an assets/ directory alongside its controller:

modules/HelloWorld/
HelloWorld.php
assets/
css/
hello-world.css
say-hello.css
js/
hello-world.js
say-hello.js
view/

Automatic Symlinking

Module assets aren't served from the modules/ directory — the framework symlinks them into the public web root automatically. On the first request to a module, Module::__construct() creates a symlink:

modules/HelloWorld/assets/
/assets/helloworld/

The symlink target uses the lowercase module name (no hyphens, no PascalCase). This happens once and is transparent — you never need to create symlinks manually or run a build step.

One-Time Operation

The symlink is created on the first request and persists until you delete it. If you rename or move a module, remove the old symlink from www/assets/ and it will be recreated on the next request.

Auto-Loading by Convention

Zero auto-loads CSS and JS files based on their filenames, using kebab-case names that match URL segments:

File Name Pattern Loaded When Example
{module}.css / {module}.js Every endpoint in the module hello-world.css loads for all HelloWorld pages
{endpoint}.css / {endpoint}.js That specific endpoint only say-hello.css loads only for /hello-world/say-hello

Names use kebab-case to match URL format, not the PascalCase/camelCase class names. The framework converts internally.

Example: HelloWorld Module

Given this asset structure:

css/
├── hello-world.css    → Loaded for ALL HelloWorld endpoints
└── say-hello.css      → Loaded only for /hello-world/say-hello
js/
├── hello-world.js     → Loaded for ALL HelloWorld endpoints
└── say-hello.js       → Loaded only for /hello-world/say-hello

When a user visits /hello-world/say-hello, the framework automatically includes:

  1. hello-world.css — matches the module name
  2. say-hello.css — matches the endpoint name
  3. hello-world.js — matches the module name
  4. say-hello.js — matches the endpoint name

When the same user visits /hello-world (index endpoint), only the module-level files load — say-hello.css and say-hello.js are excluded.

Auto-Load Helper Methods

The framework provides three methods on the Response base class that output the appropriate HTML tags for auto-loaded assets:

Method Output Description
$this->getStylesheets() <link> tags Outputs CSS files matching the module and endpoint names
$this->getScripts() <script> tags Outputs JS files matching the module and endpoint names
$this->getComponents() Component includes Loads ShadowComponent HTML files for the module

In the default frame, these methods are called automatically in frame/head.php. You never need to call them unless you override the frame.

Frame Overrides

If a module provides its own frame files (for example modules/MyModule/frame/head.php), you must call the auto-load helpers manually to preserve automatic asset loading:

<head>
    <meta charset="UTF-8">
    <title><?php echo $this->title; ?></title>

    <!-- Your custom head content -->
    <link rel="stylesheet" href="/assets/mymodule/css/custom-base.css">

    <!-- Preserve auto-loading -->
    <?php $this->getStylesheets(); ?>
    <?php $this->getScripts(); ?>
    <?php $this->getComponents(); ?>
</head>
Don't Forget the Helpers

If you override frame files without calling $this->getStylesheets() and $this->getScripts(), your module's convention-based CSS and JS files won't load. The framework has no other mechanism to inject them.

Non-Convention Assets

Files that don't match the {module}.css or {endpoint}.css naming pattern won't be auto-loaded. To include them, reference them directly in your frame or view:

<!-- Manually include a non-convention asset -->
<link rel="stylesheet" href="/assets/mymodule/css/vendor-lib.css">
<script src="/assets/mymodule/js/chart-library.js"></script>

<!-- Then auto-load convention-based assets -->
<?php $this->getStylesheets(); ?>
<?php $this->getScripts(); ?>

This is useful for third-party libraries, shared utility files, or any CSS/JS that doesn't follow the naming convention.