Frontend Best Practices and Restrictions

This page gives the rules for frontend work in a Festi project. Each rule has its reason.

The most important rule: use what the framework has before you write something new. The theme, the DGS, and the HUI components supply most of an admin interface.

Decide what to use

flowchart TD A["You need a new screen"] --> B{"A list or a form<br>of stored data?"} B -- yes --> DGS["Use the DGS"] B -- no --> C{"A dashboard with<br>blocks?"} C -- yes --> ST["Use StageBuilder"] C -- no --> D{"Standard elements:<br>forms, buttons, dialogs?"} D -- yes --> HUI["Plugin template<br>with HUI components"] D -- no --> E{"Much state in<br>the browser?"} E -- no --> TPL["Plugin template<br>with a small script"] E -- yes --> R["React application<br>in the plugin"]
Need Use Page
List, filter, add, edit, remove stored data DGS Data Grid Store
A special input in a DGS form Custom DGS field Custom DGS Fields
Dashboard of blocks StageBuilder StageBuilder
Form, button, dialog, message, tabs HUI components HUI guide
Content that is special to one page Plugin template Templates
Page with much state in the browser React application ReactJS

Reuse the theme

  • Use one theme for the admin area. SmartAdmin is the primary admin theme. A second admin theme makes each plugin more difficult to test.
  • Do not edit a theme that comes from a package or a submodule. The next update removes your change. Use the methods in How to Adjust SmartAdmin.
  • Do not edit files in vendor/. This includes the Theme package templates and ThemeAssets.
  • Add to the layout with theme events. Do not copy main.phtml to add one tag. See the Theme package.
  • Make a custom theme only for a different design. A website for visitors needs its own theme. An admin panel does not. See How to Make a Custom Theme.

Reuse the DGS

  • Do not write a list or a form by hand when a DGS can make it. The DGS supplies permissions, validation, filters, the pager, and the dialogs.
  • Change a DGS in this sequence: schema attributes, then events, then a custom field, then a template. See DGS templates.
  • Replace the smallest template part. A copy of store/list.phtml does not get the corrections of later framework versions.
  • Keep the CSS classes of the DGS templates. jimbo.js finds the elements with them.

Templates

  • A template shows data. It does not get data. Do not call the database or a different plugin in a template. Get the data in the plugin method.
  • Give data to a template as local variables (fetch('name.phtml', $vars)). Use assign() only for data that many templates of the request use.
  • Do not use the variable names templatePath and localVars. The template system uses them.
  • Divide a large template into parts in templates/parts/. Small parts are easy to test and to use again.
  • Escape each value that comes from a user: htmlspecialchars($value).
  • Use __() for each text.
  • Use the full PHP tag <?php echo ... ?>. Short tags (<?=) are not permitted.
  • Use $this->getUrl() and $this->getStaticUrl() for URLs. Do not write a URL as fixed text.
  • Add the IDE type hints at the top of each template (@var).

See Templates.

HUI components

  • Use a HUI component for each form, input, button, dialog, and message. Do not write this HTML in a template.
  • Change the look with setCSS() and the project CSS. Replace a component template only when the HTML structure must change.
  • Replace a component template in the smallest scope that is sufficient: the templates/ui/ directory of one plugin, or the project templates/ui/ directory for all plugins.
  • Add a component when three or more templates need the same HTML.

See the HUI guide.

CSS

Where to put CSS

CSS for File
The project (colors, logo, corrections of the theme) static/css/styles.css of the project
One plugin plugins/<Name>/static/css/
One DGS field class or one React application The directory of that field or application
  • Do not use the style attribute and <style> blocks in templates. The exception is a value that the server calculates, for example a color from the database.
  • Do not use !important. The project CSS loads after the theme CSS. A selector with the same weight is sufficient.
  • Do not change a Bootstrap class or a theme class for all pages from a plugin. Add your own class to the element.

See Static Files.

Class names

Festi uses a naming system with a prefix for the role of the class. It is a simple form of BEM (block, element, modifier).

Prefix Role Example
b- Block: an independent part of the interface. b-order-card
e- Element: a part of a block. e-title, e-total
m- Modifier: a variant or a state. m-paid, m-hidden
f- A form that JavaScript processes. f-db-form
i- An input that JavaScript reads. i-grouped-item
<div class="b-order-card m-paid" id="order-15">
    <div class="e-title">Order #15</div>
    <div class="e-total">120.00</div>
</div>
.b-order-card { border: 1px solid #ddd; }
.b-order-card .e-title { font-weight: bold; }
.b-order-card.m-paid { border-color: #5cb85c; }

Rules:

  • Start the block name with the subject of your plugin (b-order-card, not b-card). Then two plugins do not use the same name.
  • Write the CSS of an element with its block: .b-order-card .e-title. An element name alone (e-title) is not unique.
  • Use lowercase letters and hyphens.
  • A modifier is a separate class. Do not join modifiers into one name.
  • Use a class for CSS and for JavaScript selectors. Use an id only for one unique element.
  • Do not give a new meaning to a class of the framework: b-db-table, e-db-action, f-db-form, e-input, i-grouped-item, m-field-invalid-value.

JavaScript

  • Put JavaScript in files in the static/js directory of the plugin. Register the files in the plugin method with includeStatic().
  • Do not write <script> blocks with logic in a template. A short call of a function with data from the server is permitted.
  • Do not make JavaScript text on the server from user input. Send data (JSON or data- attributes).
  • Use jQuery, not $.
  • Use const and let, not var.
  • Do not start a variable name with $.
  • Put the functions of a plugin in one object with the name of the plugin (const Orders = {...}). Do not add many global functions.
  • Do not load a second copy of jQuery or Bootstrap.
  • Start the code of a DGS form in a listener for Jimbo.EVENT_ON_DISPLAY_STORE_FORM. The form is not in the page when the document is ready.
  • Do not change jimbo.js. Add a listener or a function Jimbo.set<Type>Response in your file.

See the Jimbo.js reference.

Server and browser

  • Do not print output in a plugin method. Fill the Response object.
  • Use onJson methods for requests from JavaScript and onAjax methods for forms that submit into the hidden <iframe>.
  • Give each reply to Jimbo.response(). Then messages, form errors, and redirects work in the same manner on each page.
  • Confirm a change with addNotification() and Response::ACTION_RELOAD or Response::ACTION_REDIRECT.
  • Validate on the server. The validation in the browser helps the user. It does not protect the data.
  • Start the names of your actions and events with the name of your plugin.

See Response and Frontend and backend events.

Restrictions

These things do not work. Do not design a solution that needs them.

Restriction Use this
A plugin templates directory does not replace the template of a different plugin. The directory templates/plugins/<Name>/ of the theme, or a template directory with a postfix. See Templates.
A plugin templates directory does not replace a default DGS template. The DGS looks there last. A lookup directory of the store view. See DGS templates.
A file in a theme or a plugin does not replace a DGS field template. A field class with its own template directory. See Custom DGS Fields.
A JSON response does not load registered JS and CSS files. A response for a dialog loads them only if the theme adds them (SmartAdmin does). Register the files on the page that opens the dialog. See Static Files.
includeStatic() accepts only .js and .css files. getStaticUrl() for other files.
There is no list of scripts for the end of <body>. jQuery(document).ready() in a file that <head> loads.
jimbo.js does not submit a form with the class f-form. The HUI form or the HUI dialog with an onAjax method.
Plugins do not share template variables. Call a method of the other plugin to get data.
The framework has no microfrontend runtime. One React application for each plugin. See ReactJS.
A project has no JavaScript test runner for the scripts of its plugins. Only jimbo.js has tests, in the ThemeAssets repository. PHPUnit render tests. See Frontend Tests.

Before you send a change to review

  1. The page shows no error in the browser console.
  2. Each new text uses __().
  3. No file in vendor/ or in a theme from a package has a change.
  4. New CSS classes use the prefixes b-, e-, m-.
  5. New JavaScript is in a file in static/js.
  6. A render test exists for each new template part or component with logic. See Frontend Tests.