Components - PageAmp/pageamp GitHub Wiki
PageAmp supports defining custom tags implemented as server-side macros.
You can define one like this:
<:define tag="my-product">
<div>Name: [[name]]</div>
<div>Price: [[price]]</div>
</:define>
and then use it like this:
<my-product :name="Thingy" :price="2$"/>
which results in this output:
<div>
<div>Name: Thingy</div>
<div>Price: 2$</div>
</div>
The <:define>
tag gives a name to an HTML block. By default the root of the block is a DIV, but you can specify what the define extends, e.g.:
<:define tag="my-code:code">...</:define>
which would generate:
<code>...</code>
Note: custom tag names must include a
-
character. This is done to encourage proper naming. Project-wide tags may start withmy-
orapp-
prefixes, for example. Reusable components should be given names prefixed with a common collection name, as it's done with kits.
A custom tag can declare default values for the attributes it relies on:
<:define tag="my-product" :name="Product" :price="0">
<div>Name: [[name]]</div>
<div>Price: [[price]]</div>
</:define>
this means if you don't specify them where you use the tag:
<my-product/>
the output will have be populated with the defaults:
<div>
<div>Name: Product</div>
<div>Price: 0</div>
</div>
HTML added to a custom tag is appended to its root by default.
For example given this definition:
<:define tag="my-product">
<div>Name: [[name]]</div>
<div>Price: [[price]]</div>
</:define>
you can add custom markup to its contents like this:
<my-product :name="Thingy" :price="2$">
<img src="..."/>
</my-product>
which generates this output:
<div>
<div>Name: Thingy</div>
<div>Price: 2$</div>
<img src="..."/>
</div>
This definition would generate exactly the same output, but it explicitly defines where the the custom tag wants custom markup to be added:
<:define tag="my-product">
<div>Name: [[name]]</div>
<div>Price: [[price]]</div>
<:slot name="default"/>
</:define>
and it does the same as above because custom markup is added to the default slot by... er... default.
When a custom tags defines more than one slot:
<:define tag="my-product">
<div>Name: [[name]]</div>
<div>Price: [[price]]</div>
<div>
<:slot name="details"/>
</div>
<!-- the following tag is reduntant, it would be
implicitly added anyway -->
<:slot name="default"/>
</:define>
you can chose where your custom markup goes using the :plug
attribute:
<my-product :name="Thingy" :price="2$">
<span :plug="details">Thingy does things</span>
<img :plug="default" src="..."/>
</my-product>
Note:
:plug="default"
above is reduntant, custom markup targets the default slot by... uh... default.
which would generate:
<div>
<div>Name: Thingy</div>
<div>Price: 2$</div>
<div>
<span>Thingy does things</span>
<div>
<img src="..."/>
</div>
A custom tag can extend another custom tag.
For example, given this base definition:
<:define tag="my-product">
<div>Name: [[name]]</div>
<div>Price: [[price]]</div>
<div>
<:slot name="details"/>
</div>
</:define>
you can define this specialized version:
<:define tag="my-bundle:my-product">
<span :plug="details">This is a bundle</span>
</:define>
which generates this when used:
<div>
<div>Name: ...</div>
<div>Price: ...</div>
<div>
<span>This is a bundle</span>
</div>
</div>
Custom tags can be defined and used in the context of a project without too much thought: they are mainly used to avoid repeating code and make it more readable by giving meaningful names to blocks of HTML.
When a custom tag proves useful in general, and could be reused in other projects, it's a good idea to turn it into a proper component.
Components are defined in their own module and include their default styling.
For example a module /components/app-product.htm could look like this:
<lib>
<style>
.app-product {
border: 1px solid rgba(0,0,0,.15);
}
.app-product > .name {
font-weight: bold;
}
.app-product > .price {
font-style: italic;
}
</style>
<:define tag="app-product"
class="app-product"
:name="Product"
:price="0">
<div class="name">Name: [[name]]</div>
<div class="price">Price: [[price]]</div>
</:define>
</lib>
Component definitions are meant to be imported into your <head>
tag. For example this page:
<html>
<head>
<:import src="/components/app-product.htm"/>
</head>
<body>
<app-product :name="Thingy" :price="2$"/>
</body>
</html>
would generate this output:
<html>
<head>
<style>
.app-product {
border: 1px solid rgba(0,0,0,.15);
}
.app-product > .name {
font-weight: bold;
}
.app-product > .name {
font-style: italic;
}
</style>
</head>
<body>
<div class="app-product">
<div class="name">Name: Thingy</div>
<div class="price">Price: 2$</div>
</div>
</body>
</html>
Component can be restyled by overriding their CSS:
<html>
<head>
<:import src="/components/app-product.htm"/>
<style>
.app-product {
background: #345;
color: #fff;
border: 0;
}
</style>
</head>
<body>
<app-product :name="Thingy" :price="2$"/>
</body>
</html>
Or, they can provide styling options themselves:
<lib :app_product_fg="#000"
:app_product_bg="#fff"
:app_product_border="1px solid rgba(0,0,0,.15)">
<style>
.app-product {
color: [[app_product_fg]];
background: [[app_product_bg]];
border: [[app_product_border]];
}
.app-product > .name {
font-weight: bold;
}
.app-product > .name {
font-style: italic;
}
</style>
<:define tag="app-product"
class="app-product"
:name="Product"
:price="0">
<div class="name">Name: [[name]]</div>
<div class="price">Price: [[price]]</div>
</:define>
</lib>
Thanks to <:include>
and <:import>
behaviour, the :app_product_*
module attributes are available as <head>
attributes, and can be personalized there:
<html>
<head :app_product_fg="#fff"
:app_product_bg="#345"
:app_product_border="0">
<:import src="/components/app-product.htm"/>
</head>
<body>
<app-product :name="Thingy" :price="2$"/>
</body>
</html>
To recap, components:
- are defined in their own eponymous modules
- include their default CSS classes, prefixed with their name
- provide one or more custom tags, also prefixed with their name.
PageAmp components are the building blocks upon which kits, coherent and visually themable collections of components, are based.