a536baabd6
Adds the complete project foundation: - BMAD BMM workflow tooling (_bmad/) - Claude slash commands, skills, and project memories (.claude/) - ESP32 firmware scaffold (PlatformIO + Waveshare e-ink driver) - .gitignore excluding _bmad-output/ and .pio/ build artifacts Planning artifacts (PRD, architecture, epics) are intentionally not tracked — they live in _bmad-output/ per project convention. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
403 lines
11 KiB
Markdown
403 lines
11 KiB
Markdown
---
|
|
name: oro-frontend-dev
|
|
description: OroCommerce frontend development reference covering storefront themes, back-office themes, layouts, SCSS, JavaScript architecture (Chaplin/Backbone), page components, templates, and asset management. Use when customizing the storefront UI, modifying admin templates, writing JS components, or styling OroCommerce pages.
|
|
---
|
|
|
|
# OroCommerce Frontend Developer
|
|
|
|
Reference for frontend/UI development on OroCommerce.
|
|
Full docs: https://doc.oroinc.com/frontend/
|
|
|
|
## Two Theme Systems
|
|
|
|
OroCommerce has two separate theme architectures:
|
|
|
|
| Area | Framework | Templates | JS | Docs |
|
|
|---|---|---|---|---|
|
|
| Storefront | OroLayouts (SEO-friendly, no SPA) | Layout YAML + Twig blocks | Chaplin/Backbone | https://doc.oroinc.com/frontend/storefront/ |
|
|
| Back-office | Placeholders (SPA-like, Twig+Underscore) | Twig extends @OroUI | Chaplin/Backbone | https://doc.oroinc.com/frontend/back-office/ |
|
|
|
|
Themes are NOT interchangeable between storefront and back-office.
|
|
|
|
## Storefront Theming
|
|
|
|
### Theme Structure
|
|
|
|
```
|
|
Resources/views/layouts/{theme_name}/
|
|
theme.yml # theme definition (label, parent, icon)
|
|
config/
|
|
assets.yml # SCSS entry points
|
|
jsmodules.yml # JS module registration
|
|
datagrids.yml # frontend datagrid configs
|
|
templates/ # Twig block templates
|
|
page/ # per-page layout overrides
|
|
```
|
|
|
|
### theme.yml
|
|
|
|
Basic:
|
|
```yaml
|
|
label: 'My Custom Theme'
|
|
parent: default
|
|
icon: bundles/mybundle/images/theme-icon.png
|
|
groups: [commerce]
|
|
rtl_support: true
|
|
```
|
|
|
|
Full configuration with admin-selectable options:
|
|
```yaml
|
|
label: 'Acme Theme'
|
|
parent: default
|
|
groups: [commerce]
|
|
rtl_support: true
|
|
|
|
configuration:
|
|
sections:
|
|
header:
|
|
label: Header
|
|
options:
|
|
promotional_content:
|
|
label: oro_frontend.theme.default.configuration.header.promotional_content.label
|
|
type: content_block_selector
|
|
options:
|
|
required: false
|
|
standalone_main_menu:
|
|
label: oro_frontend.theme.default.configuration.header.standalone_main_menu.label
|
|
type: checkbox
|
|
default: checked
|
|
previews:
|
|
checked: 'bundles/orofrontend/images/previews/menu-standalone.png'
|
|
unchecked: 'bundles/orofrontend/images/previews/menu-hamburger.png'
|
|
product_listing:
|
|
label: 'oro.product.theme.default.configuration.product_listing.label'
|
|
options:
|
|
filters_position:
|
|
label: 'oro.product.theme.default.configuration.product_listing.filters_position.label'
|
|
type: radio
|
|
default: sidebar
|
|
values:
|
|
top: 'oro.product.theme.default.configuration.product_listing.filters_position.values.top'
|
|
sidebar: 'oro.product.theme.default.configuration.product_listing.filters_position.values.sidebar'
|
|
```
|
|
|
|
Configuration option types: `content_block_selector`, `menu_selector`, `checkbox`, `radio`, `select`.
|
|
|
|
### Layout Updates
|
|
|
|
Layout updates are YAML files that modify page structure:
|
|
|
|
```yaml
|
|
layout:
|
|
actions:
|
|
- '@setBlockTheme':
|
|
id: page_container
|
|
themes: '@MyBundle/layouts/my_theme/page/layout.html.twig'
|
|
- '@add':
|
|
id: my_custom_block
|
|
parentId: page_content
|
|
blockType: text
|
|
options:
|
|
text: '=data["locale"].getLanguage()'
|
|
- '@remove':
|
|
id: unwanted_block
|
|
- '@move':
|
|
id: existing_block
|
|
parentId: new_parent
|
|
```
|
|
|
|
Place layout updates in route-specific directories to target individual pages:
|
|
```
|
|
Resources/views/layouts/{theme}/
|
|
layout.yml # global layout (all pages)
|
|
oro_frontend_root/frontend_root.yml # homepage
|
|
oro_product_frontend_product_index/product_index.yml # PLP
|
|
oro_product_frontend_product_view/product_view.yml # PDP
|
|
oro_checkout_frontend_checkout/checkout.yml # checkout
|
|
```
|
|
|
|
### Block Types
|
|
|
|
| blockType | Purpose |
|
|
|---|---|
|
|
| `container` | Wraps child blocks with a div |
|
|
| `text` | Static text or expression |
|
|
| `block` | Custom Twig-rendered block |
|
|
| `content_block` | References admin-managed content block by `alias` |
|
|
| `menu` | Renders a navigation menu |
|
|
|
|
### Layout Data Expressions
|
|
|
|
Access data providers and context variables in YAML options:
|
|
|
|
```yaml
|
|
options:
|
|
vars:
|
|
# Data provider methods (registered via layout_data_provider tag)
|
|
topCategories: '=data["category_provider"].getRootCategoriesWithSlugs()'
|
|
featuredCount: '=data["product_provider"].getFeaturedProductCount()'
|
|
ctaLinks: '=data["cta_link_provider"].getLinks(data["menu"].getMenu("commerce_main_menu"))'
|
|
|
|
# Context variables (route params, request attributes)
|
|
category: '=data["category_provider"].getCategoryById(context["category_id"])'
|
|
```
|
|
|
|
### @addTree Action
|
|
|
|
Add multiple blocks at once:
|
|
|
|
```yaml
|
|
- '@addTree':
|
|
items:
|
|
footer_menu:
|
|
blockType: menu
|
|
options:
|
|
item: '=data["menu"].getMenu("commerce_footer_links")'
|
|
depth: 2
|
|
tree:
|
|
page_footer_base:
|
|
footer_menu: ~
|
|
```
|
|
|
|
### @setBlockTheme with Multiple Files
|
|
|
|
```yaml
|
|
- '@setBlockTheme':
|
|
themes:
|
|
- 'templates/layout.html.twig'
|
|
- 'templates/page-footer/footer_branding.html.twig'
|
|
- 'templates/navigation/sidebar_menu.html.twig'
|
|
```
|
|
|
|
### Twig Block Naming Convention
|
|
|
|
Block names in layout Twig templates follow: `_{blockId}_widget`
|
|
|
|
```twig
|
|
{% block _my_custom_block_widget %}
|
|
<div {{ block('block_attributes') }}>
|
|
{{ block_widget(block) }}
|
|
</div>
|
|
{% endblock %}
|
|
```
|
|
|
|
### SCSS / Stylesheets
|
|
|
|
Register SCSS in `config/assets.yml`:
|
|
|
|
```yaml
|
|
styles:
|
|
inputs:
|
|
- 'bundles/acmetheme/scss/styles.scss'
|
|
output: 'css/styles.css'
|
|
```
|
|
|
|
Recommended SCSS directory organization:
|
|
|
|
```
|
|
scss/
|
|
styles.scss # main entry point (@import all)
|
|
variables/
|
|
_colors.scss # color palette as Sass maps
|
|
_typography.scss
|
|
_mixins.scss
|
|
components/
|
|
_product-cards.scss
|
|
_buttons.scss
|
|
layout/
|
|
_page-header.scss
|
|
_page-footer.scss
|
|
_product-view.scss
|
|
_product-index.scss
|
|
_checkout.scss
|
|
navigation/
|
|
_sidebar.scss
|
|
_topbar.scss
|
|
```
|
|
|
|
Color palette using Sass maps (Oro pattern):
|
|
|
|
```scss
|
|
@use 'sass:map';
|
|
|
|
$theme-color-palette: (
|
|
'brand': (
|
|
'primary': #424242,
|
|
'secondary': #A89571,
|
|
),
|
|
'semantic': (
|
|
'success': #005E1F,
|
|
'error': #D32F2F,
|
|
)
|
|
) !default;
|
|
|
|
$color-palette: map.deep-merge($color-palette, $theme-color-palette);
|
|
```
|
|
|
|
Access via `get-var-color('brand', 'primary')`. Build: `php bin/console oro:assets:build`.
|
|
|
|
### SVG Icons
|
|
|
|
Custom icons go in the theme's icons directory. Register via theme config.
|
|
|
|
## Back-Office Theming
|
|
|
|
### Twig Template Overrides
|
|
|
|
Override admin templates by mirroring directory structure under `Resources/views/`.
|
|
|
|
Common base templates to extend:
|
|
- `@OroUI/actions/index.html.twig` -- list/grid pages
|
|
- `@OroUI/actions/view.html.twig` -- detail/view pages
|
|
- `@OroUI/actions/update.html.twig` -- create/edit form pages
|
|
|
|
Use Oro UI macros for consistent rendering:
|
|
|
|
```twig
|
|
{% import '@OroUI/macros.html.twig' as UI %}
|
|
{{ UI.renderProperty('Label', value) }}
|
|
{{ UI.renderHtmlProperty('HTML Label', htmlValue) }}
|
|
```
|
|
|
|
### Placeholders
|
|
|
|
Inject content into predefined back-office page regions.
|
|
|
|
```yaml
|
|
# Resources/config/oro/placeholders.yml
|
|
placeholders:
|
|
oro_view_additional_data:
|
|
items:
|
|
my_custom_section:
|
|
order: 50
|
|
template: '@AcmeExample/Placeholder/section.html.twig'
|
|
applicable: '@acme.placeholder_filter->isApplicable'
|
|
```
|
|
|
|
Common placeholder targets:
|
|
- `oro_view_additional_data` -- entity view page extra sections
|
|
- `oro_widget_side_bar` -- sidebar widgets
|
|
- `oro_title_before` / `oro_title_after` -- title area
|
|
|
|
Conditional rendering via `applicable` filter (service method returning bool).
|
|
Placeholders are back-office only; use Layout Updates for storefront.
|
|
|
|
### SCSS
|
|
|
|
Override admin SCSS variables or add new stylesheets via theme settings.
|
|
|
|
## JavaScript Architecture
|
|
|
|
Built on Chaplin.js (extends Backbone.js). Key concepts:
|
|
|
|
### Page Components
|
|
|
|
The primary extension mechanism. Define in HTML via data attributes:
|
|
|
|
```html
|
|
<div data-page-component-module="mybundle/js/app/components/my-component"
|
|
data-page-component-options="{{ options|json_encode }}">
|
|
</div>
|
|
```
|
|
|
|
Component lifecycle: created on `page:update`, disposed on page change.
|
|
|
|
### JS Module Registration
|
|
|
|
Register modules in `Resources/config/jsmodules.yml`:
|
|
|
|
```yaml
|
|
dynamic-imports:
|
|
mybundle:
|
|
- mybundle/js/app/components/my-component
|
|
|
|
aliases:
|
|
jquery$: oroui/js/extend/jquery
|
|
|
|
# Override an existing module with custom implementation
|
|
map:
|
|
'*':
|
|
'orofrontend/js/app/datafilter/frontend-multiselect-decorator': 'acmetheme/js/custom-multiselect-decorator'
|
|
```
|
|
|
|
### App Modules
|
|
|
|
Execute code at application startup (auto-initialized on every page):
|
|
|
|
```yaml
|
|
app-modules:
|
|
- acmetheme/js/app/modules/inventory-checker
|
|
- acmetheme/js/app/modules/checkout-shipping-options
|
|
```
|
|
|
|
### AMD Module Pattern
|
|
|
|
Oro's JS uses AMD `define()` with the `oroui/js/mediator` for cross-component communication:
|
|
|
|
```javascript
|
|
define(function(require) {
|
|
'use strict';
|
|
|
|
const mediator = require('oroui/js/mediator');
|
|
const $ = require('jquery');
|
|
|
|
class MyComponent {
|
|
constructor() {
|
|
$(document).ready(() => {
|
|
mediator.on('checkout:shipping-method:rendered', this.onShippingRendered);
|
|
});
|
|
}
|
|
|
|
onShippingRendered() {
|
|
// DOM manipulation after Oro renders shipping methods
|
|
}
|
|
}
|
|
|
|
new MyComponent();
|
|
return MyComponent;
|
|
});
|
|
```
|
|
|
|
Key mediator events: `checkout:shipping-method:rendered`, `filters-manager:after-applying-state`, `page:update`, `page:afterChange`.
|
|
|
|
### Page Events
|
|
|
|
System events during page lifecycle:
|
|
1. `page:beforeChange`
|
|
2. `page:request`
|
|
3. `page:update` -- content is updated, init components
|
|
4. `page:afterChange` -- all components initialized
|
|
|
|
### File Naming Convention
|
|
|
|
```
|
|
js/app/
|
|
components/ # PageComponent implementations
|
|
controllers/ # Chaplin controllers (rare -- PageController handles all)
|
|
models/ # Backbone models and collections
|
|
views/ # Backbone views
|
|
modules/ # App modules (startup code)
|
|
templates/ # Underscore.js templates (*.html)
|
|
```
|
|
|
|
## Asset Build
|
|
|
|
```bash
|
|
php bin/console oro:assets:build # build all themes
|
|
php bin/console oro:assets:build --watch # watch mode for dev
|
|
php bin/console assets:install # symlink public assets
|
|
```
|
|
|
|
Requires Node.js >= 22 and NPM > 10.
|
|
|
|
## RTL Support
|
|
|
|
Enable Right-to-Left UI via theme configuration.
|
|
See: https://doc.oroinc.com/frontend/rtl-support/
|
|
|
|
## Additional Resources
|
|
|
|
- Storefront how-to guides: https://doc.oroinc.com/frontend/storefront/how-to/
|
|
- JS modularity deep-dive: https://doc.oroinc.com/frontend/javascript/javascript-modularity/
|
|
- Storefront design/style guide (Figma): https://doc.oroinc.com/frontend/storefront-style-guide/
|