EXHL is a specification for extended HTML.
It builds on standard HTML and adds features like variables, conditions, loops, and declarative components to make HTML more expressive and structured.
EXHL is a specification intended for template usage.
It is not a programming language, library, or framework. Each platform or engine can implement it in its own way.
Below is a simplified overview of the specifications.
-
Page (specification)
Definition: A page represents a routable document in the system and is declared using the
<page>element with a requiredpathattribute. Thepathdefines the unique URL or route for the page, must start with/, and may optionally end with/. Each path must be unique across the system.
Pages must always be defined at the root level of the file and cannot be nested inside other elements. A page typically contains a layout or component structure that defines its content. Parameters can be used in the path using context variables, e.g.,/foo/{bar}.<page path="">...</page>
Usage: A page is used by declaring it at the root of a document with a unique
path. Inside a page, layouts or components can be included to structure the content.<page path="/{foobar}"> <h1>Example page - <value name="foobar" /></h1> </page>
<page path="/"> <layout name="base-layout"> <h1>Example page using layout template.</h1> </layout> </page>
-
Layout (specification)
Definition: A layout defines the structural composition of a document or page. It is declared using the
<layout>element with a globally uniquenameand is responsible only for structure, never for behavior or logic. Layouts must always be placed at the root level of the file. Layouts may be applied individually or nested inside other layouts to compose more complex structures.
Each layout must contain exactly one<render />element, which marks the single rendering point where nested layouts or final content are inserted. The<render />element cannot receive parameters and acts purely as a placeholder for child content. Layouts do not accept variables or functions and do not maintain state; their role is solely to define the hierarchical arrangement of components and nested layouts.
Note: When use nested inside other layouts, the nested layout must be existent.<layout name="base-layout">...</layout>
Usage: A layout is used by placing it in the document or inside another layout. When layouts are nested, rendering flows from outer layouts to inner layouts, with each layout rendering its content at its own
<render />point. This ensures a clear and predictable structure: the outermost layout defines the broad skeleton, inner layouts refine sections, and the final content or components are inserted at the deepest<render />.<layout name="base-layout"> <!DOCTYPE html> <head> <title><value name="foobar()"/></title> <meta name="description" :value="foobar" /> </head> <body> <component name="base-header" /> <layout name="base-content-container"> <render /> </layout> <component name="base-footer" /> </body> </layout>
<body> <layout name="base-layout"> <layout name="content-layout"> <render /> </layout> </layout> </body>
-
Component (specification)
Definition: A component is defined using the
componentattribute, whose value serves as a unique name within the engine's global system.Functions and variables are inherited from the rendering context, but they can be overridden by passing parameters to the component. Components must always be placed at the root level of the file. Multiple components per file are allowed, though it is recommended to define one component per file.
Note: Functions and variables is detected automatically by references.
<header component="foobar">...</header>
Usage: A component can be used with the
<component />tag and thenameattribute, which identifies the component globally in the system.Components do not have a body or child elements (no slots). Parameters for functions and variables may be passed, but are optional.
Note: Functions are always references and do not accept parameters. Variables can be literals, references, or the result of a function.
<component name="foobar" />
<component name="foobar" value:foo="foobar()" />
<component name="foobar" value:a="foo bar" value:b="variable" value:c="function()" />
-
Loop (specification)
Definition: Loops are used to iterate over collections, strings, ranges, variables, or function results, producing repeated HTML elements. The loop is evaluated once and generates static HTML output.
A Loop is defined using the
<loop>tag. The<loop>tag must always have a body; it cannot be self-closing. The loop body is where all iterations are executed.The loop use attributes to get context values and to define operation conditions.
- value:in: The source to iterate over. Can be: a literal string (iterates over characters), a variable (array or string), or the result of an iterative function. Functions can be called with parameters if applicable.
- value:as: Defines the name of the loop variable representing the current iteration context. Optional; if omitted, the value is anonymous.
- value:min: Minimum number of iterations required. If not met, the loop result won't be rendered.
- value:max: Maximum number of iterations allowed. When reached, the loop stops.
- value:index: The current iteration index, starting at zero.
- value:even: Indicates if the current index is even; can be used in styling conditions.
- value:odd: Indicates if the current index is odd; can be used in styling conditions.
<loop value:in="range(0,100)" value:as="value">...</loop>
<loop value:in="foobar" value:as="item" value:min="1">...</loop>
<loop value:in="foobar()" value:as="item" value:max="foobar">...</loop>
<loop value:in="hello world" value:as="char" value:min="1" value:max="100" value:index="i" value:even="even" value:odd="odd">...</loop>
Usage: The
<loop>tag is used to iterate over a source and repeat HTML structures. Examples of usage include iterating over numeric ranges, strings, variables, and function results.Note: When "in" attribute is empty or contain null value, the loop will be ignored.
<loop value:in="range(0,100)" value:as="value" value:min="1" value:max="100"> <h1>Number <value name="value" /> (Index: <value name="index" />)</h1> </loop>
<loop value:in="hello world" value:as="char"> <h1>Character: <value name="char" /></h1> </loop>
<loop value:in="items" value:as="item"> <h1><value name="item.name" /></h1> </loop>