rw-elements-tools
v1.3.6
Published
Build tools for RapidWeaver Elements packs - generates properties.json and hooks.js files
Maintainers
Readme
rw-elements-tools
The official build toolkit for creating RapidWeaver Elements
Build powerful, reusable web components for RapidWeaver without the complexity. This toolkit handles the heavy lifting so you can focus on what matters: creating great elements.
What is rw-elements-tools?
rw-elements-tools is a development toolkit that simplifies the process of creating custom elements for RapidWeaver, the popular Mac website builder. It provides:
- A powerful CLI for building and watching your element files
- Ready-to-use controls for common UI patterns (colors, spacing, typography, and more)
- Shared utilities for generating Tailwind CSS classes
- Smart optimization that automatically removes unused code from your builds
Who is this for?
- Theme developers who want to create custom RapidWeaver elements
- Agencies building bespoke elements for client projects
- Developers looking to extend RapidWeaver's capabilities
- Anyone who wants to contribute elements to the RapidWeaver ecosystem
Why use rw-elements-tools?
| Without rw-elements-tools | With rw-elements-tools | |--------------------------|----------------------| | Manually write complex JSON config files | Use intuitive JavaScript configuration | | Copy/paste utility code between elements | Import from a shared library | | Bloated output with unused code | Automatic dead code elimination | | Manual rebuilds on every change | Watch mode for instant updates |
Installation
npm install --save-dev rw-elements-toolsQuick Start
Step 1: Set Up Your Project
Create a new directory for your element pack project and initialize it:
mkdir my-element-pack
cd my-element-pack
npm init -y
npm install --save-dev rw-elements-toolsStep 2: Create the Required Directory Structure
Your project needs a packs folder containing your element pack(s). Each pack follows this structure:
my-element-pack/
├── package.json
├── packs/ # Default packs directory
│ └── MyPack.elementsdevpack/ # Your pack (must end in .elementsdevpack)
│ └── components/
│ └── com.yourcompany.elementname/ # Component folder (must start with com.)
│ ├── properties.config.json # Source config (you edit this)
│ ├── properties.json # Generated output (don't edit)
│ ├── hooks.source.js # Source hooks (you edit this)
│ └── hooks.js # Generated output (don't edit)
└── node_modules/Key naming conventions:
- Pack folders must end with
.elementsdevpack - Component folders must start with
com.(e.g.,com.mycompany.button) - Source files:
properties.config.jsonandhooks.source.js - Generated files:
properties.jsonandhooks.js
Step 3: Create Your First Element
Create the folder structure for your first element:
mkdir -p packs/MyPack.elementsdevpack/components/com.mycompany.buttonCreate a minimal properties.config.json:
{
"groups": [
{
"title": "Content",
"icon": "text.alignleft",
"properties": [
{
"title": "Button Text",
"id": "buttonText",
"text": {
"default": "Click Me"
}
}
]
}
]
}Create a hooks.source.js that uses the shared hook utilities:
function transformHook(rw) {
// Props are accessed via rw.props - property IDs from properties.config.json
const { buttonText } = rw.props;
// Build CSS classes using the shared classnames utility
const classes = classnames()
.add(globalSpacing(rw))
.add(globalBgColor(rw))
.toString();
return {
classes,
buttonText
};
}
exports.transformHook = transformHook;Note: The
buttonTextprop corresponds to the"id": "buttonText"defined inproperties.config.json. All property IDs become available onrw.props. Functions likeclassnames(),globalSpacing(), andglobalBgColor()come from the shared hooks library—no imports needed.
Step 4: Add Build Scripts
Add these scripts to your package.json:
{
"scripts": {
"build": "rw-build all",
"build:properties": "rw-build properties",
"build:hooks": "rw-build hooks",
"dev": "rw-build all --watch"
}
}Step 5: Build Your Elements
# One-time build
npm run build
# Or watch for changes during development
npm run devThat's it! The build tool will generate properties.json and hooks.js files in each component folder.
Build Commands Reference
# Build all properties and hooks
npx rw-build all
# Build properties only
npx rw-build properties
# Build hooks only
npx rw-build hooks
# Watch for changes
npx rw-build all --watch # Watch both properties and hooks
npx rw-build properties --watch # Watch properties only
npx rw-build hooks --watch # Watch hooks onlyConfiguration
The packs directory can be configured via multiple methods (in priority order):
1. CLI Argument (highest priority)
rw-build all --packs ./my-elements2. Environment Variable
RW_PACKS_DIR=./my-elements npm run build3. package.json
{
"rw-elements-tools": {
"packsDir": "./my-elements"
}
}4. Config File
// rw-elements-tools.config.js
export default {
packsDir: './my-elements'
}5. Default
If no configuration is provided, looks for ./packs in the project root.
Table of Contents
- Overview
- Directory Structure
- Properties Build System
- Shared Hooks Build System
- Controls
- Properties
- Configuration File Format
- Adding New Controls
- Adding New Properties
- Adding Shared Hooks
- Advanced Features
- Build Commands
- Programmatic Usage
Overview
The build system processes properties.config.json files located in element component directories and generates expanded properties.json files. This allows developers to:
- Reuse common UI controls across multiple elements via
globalControlreferences - Share property definitions (like spacing values, font weights) via
usereferences - Override defaults per-element while maintaining a single source of truth
- Apply theme defaults for consistent theming across elements
Data Flow
properties.config.json Controls (controls/)
│ │
▼ ▼
┌─────────────────────────────────────┐
│ build-properties.js │
│ • Expands globalControl references │
│ • Resolves 'use' property refs │
│ • Applies overrides & defaults │
│ • Injects Advanced group controls │
└─────────────────────────────────────┘
│
▼
properties.json
(consumed by RapidWeaver)Directory Structure
rw-elements-tools/
├── bin/
│ └── cli.js # CLI entry point (rw-build command)
├── build-properties.js # Properties build script
├── build-shared-hooks.js # Shared hooks build script
├── config.js # Configuration resolver
├── index.js # Package entry point
├── package.json # npm package config
├── README.md # This documentation
├── controls/ # Reusable UI control definitions
│ ├── index.js # Exports all controls
│ ├── alignment/ # Flexbox/Grid alignment controls
│ ├── Animations/ # Animation and scroll animation controls
│ ├── Background/ # Background color, image, gradient, video
│ ├── Borders/ # Border and outline controls
│ ├── core/ # Essential controls (ID, CSSClasses, etc.)
│ ├── Effects/ # Box shadow, opacity, filters, blur
│ ├── grid-flex/ # Grid and flexbox item controls
│ ├── interactive/ # Button, input, link, filter controls
│ ├── Layout/ # Position, overflow, visibility, z-index
│ ├── Overlay/ # Overlay color, gradient, image
│ ├── Sizing/ # Width, height, min/max sizing
│ ├── Spacing/ # Margin and padding controls
│ ├── Transforms/ # Rotate, scale, skew, translate
│ ├── Transitions/ # Transition timing and properties
│ └── typography/ # Text color, decoration, styles
├── properties/ # Reusable property value definitions
│ ├── index.js # Exports all properties
│ ├── Slider.js # Slider value ranges
│ ├── FontWeight.js # Font weight options
│ └── ... # Other property definitions
└── shared-hooks/ # Shared JavaScript hook functions
├── animations/ # Animation and reveal functions
├── background/ # Background processing functions
├── borders/ # Border and outline functions
├── core/ # Essential utilities (classnames, etc.)
├── effects/ # Visual effects (opacity, filters)
├── interactive/ # Link and filter functions
├── layout/ # Layout and positioning
├── navigation/ # Navigation component styles
├── sizing/ # Dimensions and aspect ratios
├── spacing/ # Margin and padding functions
├── transforms/ # CSS transform functions
├── transitions/ # CSS and Alpine transitions
└── typography/ # Text and font style functionsProperties Build System
Entry Point
The properties build script is executed via:
cd src
npm run buildThis runs node build.js which:
Finds all config files matching the glob pattern:
../**/*.elementsdevpack/components/com.**/**/properties.config.jsonProcesses each config file through the following pipeline:
- Parse the JSON configuration
- Set up the Advanced group with injected controls (CSSClasses, ID)
- Process each property group
- Expand
globalControlreferences - Resolve
useproperty references - Apply overrides and theme defaults
- Write the output to
properties.json
Processing Pipeline
For each property in a config file:
┌─────────────────────────────────────────────────────────────┐
│ 1. Check for globalControl │
│ └─ If present: Load control from Controls registry │
│ └─ If absent: Pass through with 'use' resolution only │
├─────────────────────────────────────────────────────────────┤
│ 2. Deep clone the control (avoid mutations) │
├─────────────────────────────────────────────────────────────┤
│ 3. Apply property overrides │
│ └─ String values: Replace directly │
│ └─ Object values: Shallow merge │
├─────────────────────────────────────────────────────────────┤
│ 4. Apply default values │
│ └─ Primitive defaults: Set control.default │
│ └─ Object defaults: Merge into theme properties │
├─────────────────────────────────────────────────────────────┤
│ 5. Apply theme defaults │
│ └─ themeColor, themeFont, themeBorderRadius, etc. │
├─────────────────────────────────────────────────────────────┤
│ 6. Transform IDs using {{value}} template │
│ └─ "prefix{{value}}Suffix" → "prefixControlIdSuffix" │
├─────────────────────────────────────────────────────────────┤
│ 7. Process nested globalControls recursively │
├─────────────────────────────────────────────────────────────┤
│ 8. Resolve 'use' references to Properties │
└─────────────────────────────────────────────────────────────┘Shared Hooks Build System
The shared hooks build system combines reusable JavaScript utility functions with component-specific hook code, then applies dead code elimination to produce optimized output.
Overview
shared-hooks/**/*.js Component hooks.source.js
│ │
▼ ▼
┌─────────────────────────────────────────┐
│ build-shared-hooks.js │
│ • Concatenates shared + component code │
│ • Applies esbuild dead code elimination │
│ • Keeps only code reachable from │
│ transformHook function │
└─────────────────────────────────────────┘
│
▼
hooks.js (optimized)
(consumed by RapidWeaver)How It Works
- Find all source files: Scans
packs/forhooks.source.jsfiles - Read shared hooks: Loads all
.jsfiles fromshared-hooks/and its subfolders - Concatenate: Combines shared code + component code
- Dead code elimination: Uses esbuild to remove unused functions
- Output: Writes optimized
hooks.jsto each component
Shared Hook Organization
Shared hooks are organized into category subfolders:
| Folder | Purpose | Example Functions |
|--------|---------|-------------------|
| core/ | Essential utilities | classnames, getHoverPrefix, globalHTMLTag |
| layout/ | Layout and positioning | globalLayout, globalActAsGridOrFlexItem |
| sizing/ | Dimensions and aspect ratios | globalSizing, aspectRatioClasses |
| spacing/ | Margin and padding | globalSpacing, globalSpacingMargin |
| background/ | Background styles | globalBackground, globalBgImageFetchPriority |
| borders/ | Borders and outlines | globalBorders, globalOutline |
| effects/ | Visual effects | globalEffects, globalFilters, globalOverlay |
| typography/ | Text and font styles | globalTextFontsAndTextStyles, globalHeadingColor |
| transforms/ | CSS transforms | globalTransforms |
| transitions/ | CSS/Alpine transitions | globalTransitions, getAlpineTransitionAttributesMobile |
| animations/ | Animations and reveals | globalAnimations, globalReveal |
| navigation/ | Navigation styles | globalNavItems, globalMenuItem |
| interactive/ | Links and filters | globalLink, globalFilter |
Component Hook Files
Each component that needs hooks creates a hooks.source.js file:
// packs/Core.elementsdevpack/components/com.realmacsoftware.button/hooks.source.js
function transformHook(rw) {
// Use any shared hook functions here
const classes = classnames(rw.props.customClasses)
.add(globalSpacing(rw))
.toString();
return {
classes
};
}
exports.transformHook = transformHook;Key Points
- Entry point: The
transformHookfunction is the only exported function - Dead code elimination: Only code reachable from
transformHookis kept - No manual imports: Shared code is concatenated, not imported
- Auto-generated: Output
hooks.jsfiles are marked "do not edit"
Build Commands
# Build all hooks once
npm run build:hooks
# Watch for changes
npm run build:hooks:watch
# Using npm scripts
npm run build:hooksControls
Controls are reusable UI component definitions that map to RapidWeaver's property inspector UI elements.
Control Structure
A control is a JavaScript object (or array of objects) that defines:
// Simple control (single object)
const FlexDirection = {
title: "Direction",
id: "flexDirection",
select: {
default: "flex-col",
items: [
{ value: "flex-col", title: "Column" },
{ value: "flex-row", title: "Row" },
],
},
};
export default FlexDirection;// Compound control (array of objects)
const Link = [
{
title: "Link",
heading: {}
},
{
title: "To",
id: "globalLink",
link: {}
}
];
export default Link;UI Element Types
Controls can use these UI element types:
| Type | Description | Example |
|------|-------------|---------|
| select | Dropdown menu | { select: { default: "value", items: [...] } } |
| segmented | Segmented button control | { segmented: { default: "a", items: [...] } } |
| switch | Boolean toggle | { switch: { default: false } } |
| slider | Numeric slider | { slider: { default: 50, min: 0, max: 100 } } |
| number | Numeric input | { number: { default: 0 } } |
| text | Text input | { text: { default: "" } } |
| textArea | Multi-line text | { textArea: { default: "" } } |
| link | Link picker | { link: {} } |
| resource | Resource/image picker | { resource: {} } |
| heading | Section heading (no value) | { heading: {} } |
| divider | Visual separator | { divider: {} } |
| information | Info text | { information: {} } |
| themeColor | Theme color picker | { themeColor: { default: {...} } } |
| themeSpacing | Theme spacing picker | { themeSpacing: { mode: "single" } } |
| themeBorderRadius | Border radius picker | { themeBorderRadius: {...} } |
| themeBorderWidth | Border width picker | { themeBorderWidth: {...} } |
| themeShadow | Shadow picker | { themeShadow: {...} } |
Control Properties
| Property | Type | Description |
|----------|------|-------------|
| title | string | Display label in the UI |
| id | string | Property identifier (used in templates) |
| format | string | CSS class format, e.g., "gap-x-{{value}}" |
| visible | string | Visibility condition, e.g., "otherProp == 'value'" |
| responsive | boolean | Whether control is responsive (default: true) |
| globalControl | string | Reference to another control (nested) |
Nested globalControls
Controls can reference other controls for composition:
const Borders = [
{
globalControl: "ControlType",
id: "{{value}}Borders",
},
{
globalControl: "BorderStyle",
visible: "globalControlTypeBorders == 'static'",
},
{
globalControl: "BorderColor",
visible: "globalControlTypeBorders == 'static'",
},
];Properties
Properties are reusable value definitions (like enums or option lists) that can be referenced using the use key.
Property Structure
// properties/FontWeight.js
const FontWeight = {
default: "normal",
items: [
{ value: "thin", title: "Thin" },
{ value: "light", title: "Light" },
{ value: "normal", title: "Normal" },
{ value: "medium", title: "Medium" },
{ value: "semibold", title: "Semibold" },
{ value: "bold", title: "Bold" },
],
};
export default FontWeight;Using Properties in Controls
Reference a property using the use key:
const TextWeight = {
title: "Weight",
id: "textWeight",
select: {
use: "FontWeight" // Merges FontWeight's items and default
}
};The build system will merge the referenced property, with local values taking precedence:
// Output
{
title: "Weight",
id: "textWeight",
select: {
default: "normal",
items: [
{ value: "thin", title: "Thin" },
// ... etc
]
}
}Configuration File Format
properties.config.json Structure
{
"groups": [
{
"title": "Group Title",
"icon": "sf-symbol-name",
"properties": [
// Property definitions
]
}
]
}Using globalControl
Reference a control by name:
{
"globalControl": "Spacing"
}Overriding Control Properties
Add properties alongside globalControl to override:
{
"globalControl": "BorderRadius",
"title": "Corner Radius",
"default": {
"base": {
"topLeft": "lg",
"topRight": "lg",
"bottomLeft": "none",
"bottomRight": "none"
}
}
}ID Templates with {{value}}
Transform the control's ID using a template:
{
"globalControl": "Spacing",
"id": "card{{value}}"
}If the Spacing control has id: "globalPadding", the output becomes id: "cardGlobalPadding".
Theme Defaults
Override theme-related properties:
{
"globalControl": "Background_Color",
"themeColor": {
"default": {
"name": "brand",
"brightness": 500
}
}
}Supported theme properties:
themeColorthemeFontthemeBorderRadiusthemeBorderWidththemeSpacingthemeShadowthemeTextStyle
Inline Properties
Properties without globalControl are passed through with use resolution:
{
"title": "Custom Width",
"id": "customWidth",
"slider": {
"use": "Slider",
"default": 100,
"min": 0,
"max": 500
}
}Adding New Controls
Step 1: Create the Control File
Create a new file in the appropriate controls/ subdirectory:
// controls/Effects/NewEffect.js
const NewEffect = {
title: "Effect Intensity",
id: "effectIntensity",
format: "effect-[{{value}}%]",
slider: {
default: 50,
min: 0,
max: 100,
round: true,
units: "%"
}
};
export default NewEffect;Step 2: Export from index.js
Add the export to controls/index.js:
// In the appropriate section
export { default as NewEffect } from "./Effects/NewEffect.js";Step 3: Use in Config Files
Reference in any properties.config.json:
{
"globalControl": "NewEffect"
}Step 4: Rebuild
cd src
npm run buildCreating Compound Controls
For controls with multiple UI elements:
// controls/interactive/CustomButton.js
const CustomButton = [
{
title: "Button Settings",
heading: {}
},
{
title: "Style",
id: "buttonStyle",
segmented: {
default: "solid",
items: [
{ value: "solid", title: "Solid" },
{ value: "outline", title: "Outline" },
{ value: "ghost", title: "Ghost" }
]
}
},
{
title: "Size",
id: "buttonSize",
select: {
use: "ButtonSize"
}
}
];
export default CustomButton;Creating Controls with Nested globalControls
// controls/Layout/CustomLayout.js
const CustomLayout = [
{
globalControl: "ControlType",
id: "{{value}}CustomLayout"
},
{
visible: "globalControlTypeCustomLayout != 'none'",
divider: {}
},
{
visible: "globalControlTypeCustomLayout != 'none'",
globalControl: "Position"
},
{
visible: "globalControlTypeCustomLayout != 'none'",
globalControl: "ZIndex"
}
];
export default CustomLayout;Adding New Properties
Step 1: Create the Property File
// properties/CustomSizes.js
const CustomSizes = {
default: "md",
items: [
{ value: "xs", title: "Extra Small" },
{ value: "sm", title: "Small" },
{ value: "md", title: "Medium" },
{ value: "lg", title: "Large" },
{ value: "xl", title: "Extra Large" },
]
};
export default CustomSizes;Step 2: Export from index.js
// properties/index.js
export { default as CustomSizes } from "./CustomSizes.js";Step 3: Use in Controls
const SizeSelector = {
title: "Size",
id: "elementSize",
select: {
use: "CustomSizes"
}
};Or use directly in config files:
{
"title": "Size",
"id": "mySize",
"select": {
"use": "CustomSizes"
}
}Adding Shared Hooks
Step 1: Create the Shared Hook File
Create a new file in the appropriate shared-hooks/ subfolder:
// shared-hooks/effects/customEffect.js
/**
* Generate custom effect classes based on element properties
* @param {Object} rw - The RapidWeaver element object
* @returns {string} CSS class string
*/
function customEffect(rw) {
const { customEnabled, customIntensity } = rw.props;
if (!customEnabled) return '';
return classnames([
'custom-effect',
customIntensity && `intensity-${customIntensity}`
]).toString();
}Step 2: Use in Component Hooks
Reference the function in any hooks.source.js:
// packs/MyPack.elementsdevpack/components/com.example.mycomponent/hooks.source.js
function transformHook(rw) {
// customEffect is available from shared hooks (no import needed)
const effectClasses = customEffect(rw);
return {
effectClasses
};
}
exports.transformHook = transformHook;Step 3: Build
npm run build:hooksNaming Conventions
- Folder organization: Place files in the appropriate category folder
- Prefix with
global: For element property processing functions (e.g.,globalSpacing) - Use descriptive names: Match the function name to the file name
| Category | Folder | Example |
|----------|--------|---------|
| Core utilities | core/ | classnames.js, getHoverPrefix.js |
| Layout functions | layout/ | globalLayout.js |
| Visual effects | effects/ | globalEffects.js |
| Typography | typography/ | globalHeadingColor.js |
Dead Code Elimination
The build system automatically removes unused code. If you add a function to shared hooks but no component uses it, it won't appear in any output hooks.js file. This keeps the output lean.
Example: Complex Shared Hook
// shared-hooks/layout/globalLayout.js
/**
* Generate layout-related CSS classes
*/
const globalLayout = (app, args = {}) => {
const {
globalLayoutPosition: position,
globalLayoutZIndex: zIndex,
globalLayoutOverflow: overflow,
} = app.props;
return classnames([
position,
zIndex,
overflow,
]).toString();
};Advanced Features
Conditional Visibility
Show/hide controls based on other property values:
{
"title": "Custom Value",
"id": "customValue",
"visible": "sizeType == 'custom'",
"number": {
"default": 100
}
}Complex conditions:
{
"visible": "enableFeature == true && mode == 'advanced'"
}Format Strings
Generate CSS class names from values:
{
"id": "gapX",
"format": "gap-x-{{value}}",
"themeSpacing": {
"default": { "base": { "value": "4" } }
}
}Output when value is "8": gap-x-8
Responsive Controls
By default, controls are responsive. Disable with:
{
"id": "staticValue",
"responsive": false,
"text": { "default": "" }
}The Advanced Group
The build system automatically:
- Injects
CSSClassesandIDcontrols at the start of the Advanced group - Creates an Advanced group if one doesn't exist
- Moves any existing Advanced group to the end
- Sets the icon to "gearshape"
To add controls to the Advanced group:
{
"groups": [
{
"title": "Advanced",
"properties": [
{ "divider": {} },
{ "globalControl": "HTMLTag" }
]
}
]
}Build Commands
Using the CLI (recommended)
# Build everything (properties + hooks)
rw-build all
# Build properties only
rw-build properties
# Build hooks only
rw-build hooks
# Watch for changes
rw-build all --watch # Watch both properties and hooks
rw-build properties --watch # Watch properties only
rw-build hooks --watch # Watch hooks only
# Build with custom packs directory
rw-build all --packs ./my-elements
# Show help
rw-build --helpUsing npm scripts
Add these to your package.json:
{
"scripts": {
"build": "rw-build all",
"build:properties": "rw-build properties",
"build:hooks": "rw-build hooks",
"dev": "rw-build all --watch"
}
}Then run:
npm run build
npm run devDevelopment Mode Details
The --watch flag monitors for changes and automatically rebuilds:
| Command | Watches |
|---------|---------|
| rw-build all --watch | Both properties and hooks (runs watchers concurrently) |
| rw-build properties --watch | properties.config.json files in packs/ |
| rw-build hooks --watch | hooks.source.js files in packs/ and shared-hooks/*.js |
Troubleshooting
"Global control 'X' not found"
- Check the control is exported in
controls/index.js - Verify the spelling matches exactly (case-sensitive)
"Property 'X' not found in Properties"
- Check the property is exported in
properties/index.js - Verify the
usekey spelling
Build produces unexpected output
- Check for circular
globalControlreferences - Verify JSON syntax in config files
- Run with
--trace-warningsflag for more details
Example: Complete Element Config
{
"groups": [
{
"title": "Content",
"icon": "text.alignleft",
"properties": [
{
"title": "Heading",
"id": "headingText",
"text": {
"default": "Welcome"
}
},
{
"globalControl": "HeadingColor"
}
]
},
{
"title": "Layout",
"icon": "square.split.bottomrightquarter",
"properties": [
{
"globalControl": "Layout"
}
]
},
{
"title": "Spacing",
"icon": "squareshape.squareshape.dotted",
"properties": [
{
"globalControl": "Spacing"
}
]
},
{
"title": "Background",
"icon": "paintbrush.fill",
"properties": [
{
"globalControl": "BackgroundTransparent"
}
]
},
{
"title": "Borders",
"icon": "square.dashed",
"properties": [
{
"globalControl": "Borders"
}
]
},
{
"title": "Advanced",
"properties": [
{
"divider": {}
},
{
"globalControl": "HTMLTag"
}
]
}
]
}Programmatic Usage
You can also use rw-elements-tools programmatically in your own build scripts:
import {
buildProperties,
buildHooks,
watchProperties,
watchHooks,
resolveConfig,
Controls,
Properties
} from 'rw-elements-tools';
// Resolve configuration from all sources
const config = await resolveConfig({
packs: './my-elements' // Optional CLI override
});
// Build properties
await buildProperties(config);
// Build hooks
await buildHooks(config);
// Watch for changes
await watchProperties(config); // Watch properties only
await watchHooks(config); // Watch hooks only
// Or watch both concurrently
await Promise.all([
watchProperties(config),
watchHooks(config)
]);Accessing Controls and Properties
import { Controls, Properties } from 'rw-elements-tools';
// Use a control definition
console.log(Controls.Spacing);
console.log(Controls.BorderRadius);
// Use a property definition
console.log(Properties.FontWeight);
console.log(Properties.Slider);Custom Build Pipeline
import { resolveConfig, buildProperties, buildHooks } from 'rw-elements-tools';
async function customBuild() {
const config = await resolveConfig();
console.log('Building for:', config.packsDir);
// Build properties first
await buildProperties(config);
// Then build hooks
await buildHooks(config);
console.log('Build complete!');
}
customBuild();Publishing
To publish the package to npm:
cd src
npm login
npm publishFor scoped packages:
npm publish --access publicContributing
When adding new controls or properties:
- Follow naming conventions: PascalCase for exports, descriptive names
- Place in correct directory: Use the categorical structure
- Add exports: Update the relevant
index.js - Test the build: Run
npm run buildand verify output - Document: Add comments for complex controls
Last updated: January 2026
