A frontend framework built entirely on web standards.
Modular Cube is a frontend framework that builds directly on web standards, enabling modular and maintainable applications without virtual DOMs or build pipelines.
Modular Cube is a frontend framework designed around web standards. It operates entirely on native browser features such as Custom Elements, HTML Templates, ES Modules, and the Shadow DOM. This foundation enables user interface components to be defined with standard HTML and JavaScript while supporting scoped rendering, declarative structure, and encapsulated behaviors. The framework avoids virtual DOM abstractions and transpilation layers. By following this model, Modular Cube supports the development of modular, maintainable applications that run directly in the browser without additional build processes or external dependencies.
Built entirely on native browser features such as Custom Elements, Shadow DOM, and ES Modules. No external dependencies or build tools are required.
Avoids the overhead from complex runtime engines or compilation steps, making applications faster to load and easier to maintain.
Encapsulation by design, with isolated structure and styling through Shadow DOM, ensuring clean separation without global conflicts.
Interoperable and compatible, since it uses only HTML, CSS, and JavaScript, allowing seamless integration with existing codebases and libraries.
Reduced abstraction layers, which promotes long-term sustainability, predictability, and easier debugging.
Embed JavaScript expressions directly in your HTML using
${this.expression}. Supported in text content, attributes,
class names, and inline styles.
Use if directives and its extended forms
(e.g. else if, else) to selectively include
elements in the DOM based on runtime conditions.
Use forEach directives to generate repeated
markup from iterable data structures.
Bind native DOM events using a consistent pattern
(clickEvent, inputEvent, etc.) to trigger scoped
logic without manually registering listeners.
DOM updates are scoped and localized, reducing overhead and eliminating re-renders.
The architecture of Modular Cube is structured around the core features natively supported by browsers. By relying on these capabilities, the framework ensures that components remain portable, efficient, and aligned with the evolution of the web platform.
Encapsulate behavior, state, and rendering logic in self-contained components defined as JavaScript classes. Each element is registered as a native HTML tag, allowing seamless integration into the DOM.
Provides reusable blocks of markup that are parsed but not rendered immediately. Templates serve as the foundation for dynamic rendering and runtime instantiation.
Enable modular development by defining clear boundaries for imports and exports. All components and logic are authored as ES modules, ensuring compatibility with the standard JavaScript module system.
Delivers encapsulation of both structure and styling, isolating internal implementations from the global DOM while supporting predictable component behavior.
Modular Cube does not aim to create a new way of building the web, it simply leverages the web as it exists today. Its goal is to empower developers to write UI code that is expressive, explicit, and maintainable, while avoiding the hidden complexity often introduced by large abstractions. It makes no assumptions about how your application is structured, but provides lightweight conventions to support common frontend needs: data-binding, conditional logic, event handling, and templating. By using only platform features, Modular Cube ensures forward compatibility and encourages a development style that aligns closely with how browsers operate. It is ideal for projects where performance, clarity, and long-term maintainability are key concerns.
These web standards work together to provide a consistent foundation for building components. They ensure that applications remain compatible with the native browser environment, while keeping the architecture simple and easy to extend.
Modular Cube lifecycle builds on the native Web Component lifecycle, extending it with custom states for more granular control over initialization and rendering. This ensures components are predictable, configurable, and modular throughout their lifetime in the DOM.
The lifecycle of a component defines the sequence of stages a component passes through from creation to removal. It combines standard Web Component callbacks with framework specific processing states to ensure proper initialization, configuration, and rendering.
constructor()
connectedCallback()
adoptedCallback()
attributeChangedCallback()
disconnectedCallback()
When a component instance is created, the constructor method is invoked.
In Modular Cube, this immediately calls the preProcess state. At this
stage, the component loads and validates its configuration, ensuring all required
parameters (such as references to HTML, CSS, or behavior settings) are in place before
any further lifecycle steps begin.
When a componentโs constructor is invoked, the preProcess state is
automatically executed.
During this stage, configuration data is loaded, validated, and prepared before any
rendering or attachment to the DOM occurs.
The constructor expects a mandatory configuration object with the following structure.
(MyComponent.config.json)
(MyComponent.js)
The connectedCallback() method is invoked automatically when a custom
element is added to the DOM.
This marks the point where the component becomes active and begins its operational
lifecycle.
This stage is aligned with the toProcess, atProcess, and
inProcess states, which provide a structured sequence for initialization,
rendering, and post-render logic.
This structured approach ensures that components are consistently initialized and
rendered in a predictable order, reducing errors and improving maintainability.
When the component is first attached to the DOM, it enters the toProcess
state.
This marks the beginning of the active lifecycle, where the component validates
prerequisites and prepares resources, but no rendering has yet taken place.
Immediately after prerequisites are resolved within connectedCallback, the
atProcess state runs.
The component has reached the point where rendering logic is about to be executed.
This state occurs after the component has completed its rendering phase. The DOM is now fully available for manipulation, and all initial behaviors are active. At this point, logic that depends on the rendered structure, such as querying the DOM with querySelector, setting focus, or attaching runtime event listeners can be safely executed.
The adoptedCallback is a lifecycle method that is called when a custom
element is moved from one document to another, such as between iframes or windows.
The attributeChangedCallback is a lifecycle method in Web Components,
specifically in the Custom Elements API.
It's used in custom HTML elements to detect when one of the element's observed
attributes has changed.
The disconnectedCallback is a lifecycle method thatโs called when a custom
element is removed from the DOM.
The Modular Cube component lifecycle defines how components transition through
initialization, configuration, rendering, and cleanup.
It combines the standard Web Component lifecycle methods with custom internal states
preProcess, toProcess, atProcess, and
inProcess to ensure predictable behavior, structured processing, and safe
DOM interactions throughout a componentโs existence.
The component lifecycle in Modular Cube unifies the standard Web Component callbacks with additional custom states. This approach establishes a predictable sequence in which configuration is validated, resources are prepared, rendering takes place, and post-render behavior becomes available. By following this structured progression, components remain consistent, modular, and easier to maintain, regardless of complexity or use case.
Configuration files, initialization logic, and the main entry point reside at the root level.
UI components are grouped within the app/ directory.
Supporting folders such as static/, modules/,
and services/ are used to manage assets, reusable
functionality, and core application logic.
This organization establishes distinct responsibilities for different parts of the project, making it easier to navigate, maintain, and extend over time.
To make a component available to the application, it must be registered in the global
application configuration file: app.config.json.
This setup allows the framework to dynamically load and define the components at
runtime.
The application configuration file app.config.json contains a root object
named modularCube, which describes both the application entry
point and the components that will be registered.
The application attribute is optional. Its purpose is to define the root
application container.
Dom.init(), it first
looks at the components attribute list.
If an entry component (for instance, app-root) is defined there, it
will be instantiated as the root.
components attribute list,
Dom.init() will then check whether an application
attribute exists.
If present, the application will be used to define and initialize
the root container.
The init.js file is the bootstrap script.
Its job is to initialize your application, load the configuration, and register
components before rendering.
After defining and registering all components, the application must initialize the
system using the Dom.init() method.
By default, app.config.json should be placed at the same folder level as
init.js.
If no parameter is provided, the initializer will look for it automatically.
If the configuration file is stored elsewhere, you can provide its path explicitly:
The index.html is the root document of your project.
It loads the initialization script and acts as the starting point for rendering UI
components.
To ensure components are initialized properly at application startup, the
init.js file must be imported in the main HTML file.
The import map, initialization script, and root component provide the foundation of the
application.
The index.html file serves as the starting point where these elements are
brought together to initialize and render the user interface.
Each component in Modular Cube is encapsulated using the Shadow DOM to ensure scoped styles and isolated behavior. By convention, a component can be composed of four main files:
The framework allows you to keep HTML and CSS in separate files
for clarity.
A configuration file (.config.json) to declare metadata,
file paths, and runtime options.
A CSS stylesheet (.css) to provide encapsulated styles.
An HTML template (.html) to define the componentโs
structure.
A JavaScript module (.js) to implement the componentโs logic
and lifecycle.
Together, these files ensure that each component remains self-contained, easy to reason about, and reusable across different parts of the application.
The configuration file defines metadata, file references, and runtime behaviors for the
component.
It specifies the paths to the associated HTML and CSS files,
along with options such as appendToBody, lazyLoading, and
debug.
The stylesheet provides styles scoped to the component through the Shadow DOM. This ensures that styles remain encapsulated and do not leak into the global document. Keeping styles in a separate file helps maintain a clean separation between structure, logic, and presentation.
The HTML template defines the componentโs structure and static markup. It serves as the foundation of the componentโs UI, which is later combined with the stylesheet and logic during initialization. Using an external template file improves readability and keeps layout concerns separate from logic.
The JavaScript file implements the componentโs logic, lifecycle, and behaviors.
It loads the configuration, processes the referenced HTML and
CSS files (via preProcess()), and manages dynamic
interactions.
This file is responsible for defining how the component behaves.
Each of these files plays a specific role in defining a component.
The configuration file describes its metadata and behavior, the
CSS handles encapsulated styling, the HTML defines its
structure, and the JavaScript manages its logic and lifecycle.
Together, they provide a clear and modular approach to building self-contained,
maintainable components.
HTML directives provide a declarative way to extend component templates by using attributes. They make components easier to read and maintain by clearly expressing intent in the markup. Directives support dynamic binding, allowing variables, expressions, or configuration values to be injected directly into the template. They can also control behavior, such as managing conditional rendering, iterating over collections, or binding events to elements.
Variables in Modular Cube templates are expressed in two complementary forms: template interpolation and parameter injection. Both approaches allow component data to be incorporated into the DOM in a controlled and explicit manner, but they serve distinct purposes.
The template interpolation enables the embedding of
JavaScript expressions directly within HTML content, using the
${this.expression} syntax.
This mechanism allows dynamic rendering of component state and computed values inline
within native HTML templates.
Interpolation is resolved at render time and evaluated within the current data context
(typically the component instance this or the shadow root scope).
Only valid JavaScript expressions are allowed. These must reference properties or methods available in the current scope.
Functions used within expressions must be side-effect-free and return a value.
Expressions should remain simple and easy to read. Complex logic or deeply nested expressions should be avoided.
Expressions are enclosed in $ { expression }:
class, title,
placeholder, ... ).Inspector View (Elements)
The parameter injection feature provides a mechanism to
inject parameter values directly into templates.
Unlike interpolation, which operates inline, parameter injection designates explicit
placeholder elements that are fully replaced with evaluated values during the initial
rendering.
The <param value=""> element is used as a placeholder for injecting
parameter values into HTML templates.
At render time, the placeholder is replaced by the evaluated parameter, and the
<param value=""> tag itself does not persist in the DOM.
Each <param> element with a value
attribute is replaced with the current value of the corresponding
parameter during the initial render.
Parameter values are evaluated from the componentโs declared parameter scope.
Complex or computed values should be resolved before assignment to parameters.
The <param> tag is a placeholder element and does not
produce standalone DOM elements after rendering; it is replaced entirely
by the parameterโs value.
Parameter injection is expressed using the
<param value=""> element.
${this.expression}), parameter
injection cannot be used inside attributes such as class,
style, or title.<param> element is limited to injecting values directly
into DOM content.Inspector View (Elements)
Together, interpolation and parameter injection provide complementary approaches for embedding dynamic values into component templates. While interpolation supports inline expressions within attributes and text nodes, parameter injection offers a direct mechanism for inserting parameter values into the DOM.
The if directive provides conditional rendering in HTML templates using declarative expressions. It enables developers to control whether a given element and its children should be rendered based on the evaluation of a boolean condition.
Single Condition Per Element: Each element can
only have one conditional directive (if,
elseIf, or else).
Evaluation Order: Conditions are evaluated top-to-bottom. The first matching branch is rendered; subsequent branches are skipped.
Mutual Exclusivity: Only one element in a
chain (if โ elseIf โ else) is
rendered at a time.
Expression Validity: Conditions must resolve to a boolean value or an expression that can be evaluated to true/false.
Clarity Over Complexity: Keep conditions simple and readable. Prefer computed properties or helper methods for complex logic.
Mutual Exclusivity: Only one element in a
chain (if โ elseIf โ else) is
rendered at a time.
Conditional directives are expressed as HTML attributes on elements and evaluated at render time:
if="${this.expression}" โ Renders the element only if the
expression evaluates to true.elseIf="${this.expression}" โ Renders the element if all preceding
if/elseIf conditions are false and this expression evaluates to
true.else โ Renders the element if all preceding
if/elseIf conditions are false.Supported Contexts:
if โ elseIf โ
else.this and
may reference component properties or methods.Inspector View (Elements)
Inspector View (Elements)
Inspector View (Elements)
In JavaScript, you can absolutely use if, else if,
else if without a final else.
The else block is completely optional.
ifif...elseif...else if...elseif...else if...else if
(with or without else)
The if directive allows declarative, structured control
of what elements are rendered based on logical conditions. Support for
else if, else, and nested conditionals provides flexibility
while maintaining clear and predictable rendering behavior.
The use of native HTML syntax and controlled evaluation reinforces clarity, performance,
and consistency with modern web development practices.
The forEach directive in enables declarative iteration over collections within HTML templates. It renders a repeated structure for each item in a given array or iterable, following a clear and scoped expression pattern. This approach brings structured, readable list rendering to markup using standard JavaScript expression syntax. This directive is designed to integrate seamlessly with the component's data context and maintain clarity in both simple and nested list structures.
Scoped Variables: The loop variable is only valid inside the iteration block. It should not conflict with variables declared in outer scopes.
Combined with Directives: Loop-generated
elements can also use conditional directives (if,
elseIf, else) within the iteration body.
Static and Dynamic Data: Works with both static arrays defined at initialization and dynamic data updated during component lifecycle.
The forEach directive is expressed as an HTML attribute and enables iteration over iterable data structures:
${this.forExpression} โ Repeats the element for each value in the
iterable this.items, binding the current value to the loop
variable.Supported Contexts:
${this.forExpressionItem}) or parameter injection
(<param value="${this.forExpressionItem}">).Inspector View (Elements)
The forEach directive offers a concise and powerful way to generate HTML content dynamically based on collection data. With native expression support and clear scope handling, it supports structured iteration at any depth and promotes maintainable template design. Repeated content is easy to define and aligns with modern rendering practices through direct integration with JavaScript evaluation and native web capabilities.
HTML directives support binding JavaScript event handlers directly within your markup
using a simple and consistent syntax.
It handle DOM events like click, input, keydown,
mouseenter, and more.
These bindings follow standard JavaScript event behavior but are declared directly in
the template, no need for addEventListener or inline onclick attributes.
Use the format:
placeholderEvent="this.functionCall(...)".
The event name must be lowercase and match a standard DOM event (e.g.
click, input, keydown).
You must append Event to the event name:
clickEvent, inputEvent,
keydownEvent, etc.
The value must be a valid JavaScript function call (with or without arguments).
The $event object can be passed to access native event data.
Inspector View (Console)
$event gives you access to native event details (like target,
key, etc.).onClick, onInput, etc. โ use
clickEvent, inputEvent, etc. instead.if / else if /
else and forEach for dynamic UI behavior.CSS directives provide a way to make component styling more flexible and maintainable by supporting variable substitution and scoped customization. They enable styles to adapt dynamically at runtime without breaking encapsulation.
Template interpolation enables the embedding of JavaScript expressions directly within
CSS content, using the ${this.expression} syntax.
This works similarly to HTML template interpolation, allowing you to bind JavaScript
properties or return values directly into your styles at render time.
Expressions must be valid JavaScript and scoped to the current component
(this.property or this.method()).
Only values that result in a valid CSS value are allowed (colors, units, keywords).
Function calls must return a value and must be pure (no side effects).
You can interpolate in inline styles, style blocks, or custom properties
(e.g., --my-color: $ {this.color}).
Do not use interpolation inside CSS selectors or media queries, only in property values.
Keep expressions simple and readable. Avoid chaining or complex logic inside styles.
Inspector View (Elements)
--theme-accent: $ {this.accentColor}.The framework includes a lightweight Observable utility for managing event-based communication between components or logic blocks. This pattern allows you to decouple parts of your application by subscribing to and publishing named events, similar to a pub/sub model.
subscribe(name, subscriber): Register a function to run when
a named event is published.
unsubscribe(name): Remove a previously registered event
handler.
publish(name, payload): Trigger all subscriber callbacks for
the given event name, passing in an optional payload.
Each event supports a single active subscriber, subsequent calls with the same name will replace previous ones.
The Observable system can be used for reactive data handling, custom event broadcasting, or integrating cross-component behaviors in a clean, manageable way.
HTMLComponent class with Shadow DOM
support.
.js, .html,
.css, and .config.json.
app.config.json.
Dom.init() initialization mechanism.
lazyLoading.
if,
else if, and
else.
forEach template directive for repeated
content.
preProcess() โ
toProcess() โ
atProcess() โ
inProcess().
apendToBody.