All pages
Powered by GitBook
1 of 73

Components

info.json

Outline basic information about your Element

The info.json file contains important information about a Component. The file uses the JSON format and consists of a series of key and value pairs. It's required for Component to function.

Component Categories

Every Component needs to define a category in which they belong. This tells Elements how to group components in the UI and helps users to locate components. Your Component MUST use one of the following pre-defined categories.

If you think we're missing a category, please visit the forum and let us know.

Group
example

Accessibility

ARIA Lables, Contrast Checkers.

Animation

Hover Effects, Animated SVG's.

CMS

Content Management Systems

Content

Headings, Paragraphs, Lists.

Dynamic

Online Databases, Google Sheets.

Ecommerce

Cart Integration, Product display, Add to Cart Buttons.

Forms

Text Fields, Text Areas, Checkboxes, Buttons.

Interactive

Modals, Popovers, Accordions, Carousels.

Layout

Grid, Flex, Containers, Columns.

Media

Images, Video, Audio Players, Icons, Embeds (YouTube, Vimeo).

Navigation

Menus, Nav Bars, Breadcrumbs, Tabs.

Security

Password Protection, Login Forms.

SEO

Keywords, SEO helpers.

Utility

Cookies, Anchors, Placeholders, Dividers.

Supported Key-Value Pairs

The Elements info.json file supports the following key-value pairs.

Key
Type
Notes

identifier (Required)

string

The identifier is the unique ID for your Element. We recommend using a reverse DNS format. This should be a string consisting of just lowercase characters and periods without spaces.

author (Required)

string

The name of the author of the Element. This would usually be the developer name, or the name of the company publishing the Element.

title (Required)

string

The name of the Element. This will be displayed inside of RapidWeaver. A unique and descriptive name is preferable. Overly long names will get truncated, check inside of the RapidWeaver UI for readability.

group (Required)

string

One of the above categories. Components with the same category are grouped together.

tags

array

A list of tags relevant to the Elements.

helpURL

string

A URL to the location of the help documentation online.

infoURL

string

A URL to the location of the marketing page online.

info.json Example

You can use the following code as a starting point for your own info.json file.

{
  "identifier": "com.realmac.elementpack.helloworld",
  "author": "Realmac Software",
  "title": "Hello World",
  "group": "Utility",
  "tags": [
    "simple",
    "example"
  ]
}

Icons

At a bare minimum you'll want to include PDF images for light mode icons so they can be used for all views where your Component appears within the RapidWeaver Elements UI.

Important: The PDF graphic for your icon should have a transparent background so it can be composited in app on to the background tile to give a consistent look.

Standard Icon

All Elements require an icon.pdf file for light mode. If the dark-mode.pdf file isn't provided, icon.pdf will be used in Dark Mode.

  • icon.pdf (Required), 1:1 ratio, e.g. 128x128 (Square)

  • icon-dark.pdf (optional), 1:1 ratio, e.g. 128x128 (Square)

Palette Icons

The Palette image should be named paletteIcon.pdf and paletteIcon-dark.pdf for Dark Mode. If the paletteIcon-dark.pdf file isn't provided, paletteIcon.pdf will be used in Dark Mode.

  • paletteIcon.pdf (Required), 1:2 ratio, e.g. 128x256 (landscape)

  • paletteIcon-dark.pdf (optional), 1:2 ratio, e.g. 128x256 (landscape)

Asset Placement

All Icon files should be placed at the root of the Component alongside the info.json file.

Finder window showing the icon files inside of a typical Element.

How to add a Custom Icon to your Component

Watch the short video below to learn more about adding custom icons to your Components.

Sketch Example File

Your pdf icons MUST be on a transparent background. The background should not be exported as this is generated in Elements.

Showing background in Sketch (for design purposes only)

We've provided an example Sketch document below you can use to get started.

3MB
Components Icon Template.zip
archive

Experimental / Unsupported - Banner Icons

⚠️ While this feature works, please do not use it as it may be removed in a future build.

There is some unfinished experimental support for banner style icons, these are great for layout style components. However, this style of icon may not be supported when Elements ships, please use with caution and ensure you also include the standard style of icon.

  • bannerLayer1.png

  • bannerLayer1-Dark.png (optional)

Banner icons can be layered, and each layer can represent a different colour in the Theme Studio. For example this icon uses four layers and three of those layers will be tinted with the Theme colour.

  • bannerLayer1.png

  • bannerLayer2-brand-500.png

  • bannerLayer3-brand-200.png

  • bannerLayer4-surface-500.png

Banner images should be 512px in width, but can be as short or tall as you need. Banner icons will appear the same in both grid and list.

Element Library: Banner icons (Pre-Beta Build 22484)

Properties.json

The properties.json file defines the User Interface for an Element. Browse the UI Control docs for a full list of available controls.

Here's an example of setting Default Properites on various UI controls.

{
    "groups": [{
        "title": "Content",
        "properties": [{
            "title": "Text Style",
            "id": "textStyle",
            "themeTextStyle": {
                "default": { "base": { "name": "lg" }, "sm": { "name": "2xl" }, "md": { "name": "4xl" } }
            }
        }, {
            "title": "Theme Font",
            "id": "textFont",
            "themeFont": {
                "default": { "base": { "name": "heading" }, "sm": { "name": "quote" } }
            }
        }, {
            "title": "Padding",
            "id": "themeSpacing",
            "themeSpacing": {
                "mode": "padding",
                "default": {
                    "base": {
                        "custom": true,
                        "left": 10,
                        "right": 20,
                        "top": 30,
                        "bottom": 40,
                        "linkVertical": true
                    },
                    "sm": {
                        "custom": false,
                        "left": "xl",
                        "right": "xl",
                        "top": "xl",
                        "bottom": "xl"
                    }
                }
            }
        }, {
            "title": "Border Width",
            "id": "borderWidth",
            "themeBorderWidth": {
                "default": {
                    "base": {
                        "custom": false,
                        "left": "2",
                        "right": "2",
                        "top": "2",
                        "bottom": "2",
                        "linkHorizontal": true
                    },
                    "sm": {
                        "custom": false,
                        "left": "8",
                        "right": "8",
                        "top": "8",
                        "bottom": "8",
                        "linkHorizontal": true
                    }
                }
            }
        }, {
            "title": "Border Radius",
            "id": "borderRadius",
            "themeBorderRadius": {
                "default": {
                    "base": {
                        "custom": false,
                        "topLeft": "sm",
                        "topRight": "sm",
                        "bottomLeft": "sm",
                        "bottomRight": "sm",
                        "linkHorizontal": true
                    },
                    "sm": {
                        "custom": true,
                        "topLeft": 5,
                        "topRight": 5,
                        "bottomLeft": 5,
                        "bottomRight": 5,
                        "linkHorizontal": true
                    }
                }
            }
        }, {
            "title": "Spacing",
            "id": "spacing",
            "themeSpacing": {
                "default": {
                    "base": {
                        "custom": false,
                        "left": "sm",
                        "right": "sm",
                        "top": "sm",
                        "bottom": "sm",
                        "linkHorizontal": true
                    },
                    "sm": {
                        "custom": false,
                        "left": "lg",
                        "right": "lg",
                        "top": "lg",
                        "bottom": "lg",
                        "linkHorizontal": true
                    }
                }
            }
        }, {
            "title": "Shadow",
            "id": "themeShadow",
            "mode": "shadow",
            "themeShadow": {
                "default": { "base": { "name": "sm" }, "sm": { "name": "lg" }, "md": { "name": "xl" } }
            }
        }, {
            "title": "Slider",
            "id": "slider",
            "slider": {
                "default": "45",
                "min": 20,
                "max": 60,
                "round": true,
                "units": "vw"
            }
        }, {
            "title": "Slider 2",
            "id": "slider2",
            "slider": {
                "default": { "base": 20, "sm": 30 },
                "min": 20,
                "max": 60,
                "round": true,
                "units": "vw"
            }
        }, {
            "title": "Slider 3",
            "id": "slider3",
            "slider": {
                "default": "3",
                "content": [
                    { "title": "a", "value": "2" },
                    { "title": "b", "value": "3" },
                    { "title": "c", "value": "4" }
                ]
            }
        }, {
            "title": "Alignment",
            "id": "alignment",
            "segmented": {
                "default": {
                    "base": "w-auto",
                    "md": "w-full"
                },
                "items": [
                    { "value": "w-full", "icon": "text.alignleft" },
                    { "value": "w-asdf", "icon": "text.alignleft" },
                    { "value": "w-auto", "icon": "text.aligncenter" },
                    { "value": "custom", "icon": "text.alignright", "title": "Custom" }
                ]
            }
        }]
    }]
}

Grouping Controls

Groups allow you to organise similar settings into relevant groups.

the Icon property uses SF Symbols. Please refer to the SF Symbol app for a list of available icons.

Here is an example emtpy group:

{
	"groups": [
			{
	  "title": "Colour Settings",
	  "icon": "paintpalette",
	  "properties": [
		  
		]
	}]
}

Group Icon

You can specify an icon to appear next to the title of your group. The Icon property uses SF Symbols. Please refer to the SF Symbol app for a list of available icons.

Group Controls

Controls should placed inside of the properties array. The following example has a heading and an image drop well inside of the group.

{
    "groups": [
        {
            "title": "Button Design",
            "icon": "paintpalette",
            "properties": [
                {
                    "title": "Title",
                    "id": "buttonTitle",
                    "default": "",
                    "text": {
                       "default": "Click Me"
                    }
                },
                {
                    "title": "Image",
                    "id": "buttonImage",
                    "image": {}
                },
                
            ]
        }
    ]
}

The following example has two groups, the first with a text box, and the second with an image dropwell.

{
    "groups": [{
        "title" : "First Group",
        "icon" : "1.circle.fill",
        "properties" : [{
            "title": "Title",
            "id": "title",
            "text": {}
        }]
    },
    {
        "title" : "Second Group",
        "icon" : "2.circle.fill",
        "properties" : [{
            "title": "Image",
            "id": "image",
            "image": {}
        }]
    } 
    ]
}

Default Values

General Structure

All properties have the same general structure using the following object keys.

Key
Value
Notes

title

string

See Title for more information

id

string

See ID for more information

format

string

See Format for more information

visible

string

See Visible for more information

enabled

string

See Enabled for more information

responsive

boolean

See Responsive for more information

Code Example

{
  "title": "Example Title",
  "id": "unique-id-here",
  "format": "pt-{{value}}",
  "visible": "myOtherControl == 'someValue'",
  "enabled": "anotherControl == 'anotherValue'",
  "responsive": false
}

Title

ID

Format

In some cases, it's adventagous to format a value before it's passed to your element's templates. To achieve this, add a format property to the configuration specifying the format string.

Value formatting is support by all controls.

Basic Example

For example, the following creates a slider ranging from 0-12 with a property called padding

{
    "title" : "Padding",
    "id" : "padding",
    "format": "pt-{{value}}",  
    "slider": {
        "default" : 5,
        "min" : 0,
        "max" : 12
    }
}

If the slider is set to 5 and the template contains

{{padding}}

the output will be

pt-5

Arbitrary Value Example

You could also format the control's value to use tailwind's arbitrary value feature. This would allow your element precis control over all css properties. For example, you can control the opacity utility class from tailwind with a slider:

{
    "title" : "Opacity",
    "id" : "opacity",
    "format": "opacity-[{{value}}%]",  
    "slider": {
        "default" : 50,
        "min" : 0,
        "max" : 100
    }
}

If the slider is set to 50 and the template contains

{{opacity}}

the output will be

opacity-[50%]

Advanced CSS Example

You could also format the control's value to output a valid CSS property. For example, if we want to control the translateX css property with a number input, we can do the following:

{
    "title" : "Translate X",
    "id" : "translateX",
    "format": "transform: translateX({{value}}px);",  
    "number": {
        "default" : 50,
    }
}

If the number field is set to 50 and the template contains

{{translateX}}

the output will be

transform: translateX(50px);

Of cause, in this case you would want to add this to a CSS template file.

CSS Custom Property Example

You can even use the format to control a CSS Custom Property (css variable). If we take our previous example and expand on it, we might want to instead set a --translateX CSS custom property with a number input:

{
    "title" : "Translate X",
    "id" : "translateX",
    "format": "--translateX: {{value}}px;",  
    "number": {
        "default" : 50,
    }
}

If the number field is set to 50 and the template contains

{{translateX}}

the output will be

--translateX: 50px;

In this case you would either want to add this to a CSS file, or use an inline style attribute to set the --translateX:

// first you would use the CSS custom property in your CSS:
<style>
.myDiv {
  ...
  transform: scale(0.5) translateX(var(--translateX, 0));
}
</style>

// now that your class is setup to use the --translateX custom property
// you can reassign it locally:
<div class="myDiv" style="{{translateX}}"> ... </div>

// this would output:
<div class="myDiv" style="--translateX: 50px;"> ... </div>

Visible

The visible key in an object's properties can be set using a logical expression that evaluates to true or false. This determines whether a specific UI element is shown or hidden based on the conditions specified in the expression. Works in the same way as enabled.

  • Boolean Logic: Use logical operators (&&, ||) to combine multiple conditions.

  • Comparison Operators: Use ==, !=, >, <, >=, <= to compare values.

  • Regex Match: Use matches to perform a regex comparison.

Examples

  1. Complex Condition

    "visible": "(backgroundType == 'custom' || backgroundType == 'image') && textColor == 'white'"

    This makes the property visible only if the backgroundType is either custom' or 'image', and the textColor is 'white'.

  2. Visibility Based on Numeric Ranges

    "visible": "opacity > 20 && opacity < 30"

    Useful for showing elements that should only be visible within a specific range, e.g., showing a message if the opacity is not between 20-30.

  3. Negation to Hide Elements

    "visible": "mySwitchControl != true"

    The property is visible when mySwitchControl is false.

  4. Match a regex

    "visible": "name matches /rapid|weaver|elements/"

    The property is visible when name contains rapid or weaver or elements.

The following example code toggles the visibility of controls based on the value of the switch.

{
    "groups": [{
        "title": "SWITCH TEST",
        "icon": "switch.2",
        "properties": [{
            "title": "Test Switch",
            "id": "testSwitch",
            "responsive": false,
            "switch": {
                "default": false
            }
        }, {
            "information": {},
            "title": "Let's Slide…",
            "visible": "testSwitch == true"
        }, {
            "title": "slider",
            "id": "slider",
            "visible": "testSwitch != false",
            "slider": {
                "default": 5,
                "min": 1,
                "max": 50,
                "round": true
            }
        }]
    }, {
        "title": "MATCHES",
        "icon": "equal",
        "properties": [{
            "title": "Name",
            "id": "name",
            "responsive": false,
            "input": {}
        }, {
            "information": {},
            "title": "Name contains rapid or weaver or elements",
            "visible": "name matches /rapid|weaver|elements/"
        }]
    }]
}

Enabled

Show and hide controls based on another control's value.

The enabled key in an object's properties can be set using a logical expression that evaluates to true or false. This determines whether a specific UI element is enabled or disabled based on the conditions specified in the expression. Works in the same way as visible.

  • Boolean Logic: Use logical operators (&&, ||) to combine multiple conditions.

  • Comparison Operators: Use ==, !=, >, <, >=, <= to compare values.

Examples

  1. Complex Condition

    "enabled": "(backgroundType == 'custom' || backgroundType == 'image') && textColor == 'white'"

    This makes the property enabled only if the backgroundType is either custom' or 'image', and the textColor is 'white'.

  2. Visibility Based on Numeric Ranges

    "enabled": "opacity > 20 && opacity < 30"

    Useful for enabling controls that should only be visible within a specific range.

  3. Negation to Hide Elements

    "enabled": "mySwitchControl != true"

    The property is enabled when mySwitchControl is false.

Responsive

Responsive Values

By default all properties in Elements are responsive, allowing users to override setting for different device breakpoints. Elements accomplishes this by prefixing Tailwind’s responsive modifiers to the control’s value. Let’s explore how this works in an example.

Responsive Example

In this example, a select control uses Tailwind’s text transform utility classes. The user can customize the control’s value across device breakpoints by clicking the responsive icon (dot) to the left of the control title in Elements.

Initially, the headingTextTransform property output is set to normal-case. However, once the user customizes this setting in Elements, it may look something like this: normal-case sm:uppercase md:lowercase lg:capitalize.

Elements automatically applies and manages these responsive modifiers, saving you the effort of manually setting values for each device size and applying the appropriate modifier.

{
    "title": "Case",
    "id": "headingTextTransform",
    "select": {
        "default": "normal-case",
        "items": [
            {
                "value": "normal-case",
                "title": "None"
            },
            {
                "value": "uppercase",
                "title": "Uppercase"
            },
            {
                "value": "lowercase",
                "title": "Lowercase"
            },
            {
                "value": "capitalize",
                "title": "Capitalize"
            }
        ]
    }
}

Multi Value Responsive

This even works for controls that require multiple classes as the value. So given the following control:

{
  "title": "Position",
  "id": "position",
   "select": {
     "default": "bottom-0 left-0",
     "items": [
       {
         "title": "Top Right",
         "value": "top-0 left-auto right-0"
       },
       {
         "title": "Bottom Left",
         "value": "bottom-0 left-0 right-auto"
       }
     ]
   }
}

You can expect the output of position to be bottom-0 left-0 right-auto md:top-0 md:left-auto md: right-0

Disable Responsive Controls

To disable responsive values for your control, simply add "responsive": false to your control. This allows you to use the raw value from a control.

{
    "title": "Not Responsive",
    "id": "nonResponsiveControl",
    "responsive": false,
    "switch": {}
}

UI Controls

All UI Controls need to be placed inside of a Group. The controls appear in the inspector.

Available Controls

DividerSelectHeadingImageInformationLinkNumberResourceSegmentedSliderSwitchTextText AreaTheme Border WidthTheme Border RadiusTheme ColorTheme FontTheme SpacingTheme ShadowTheme Text Style

Divider

Display a divider between content.

{
    "divider": {}
}
{
    "groups": [{
        "title": "Divider Example",
        "properties": [{
            "divider": {}
        }]
    }]
}

Heading

Displays a heading in the inspector interface.

{
    "title": "Border",
    "heading": {}
}
{
    "groups": [{
        "title": "Heading Example",
        "properties": [{
            "title": "Border",
            "heading" : {}
        }]
    }]
}

Image

Displays an image dropwell in the inspector.

{
    "title": "Image",
    "id": "image",
    "image": {}
}
{
    "groups": [{
        "title": "Image Resource Example",
        "properties": [{
            "title": "Image",
            "id": "image",
            "image": {}
        }]
    }]
}

Information

Display some informational static text.

{
    "information": {},
    "title": "You can display a helpful tip here!"
}
{
    "groups": [{
        "title": "Information Example",
        "properties": [{
            "information": {},
            "title": "You can display a helpful tip here!"
        }]
    }]
}

Link

The Link control allows users to input a URL or link to another page within their RapidWeaver project.

{
    "title": "Link",
    "id": "link",
    "link": {
        "subtitle": "Link to another page"
    }
}
{
    "groups": [{
        "title": "Link Example",
        "properties": [{
            "title": "Link",
            "id": "link",
            "link": {
                "subtitle": "Link to another page"
            }
        }]
    }]
}

Template Example

The following will give you access to the link object in your template file(s).

<a href="{{link.href}}" target="{{link.target}}">Click Me</a>

Getting the Absolute URL

By default the link attribute will return a relative URL. To get the full URL add the absoluteURL property.

        {
          "title": "Link",
          "id": "globalLink",
          "link": {
            "absoluteURL": true
          }
        }

Corresponding template example using the absoluteURL.

<a href="{{redirectURL.href}}">Click Me</a>

Number

Displays a number field with a stepper.

{
    "title": "Number",
    "id": "myNumber",
    "number": {
        "default": 100,
        "subtitle": "a simple number input"
    }
}
{
    "groups": [{
        "title": "Number Example",
        "properties": [{
            "title": "Number",
            "id": "myNumber",
            "number" : {
                "default" : 100,
                "subtitle" : "a simple number input",
            }
        }]
    }]
}

Resource

Displays a resource dropwell that accepts all file types.

{
    "title": "Resource",
    "id": "resource",
    "resource": {}
}
{
    "groups": [{
        "title": "Resource Example",
        "icon": "folder",
        "properties": [{
            "title": "Resource",
            "id": "resource",
            "resource": {}
        }]
    }]
}

Restricting File Types

You can restrict the file types allowed in a dropwell, by using the accepts property.

"resource": {
    "accepts": "svg"
}

Support for multiple file types can be added using comma seperated values.

"resource": {
    "accepts": "zip, pdf"
}

Segmented

Display a segmented control.

{
    "title": "Alignment",
    "id": "alignment",
    "segmented": {
        "default": {
            "base": "align-left",
            "md": "align-center"
        },
        "items": [{
            "value": "align-left",
            "icon": "text.alignleft"
        }, {
            "value": "align-center",
            "icon": "text.aligncenter",
            "default": true
        }, {
            "value": "align-right",
            "icon": "text.alignright"
        }]  
    }
}
{
    "groups": [{
        "title": "Segmented Example",
        "properties": [{
            "title": "Alignment",
            "id": "alignment",
            "segmented": {
                "default": {
                    "base": "align-left",
                    "md": "align-center"
                },
                "items": [{
                    "value": "align-left",
                    "icon": "text.alignleft"
                }, {
                    "value": "align-center",
                    "icon": "text.aligncenter",
                    "default": true
                }, {
                    "value": "align-right",
                    "icon": "text.alignright"
                }]  
            }
        }]
    }]
}

You can also use the following title key to display text instead of icons.

{
    "groups": [{
        "title": "Segmented Example",
        "properties": [{
            "title": "Alignment",
            "id": "alignment",
            "segmented": {
                "items": [{
                    "value": "align-left",
                    "title": "Left"
                }, {
                    "value": "align-center",
                    "title": "Center",
                    "default": true
                }, {
                    "value": "align-right",
                    "title": "Right"
                }]  
            }
        }]
    }]
}

Select

Supports responsive values

Displays a select box, also known as a dropdown menu. A select box can be responsive or non-responsive and has two or more items to choose from.

Property Schema

Key
Value
Notes

title

string

See Title for more information

id

string

See ID for more information

format

string

See Format for more information

visible

string

See Visible for more information

enabled

string

See Enabled for more information

responsive

boolean

See Responsive for more information, default: true

select

object

See Select Schema below

Select Schema

Key
Value
Notes

default

string

Value of the default item to be selected

items

array

See Item Schema below

Item Schema

Key
Value
Notes

title

string

The item title to be displayed in the dropdown

value

string

The value to be returned

Return Value

The returned value is dependent on the responsive setting and either returns a string of tailwind classes or the raw value.

Responsive
Return Value

true

string of tailwind classes

false

raw string value

Example

Below is an example of a typical responsive dropdown that returns one or more tailwind text transform classes, one for each device breakpoint.

{
    "title": "Case",
    "id": "headingTextTransform",
    "select": {
        "default": "normal-case",
        "items": [{
            "value": "normal-case",
            "title": "None"
        }, {
            "value": "uppercase",
            "title": "Uppercase"
        }, {
            "value": "lowercase",
            "title": "Lowercase"
        }, {
            "value": "capitalize",
            "title": "Capitalize"
        }]
    }
}
{
    "groups": [{
        "title": "Select Example",
        "properties": [{
            "title": "Case",
            "id": "headingTextTransform",
            "select": {
                "default": "normal-case",
                "items": [{
                    "value": "normal-case",
                    "title": "None"
                }, {
                    "value": "uppercase",
                    "title": "Uppercase"
                }, {
                    "value": "lowercase",
                    "title": "Lowercase"
                }, {
                    "value": "capitalize",
                    "title": "Capitalize"
                }]
            }
        }]
    }]
}

Slider

A slider can be used for choosing from a range of values.

{
    "title": "Contrast",
    "id": "contrast",
    "slider": {
        "default": "contrast-70",
        "items": [
            { "value": "contrast-0", "title": "0" },
            { "value": "contrast-50", "title": "50" },
            { "value": "contrast-70", "title": "75" },
            { "value": "contrast-100", "title": "100" },
            { "value": "contrast-125", "title": "125" },
            { "value": "contrast-150", "title": "150" },
            { "value": "contrast-200", "title": "200" }
        ]
    }
}

The following example displays a stepped slider with seven pre-defined values.

{
    "groups": [{
        "title": "Slider Example",
        "properties": [{
            "title": "Contrast",
            "id": "contrast",
            "slider": {
                "default": "contrast-70",
                "items": [
                    { "value": "contrast-0", "title": "0" },
                    { "value": "contrast-50", "title": "50" },
                    { "value": "contrast-70", "title": "75" },
                    { "value": "contrast-100", "title": "100" },
                    { "value": "contrast-125", "title": "125" },
                    { "value": "contrast-150", "title": "150" },
                    { "value": "contrast-200", "title": "200" }
                ]
            }
        }]
    }]
}

The following example displays a stepped slider with seven pre-defined values.

{
    "groups": [{
        "title": "Slider Example",
        "properties": [{
            "title": "Contrast",
            "id": "contrast",
            "slider": {
                "default": "contrast-70",
                "items": [
                    { "value": "contrast-0", "title": "0" },
                    { "value": "contrast-50", "title": "50" },
                    { "value": "contrast-70", "title": "75" },
                    { "value": "contrast-100", "title": "100" },
                    { "value": "contrast-125", "title": "125" },
                    { "value": "contrast-150", "title": "150" },
                    { "value": "contrast-200", "title": "200" }
                ]
            }
        }]
    }]
}

The following example has a minimum value of 0 and maximum of 100. The units are set to round, and display in the UI with a "%" after the number.

{
    "groups": [{
        "title": "Slider Example",
        "properties": [{
            "title": "Contrast",
            "id": "contrast",
            "format": "{{value}}%",
            "slider": {
                "default": 50,
                "min": 0,
                "max": 100,
                "units": "%",
                "round": true
            }
        }]
    }]
}

An example slider setting a star rating between 0 and 5.

{
    "groups": [{
        "title": "Slider Example",
        "properties": [{
            "title": "Rating",
            "id": "rating",
            "slider": {
                "default": 3,
                "min": 0,
                "max": 5,
                "round": true,
                "units": "Stars",
                "ticks": 6
            }
        }]
    }]
}

Here's an example of an items array in sliders so you can set custom values for each point in the slider.

{
    "groups": [{
        "title": "Slider Example",
        "properties": [{
            "title": "Z Index",
            "id": "zIndex",
            "format": "z-{{value}}",
            "slider": {
                "default": "0",
                "items": [{
                    "value": "0",
                    "title": "0"
                }, {
                    "value": "10",
                    "title": "10"
                }, {
                    "value": "20",
                    "title": "20"
                }, {
                    "value": "30",
                    "title": "30"
                }, {
                    "value": "40",
                    "title": "40"
                }, {
                    "value": "50",
                    "title": "50"
                }]
            }
        }]
    }]
}

Supported Options

The slider control supports the following options.

Key
Type
Notes

title

string

The name of the Slider. This will be displayed beside the UI element of RapidWeaver

id

string

The id for this control.

format

string

Can be used to apply additional formatting to the value. {{value}} will be replaced with the selected value. See value formatting for more information.

default

string

The default value the slider should be set to.

items

array

Can be used to specify an array of values to the slider. See code example below.

min

number

The minimum value for the slider.

max

number

The maximum value for the slider.

units

string

The units string appears alongside the slider value in the user interface, but it is not included in the template output value.

round

boolean

If true an integer will be used instead of a floating point.

ticks

number

the number of ticks that will appear beneath the slider.

snap

boolean

is set to true, slider will snap to ticks.

Switch

Displays a switch, similar to a checkbox in functionality.

{
    "title": "Visible",
    "id": "display",
    "responsive": false,
    "switch": {
        "default": true
    }
}
{
    "groups": [{
        "title": "Switch Example",
        "properties": [{
            "title": "Visible",
            "id": "display",
            "responsive": false,
            "switch": {
                "default": true
            }
        }]
    }]
}

The following examples returns a true or false value. Note that responsive should be false.

{
    "groups": [{
        "title": "Switch Example",
        "properties": [{
            "title": "Visible",
            "id": "display",
            "responsive": false,
            "switch": {
                "default": true
            }
        }]
    }]
}

The following example returns a string value instead of true or false. Note that responsive should be false.

{
    "groups": [{
        "title": "Switch Example",
        "properties": [{
            "title": "Prices Inc VAT",
            "id": "includeVATMessage",
            "responsive": false,
            "switch": {
                "trueValue": "Price includes VAT",
                "falseValue": "Price does not include VAT",
                "default": true
            }
        }]
    }]
}

To display this value in a template file, use {{display}}.

Responsive

When using a switch responsively, trueValue and falseValue should be set to a Tailwind class name. You'll then receive a list of Tailwind classes within your template, prefixed for each breakpoint as necessary.

{
    "groups": [{
        "title": "Responsive Switch Example",
        "properties": [{
            "title": "Show Detail",
            "id": "showDetail",
            "responsive": true,
            "switch": {
                "trueValue": "block",
                "falseValue": "hidden",
                "default": {
                    "base": false,
                    "md": true,
                }
            }
        }]
    }]
}

Template

In the Template file you can do the following to display different html based on the value.

@if(display)
    <div class="p-sm text-lg text-center text-primary-500">I'm True!</div>
@else
    <div class="p-sm text-lg text-center text-primary-500">I'm False!</div>
@endif

Text

Displays a text field in the inspector.

{
    "title": "Text Field",
    "id": "MyTextField",
    "text": {
    "default": "Hello World",
        "subtitle": "one"
    }
}
{
    "groups": [{
        "title": "Text Field Example",
        "properties": [{
            "title": "Text Field",
            "id": "MyTextField",
            "text": {
                "default" : "Hello World",
                "subtitle" : "one",
            }
        }]
    }]
}

Text Area

Displays a multiline text area in the inspector.

{
    "title": "TextArea",
    "id": "myTextArea",
    "textArea": {
        "default": "Hello World",
        "subtitle": "a multi-line text input"
    }
}
{
    "groups": [{
        "title": "TextArea Example",
        "properties": [{
            "title": "TextArea",
            "id": "myTextArea",
            "textArea": {
                "default": "Hello World",
                "subtitle": "a multi-line text input"
            }
        }]
    }]
}

Theme Border Width

Displays the Theme Studio Border Width control.

{
    "title": "Border",
    "id": "borderWidth",
    "themeBorderWidth": {
        "default": {
            "base": {
                "top": "2",
                "right": "2",
                "bottom": "2",
                "left": "2"
            }
        }
    }
}
{
    "groups": [{
        "title": "Theme Border Example",
        "properties": [{
            "title": "Border",
            "id": "borderWidth",
            "themeBorderWidth": {
                "default": {
                    "base": {
                        "top": "2",
                        "right": "2",
                        "bottom": "2",
                        "left": "2"
                    }
                }
            }
        }]
    }]
}

Theme Border Radius

Displays the Theme Studio Border Radius control.

{
  "title": "Radius",
  "id": "borderRadius",
  "themeBorderRadius": {
    "default": {
      "base": {
        "topRight": "lg",
        "topLeft": "lg",
        "bottomRight": "lg",
        "bottomLeft": "lg"
      }
    }
  }
}
{
    "groups": [{
        "title": "Theme Border Radius Example",
        "properties": [{
            "title": "Radius",
            "id": "borderRadius",
            "themeBorderRadius": {
                "default": {
                    "base": {
                        "topRight": "lg",
                        "topLeft": "lg",
                        "bottomRight": "lg",
                        "bottomLeft": "lg"
                    }
                }
            }
        }]
    }]
}

Theme Color

Displays the Theme Studio Color control.

{
    "title": "Color",
    "id": "customColor",
    "format": "text-{{value}}",
    "themeColor": {
        "default": {
            "name": "blue",
            "brightness": 600
        }
    }
}
{
    "groups": [{
        "title": "Theme Color Example",
        "properties": [{
            "title": "Color",
            "id": "customColor",
            "format": "text-{{value}}",
            "themeColor": {
                "default": {
                    "name": "blue",
                    "brightness": 600
                }
            }
        }]
    }]
}

For consistency and integration with the Theme Studio in RapidWeaver Elements, use the themeColor attribute to specify all color options for your elements. This ensures that the color settings are centrally managed and adaptable to theme changes.

Usage of themeColor:

  • default: Sets the initial or fallback color and brightness. In this case, the default color is black with a brightness value of 300.

Output Example: The format key controls the output format of the customColor property. In your template files, referencing {{customColor}} with the example configuration above would output: text-black-300.

Setting Light and Dark mode colours

"themeColor": {
  "default": {
    "name": "surface",
    "brightness": 800,
    "darkName": "surface",
    "darkBrightness": 50
  }
}

Theme Font

Displays the Theme Studio Font control.

{
    "title": "Theme Font",
    "id": "fontFamily",
    "themeFont": {
        "default": {
            "base": { "name": "body" },
            "sm": { "name": "heading" },
            "md": { "name": "quote" }
        }
    }
}
{
    "groups": [{
        "title": "Theme Font Example",
        "properties": [{
            "title": "Theme Font",
            "id": "fontFamily",
            "themeFont": {
                "default": {
                    "base": { "name": "body" },
                    "sm": { "name": "heading" },
                    "md": { "name": "quote" }
                }
            }
        }]
    }]
}

For effective theme integration in RapidWeaver Elements, it is recommended to use the themeFont attribute to define all font family settings for your elements. This approach ensures that font preferences are coordinated with the overall theme settings, providing a consistent user experience.

Here’s a structured example for defining a font property:

Output Example: In your template files, referencing {{fontFamily}} with the example configuration above would output: font-sub-heading sm:font-heading md:font-quote.

Theme Spacing

Displays the Theme Studio Spacing control.

{
    "title": "Padding",
    "id": "themeSpacing",
    "themeSpacing": {
        "mode": "padding",
        "default": {
            "base": {
                "left": "sm",
                "right": "sm",
                "top": "sm",
                "bottom": "sm"
            },
            "md": {
                "left": "md",
                "right": "md",
                "top": "md",
                "bottom": "md"
            }
        }
    }
}
{
    "groups": [{
        "title": "Theme Spacing Example",
        "properties": [{
            "title": "Padding",
            "id": "themeSpacing",
            "themeSpacing": {
                "mode": "padding",
                "default": {
                    "base": {
                        "left": "sm",
                        "right": "sm",
                        "top": "sm",
                        "bottom": "sm"
                    },
                    "md": {
                        "left": "md",
                        "right": "md",
                        "top": "md",
                        "bottom": "md"
                    }
                }
            }
        }]
    }]
}

The themeSpacing attribute is should be used for all space settings like padding, margins, gaps, translate, and positioning.

This control allows the user's site to dynamically adapt to any theme updates, changes, or overrides to the spacing scale.

The themeSpacing control has the following modes:

  • padding

  • margin

  • gap

  • transition

  • position

  • single

Each one of these modes correspond to the appropriate Tailwind utility classes. This means both developers and users can manage one single spacing scale that help promote consistency across all sites built in RapidWeaver Elements. See the examples below for more information.

{
    "groups": [{
        "title": "Theme Spacing Example",
        "properties": [{
            "title": "Padding",
            "id": "themeSpacing",
            "themeSpacing": {
                "mode": "padding",
                "default": {
                    "base": {
                        "left": "sm",
                        "right": "sm",
                        "top": "sm",
                        "bottom": "sm"
                    },
                    "md": {
                        "left": "md",
                        "right": "md",
                        "top": "md",
                        "bottom": "md"
                    }
                }
            }
        }]
    }]
}

Single Mode

The single mode allows you to access the spacing scale from the Theme Studio in a single select control. This is handy when you need to set a single property rather than all four properties (top, right, bottom left). For example, if you need to set only the top css property you can do so like this:

{
    "title": "Top",
    "id": "top",
    "format": "top-{{value}}",
    "themeSpacing": {
        "mode": "single",
        "default": {
            "base": {
                "value": "2"
            }
        }
    }
}

In this example, the default value will be returned as top-2.

Custom Values

You can also specify that a custom value should be used by default for the themeSpacing control. This allows you to gain precise control over the value of the spacing when your element is dropped on to a page.

Linking Values

Theme Shadow

Displays the Theme Studio Shadow control.

{
    "title": "Shadow",
    "id": "boxShadow",
    "themeShadow": {
        "default": {
            "name": "none"
        }
    }
}
{
    "groups": [{
        "title": "Theme Shadow Example",
        "properties": [{
            "title": "Shadow",
            "id": "boxShadow",
            "themeShadow": {
                "default": {
                    "name": "none"
                }
            }
        }]
    }]
}

Theme Text Style

Displays the Theme Studio Text Style control.

{
    "title": "Text Style",
    "id": "headingTextStyles",
    "themeTextStyle": {
        "default": {
            "base": {
                "name": "3xl"
            }
        }
    }
}
{
    "groups": [{
        "title": "Theme Text Style Example",
        "properties": [{
            "title": "Text Style",
            "id": "headingTextStyles",
            "themeTextStyle": {
                "default": {
                    "base": {
                        "name": "3xl"
                    }
                }
            }
        }]
    }]
}

Theme Typography

Displays the Theme Studio Typography class picker.

 {
          "title": "Typography",
          "id": "typography",
          "themeTypography": {
            "default": {
              "base": {
                "name": "body"
              },
              "md": {
                "name": "heading"
              }
            }
          }
        },
{
    "groups": [{
        "title": "Theme Text Style Example",
        "properties": [
         {
          "title": "Typography",
          "id": "typography",
          "themeTypography": {
            "default": {
              "base": {
                "name": "body"
              },
              "md": {
                "name": "heading"
              }
            }
          }
        },
        ]
    }]
}

Templates

Component Template Folder

Template files in RapidWeaver Elements house all the HTML, CSS, JavaScript, and other assets your component needs to work seamlessly. These files are dynamically processed by Elements, allowing for property replacements (think handlebars-style), perform iterations, and more—enabling you to build highly flexible and reusable components.

Templates Directory Structure

The structure of the templates directory is important and denotes in which area the contained files will be inserted into the page. Any files placed at the root of the templates directory will be processed and added for each instance of the component on the page.

The templates directory may also contain the following sub directories

  • headStart - placed at the top of head

  • headEnd - placed at the end of head

  • bodyStart - placed at the top of body

  • bodyEnd - placed at the bottom of body

  • include - files can be included from other template files using an @include statement

Supported Template File Types

  • html - contains HTML content

  • js - contains javascript

  • css - CSS styles

  • php - files containing PHP

Portal

The portal feature allows you to transport sections of your template code to another part of the page.

@portal(pageStart)
<!-- Code here will be transported to the top of the page before the open HTML tag.-->
@endportal
@portal(pageEnd)
<!-- Code here will be transported to the bottom of the page after the end html tag.-->
@endportal
@portal(headStart)
<!-- Code here will be transported to the top of the page head.-->
@endportal
@portal(headEnd)
<!-- Code here will be transported to the bottom of the page head.-->
@endportal
@portal(bodyStart)
<!-- Code here will be transported to the top of the page body.-->
@endportal
@portal(bodyEnd)
<!-- Code here will be transported to the bottom of the page body.-->
@endportal

If you are linking and including scripts, you'll want to tell Elements to only include the link once (when multiple instances of the same component are on the page). To do that you can use the "includeOne argument.

@portal(headEnd, includeOnce: true)
<!-- Code will only be included once when using multiple instances of the same component.-->
@endportal

Include once across multiple components.

@portal(headEnd, id: "com.realmacsoftware.alpine", includeOnce: true)
<!-- Code will only be included once when using the same ID across multiple compoenents.-->
@endportal

Backend

Deploy extra files to the backend.

Sub-directories are not supported in the backend directory. Instead, use the shared assets directory for the bulk of your code and reference it from backend scripts, passing in the values from properties. See more

Files added to the backend directory are processed in the same context as other template files. However, instead of forming part of the page, they will be deployed as extra files to the page's backend directory during publish.

To ensure there are no conflicts with other components, all backend files are stored in a subdirectory corresponding to the current node id. For example:

contact
    index.html
    backend
        rw29BB9A86_0D4A_4E46_9BF5_CD5041A9ECE2
            submit.php

The following file types are supported:

  • html

  • php

  • js

  • css

Example Usage

A good use-case for this would be to process a contact form. Create a new component and add the following code to the templates/backend/submit.phpfile.

<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Sanitize input data
    $name = htmlspecialchars(strip_tags(trim($_POST["name"])));
    $email = filter_var(trim($_POST["email"]), FILTER_SANITIZE_EMAIL);
    $message = htmlspecialchars(strip_tags(trim($_POST["message"])));
    
    // Validation
    if (empty($name) || empty($email) || empty($message)) {
        echo "All fields are required.";
        exit;
    }
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        echo "Invalid email format.";
        exit;
    }
    
    
    // Process data (e.g., send email, save to database)
    
    
    echo "Thank you, $name. Your message has been received.";
}
?>

Then place this in the templates/form.html frontend file.

<script>
    function submitForm(event) {
        event.preventDefault();
        let formData = new FormData(document.getElementById("contactForm"));
        
        fetch("{{node.backendPath}}/process.php", {
            method: "POST",
            body: formData
        })
        .then(response => response.text())
        .then(data => {
            document.getElementById("messageDisplay").innerText = data;
        })
        .catch(error => {
            document.getElementById("messageDisplay").innerText = "An error occurred.";
        });
    }
</script>

<form id="contactForm" onsubmit="submitForm(event)">
    <label for="name">Name:</label>
    <input type="text" id="name" name="name" required>
    <br>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" required>
    <br>
    <label for="message">Message:</label>
    <textarea id="message" name="message" required></textarea>
    <br>
    <button type="submit">Submit</button>
</form>
<p id="messageDisplay"></p>

When the submit button is pressed in the above form, the form sends all the form values to the {{node.backendPath}}/submit.php file on the server. The node.backendPath property will be replaced with the node's unique id.

To use the node.backendPath property, we'll need to use the hooks.js file. Create a hooks.js file in your component and add this.

const transformHook = (rw) => {
    rw.setProps({
        node: rw.node
    });
};

exports.transformHook = transformHook;

Subdirectories are not supported

Elements monitors every file in the backend directory for changes. This can cause problems when adding large php libraries with hundreds of files. A better solution is to add the php library to the Element pack's shared assets directory.

The assets are deployed only once after a component from the pack is added to the page. Use the hooks file to find the site assets path and pass it to the backend file.

const transformHook = (rw) => {
    rw.setProps({
        siteAssetPath: rw.component.siteAssetPath,
        node: rw.node
    });
};

exports.transformHook = transformHook;

We've found it good practice to keep the backend php files to a minimum and use them to build a minimal config object using the properties from the component. Then call a method included from a php file within site assets, passing in the config object.

Conditional Statements

Display content based on dynamic conditions.

Conditional statements in Elements allow you to control when content is displayed based on certain conditions. Using @if, @else if, and @else, you can define different outcomes depending on the values of variables. This is useful for showing or hiding content based on user settings, mode (edit or preview), or other dynamic conditions.

When performing complex logic like string comparisons, use the hooks file to perform this logic and provide simple bool values to the template. This helps to keep templates simple and focused on HTML rather than complex conditional logic!

If Statement

The following example displays the text inside of the IF statment when the switch is on/true. It's worth noting that only non-responsive controls can be used in 'if' statements.

@if(switch)
    <li><b>This is only visible when switch is true</b></li>
@endif

If Not Statement

Elements lets you use the ! symbol to check if something is false. For example, @if(!edit) means ‘if edit mode is not active.’ This is useful when you only want to show something when a condition is not true. The ! can be used with any true or false value.

@if(!edit)
    We're in preview mode or the site is Published.
@endif

Else If Statements

Elements allows you to specify a number of else if statements to run before falling back to the else block. An @elseif statement works just like a regular @if statement and follows the same syntax.

@if(edit)   
    We’re in edit mode   
@elseif(preview)   
    And now in preview   
@elseif(checkbox)   
    The checkbox is ticked   
@else
    Otherwise do this   
@endif

Conditional Statement for Edit/Preview Modes

Elements gives you access to the current RapidWeaver mode inside of conditional statements. This can be particularly useful for showing content in edit mode or in preview.

@if(edit)   
    Visible in Edit mode (inside Elements)
@elseif(preview)   
    Visible in local preview (in Safari).
@else
    Visible when Published.
@endif

Regular Expressions

RegEx (Regular Expression) is a powerful tool for searching, matching, and manipulating text based on specific patterns. It uses a combination of characters, meta-characters, and quantifiers to define complex search criteria. RegEx is widely used in programming, text editors, and command-line tools for tasks like input validation, pattern extraction, and string replacement. Its versatility allows users to efficiently find and process text, from simple matches to intricate parsing operations.

In Elements it can be used to conditionally check for values, the following example checks for specific words in a text field before showing a slider.

{
    "groups": [{
        "title": "Text Field Example",
        "properties": [{
            "title": "Text Field",
            "property": "myText",
            "text": {
                "default" : "Hello World",
                "subtitle" : "two",
            }
        },{
        "visible": "myText matches /(this|that|other)+/",
        "title" : "vis",
        "property" : "slider",
        "default" : 21,
        "slider": {
            "min" : 0,
            "max" : 100,
            "round" : true
            } 
        }       
        ]
    }]
}

Looping

Elements gives you access to the current RapidWeaver mode inside of conditional statements. This can be particularly useful for showing content in edit mode or in preview when a certain property evaluates to true.

@each(item in items)
   This is item: {{item.title}}
@endeach

::index

::isFirst

::isLast

Includes

Include content from another template file

An include allows you to insert the content of another template file within the current template file.

Include files should be stored in the include directory.

Suppose you have the following template files in your include folder:

hello.html

Hello

world.html

World

You could then include the contents of those files into your main template.html file by using the following code:

@include("hello")
@include("world")

The output from template.html would look like this

Hello World

Property access

The include template files allow access to properties as if they were part of the main template file. Given that you have set a pages value in your hooks file (via the rw.setProps() method), you can pass that property to your template like so:

@include("menuitem", property: pages)

You can of cause include multiple properties simply by comma separating them:

@include("banner", title: "Can be a string", body: canAlsoBeAVariable)

Conditional Includes

Rather than wrapping your @include statement inside an @if statement, you can use the @includeIf helper:

// Instead of doing this:
@if(myVariable)
  @include("myTemplate")
@endif

// You can do this:
@includeIf(myVariable, template:"myTemplate")

// You can also negate the property
@includeIf(!myVariable, template:"myTemplate")

Image Resources

info about the new @resource (or will it be @image()

HTML

Anchor

Setting an anchor in a template file will list it and allow it to be linked to from the Link Panel in the Editor.

<div id="@anchor("myAnchor")"></div>

And using a property:

<div id="@anchor(myAnchorProperty)"></div>

See also rw.addAnchor in the hooks file.

Raw

Adding the @raw() tag around templte content will stop Element properties from being processed.

@raw()
    ‹div class="text-sm text-black-600">{{message}}</div>
@endraw

Editable Content

The following tags should be placed in the html template file. Using any of the following tags enable editable areas with the page. No setup of configuration in the properties file is required.

Editable Text

Add a simple editible text area with optional default text.

@text("heading")

An editable text area with a default value of "Hello World!".

@text("heading", default: "Hello World!")

Editable Typography

Adds a text area that support the Typography feature.

 @richtext("heading", default: "Hello World!")

Dropzones

Dropzone Naming

A dropzones is an area within a template where child element can be added. Each dropzone requires a name giving you a consistent & reliable way to manage child items.

@dropzone("extraItems")
@dropzone(name: "content")
@dropzone("zone-1", title: "Zone 1")

Multiple dropzones with the same name can used, but you should be careful not to show them at the same time. This can be useful if you want to show child items in different places based on a control.

@if(edit)
   @dropzone("extraItems")
@elseif(preview)
   @dropzone("extraItems")
@endif

Resource Dropzone

Sometimes you want to allow the user to drop the resources directly into the Editor, this can be achieved with the rwResourceDropZone key.

The following example shows how to support dropping a folder of images directly into the Editor. Place the following in the Template HTML:

<div class="grid grid-cols-4 gap-4" rwResourceDropZone="images">
    @if(!images.isFolder)
        <!-- if the `images` resource property is not a folder, display this message -->
        <h3 class="font-bold text-lg uppercase text-brand-500 p-10 col-span-full place-self-center">
            Drop a folder of images here!
        </h3>
    @else
        <!-- Otherwise we have a folder of resources (yay!) -->
        @each(image in images.resources)
            <img src="{{image.image}}" alt="{{image.caption}}" />
        @endeach
    @endif
</div>

You also need a corresponding image control in the Properties file:

{
    "groups": [{
        "title" : "Content",
        "properties" : [{
            "title" : "Images",
            "property": "images",
            "resource": {
                "types": ["image"]
            }
        }]
    }]
}

Inline templates

At the start of a template file you can add an inline template that can be included like any other template file. This is handy if you have small parts of your template you want to extract, and don't want or need to create separate templates files.

@template("list-item")
    <li>{{item.name}}</li>
@endtemplate

<h1>My List</h1>
<ul>
    @each(item in items)
        @include("list-item")
    @endeach
</ul>

CSS

JavaScript

Assets

Assets can be included per component or shared in a component pack. Assets are additional files that should be deployed when a project is published. They are not processed in any way and are simply copied during publish.

Directory structure

All assets should be placed inside a directory named page and will be deployed once to the page's files directory during publish. Here's an example showing where to place the snowstorm.js file.

components
    com.domain.weatherpack.snowmachine
        assets
            page
                snowstorm.js

Currently, page is the only area currently supported. If you need to deploy assets to another area, please visit the forum and let us know.

Usage

To link to an asset in a single component you'll need to do the following;

  1. Use the {{assetPath}} macro inside of the index.html Template file. If you need the include to appear in the head (or other areas) of the page you can use the Portal Function.

@portal(headEnd)
    <script src="{{assetPath}}/snowstorm.js"></script>
@endportal
  1. Include the following code in the hooks.js to return the assetPath to the page template:

const transformHook = (rw) => {

	// Extract the slider property
	const { assetPath } = rw.component;
	
	// Set slider and message properties in our template data
	rw.setProps({
		assetPath
	});
}

exports.transformHook = transformHook;

Hooks.js

Hooks provide a powerful way to extend your components by manipulating properties before they are applied within templates.

The typical flow of a component follows this sequence: properties → hooks → template. Hooks process and refine properties, allowing you to perform complex logic before passing them into the template for rendering.

Each component has its own unique hooks.js file. To share code across multiple components, refer to shared hooks.

To use a transform hook, create a hooks.jsfile in the root of your component with this code.

// Create a function called transformHook that receives the rw API object
const transformHook = (rw) => {

    // Use the API to set a property called message to "Hooks are awesome!"
    rw.setProps({
        message: "Hooks are awesome!"
    });
};

// Register the transformHook function with Elements
exports.transformHook = transformHook;

Next, add this to the template.html file in the component templates directory.

<div class="p-6 text-black-600 text-center">
    {{message}}
</div>

Global Properties

Property Name
Type
Values

rw:mode

string

edit or preview

Available Context methods

Method Name
Type
Values

getValues

getClassNames

getResponsiveValues

getBreakpoints

getBreakpointNames

Setting Values

in hooks.js

// Set values in the context
context.setValues({
    "obj": {
        "name" : "Mario",
        "job" : {
            "title" : "Plumber"
        }
    }
})

in template.html

<p><strong>{{obj.name}}</strong> - {{obj.job.title}}</p>

The rendered output would look something like this:

Mario - Plumber

Transform Hook

The transform hook allows you to get and set property values before they're used in templates. Let's assume you have the following slider defined in your properties and you're using the value in a template.

// properties.json
{
    "title" : "Padding",
    "property" : "padding",
    "default" : 5,
    "slider": {
        "min" : 0,
        "max" : 12,
        "round" : true
    }
}
// template.html
The slider is {{padding}}

When the slider is set to 5, the output would be

The slider is 5

Now lets add a transform hook that multiplies the padding value by 2

const transformHook = (rw) => {
  const {
    padding,
  } = rw.props;

  rw.setProps({
    padding: padding*2,
  });
};

exports.transformHook = transformHook;

With the slider still set to 5, when the template is processed the output will be

The slider is 10

This becomes incredibly powerful and convenient when you need to perform logic based on multiple properties. The following example show how you might generate a list of css classes based on standard or custom settings.

const transformHook = (rw) => {
  const {
    customSizing,     // Checkbox
    size,             // Slider 
    fontSize          // Slider 
    paddingTop        // Slider 
    paddingRight      // Slider 
    paddingBottom     // Slider 
    paddingLeft       // Slider
  } = rw.props;
  
  const sizeClasses = [
    // Included if customSizing == false
    !customSizing && size,
    
    // Included if customSizing == true
    customSizing && fontSize,
    customSizing && paddingTop,
    customSizing && paddingRight,
    customSizing && paddingBottom,
    customSizing && paddingLeft,
  ]
    .filter(Boolean)
    .join(" ");

  rw.setProps({
    classes: sizeClasses,
  });
};

exports.transformHook = transformHook;

The template simply uses the classes property instead of attempting to handle all options from 7 different properties. For example

<div class="{{classes}}"><div>

Common use cases

Passing data to templates

Working with Collections

const transformHook = (rw) => {
    rw.setProps({
        // Very important to have access to the collections in the template.
        ...rw.collections
    })
}
exports.transformHook = transformHook;

Working with UI Controls

Working with Resources

Available Functions

rw.addAnchor

rw.addAnchor("myAnchor");

rw.getBreakpoints

rw.getBreakpointNames

rw.getResponsiveValues

rw.resizeResource

rw.setProps

rw.setRootElement

Available Data

Access Project Properties

Properties from the current environment can be used within templates but most of them need to be passed through using the setProps command in the hooks.js file first. The node object is always available.

const transformHook = (rw) => {    
    // Expose project, page and component objects to the template, node is always available
    rw.setProps({
        project: rw.project,
        page: rw.page,
        component: rw.component
    })
}

exports.transformHook = transformHook;

With this in place, we gain access to the following within template files.

Project Properties

Property Name
Type
Description

{{project.title}}

String

{{project.mode}}

String

{{project.siteURL}}

String

{{project.language}}

String

{{project.enableSocialTags}}

Boolean

{{project.allowDarkMode}}

Boolean

{{project.logo.url}}

String

{{project.logo.alt}}

String

Page Properties

Property Name
Type
Description

{{page.id}}

String

{{page.title}}

String

{{page.menuTitle}}

String

{{page.filename}}

String

{{page.ext}}

String

{{page.language}}

String

{{page.absolutePath}}

String

{{page.docRootPath}}

String

{{page.projectResourcesPath}}

String

Path to the project resources

{{page.isFolder}}

Boolean

Does this page represent a folder

{{page.displayInMenu}}

Boolean

Should the page be shown in menus

{{page.isDraft}}

Boolean

Draft pages are not published

{{page.icon.url}}

String

Path to the page icon

Node Properties

The node object is always passed into templates and contains the following properties.

Property Name
Type
Description

{{node.id}}

String

{{node.title}}

String

{{node.parent.id}}

String

{{node.parent.container}}

String

{{node.backendPath}}

String

Path to the node's backend folder.

Component Properties

Property Name
Type
Description

{{component.title}}

String

{{component.group}}

String

{{component.version}}

Integer

{{component.build}}

Integer

{{component.assetPath}}

String

Path to component assets

{{component.siteAssetPath}}

String

Path to component site assets

{{component.sharedAssetPath}}

String

Path to pack shared assets

rw.collections

rw.component

rw.node

rw.pages

The following template example will output the page (and folder) details for all the pages in a project.

@each(page in pages)
    {{page.title}}
    {{page.isPublished}}
    {{page.displayInMenu}}
    {{page.url}}
    {{page.isActive}}
    {{page.isExternalPage}}
    {{page.pageDepth}}
    {{page.openInNewWindow}}
    {{page.icon}}
    {{page.hasPages}}
    {{page.hasActiveChild}}

    @each(childpage in page.pages) 
        {{childpage.title}} 
    @endeach
@endeach

Before you can access the page properties you'll need to include the following code in the Hooks.js

const transformHook = (rw) => {    
    // Get pages from rw
    const pages = rw.pages
    
    // Set pages in our template data
    rw.setProps({ 
        pages
    })
}

exports.transformHook = transformHook;

rw.project

rw.props