NAV

Introduction

Lucidchart and Lucidspark support using custom, user generated shapes. Custom shapes can be made through the Extension API which will produce .lcsz files. This page covers the creation and testing of custom shapes.

The Extension API allows users and developers to build custom shape libraries that can be distributed to select Lucid users, to all users in their Lucid account, or to all Lucid users via the Lucid Marketplace. This page also describes Lucid’s custom shape format, which provides users and developers a powerful set of capabilities for creating and customizing shapes.

The purpose of this documentation is to describe the basic functionality, format, and validation of custom shapes, which should allow a user to build a custom shape library from scratch. The format is still evolving, and it is expected that this documentation will be revised over time to reflect the most up-to-date information as it relates to the custom shape format.

Known issues

In general, concepts and features listed in this document are implemented and functional. However, there are a few areas which are in development and are not considered functional:

  • User-defined i18n translations are parsed and verified, but not currently used.
  • Formatting inside of text areas is not implemented (textareas as a whole can be formatted, but the format cannot change within the textarea).
    • In the future, dynamic textareas that aren't backed by block properties will allow this, but only for non-editable text areas.

Getting started

A shape library is defined in a folder that lives in an extension package. An extension package is a set of code, content, and configuration for Lucid products that is installable. Extension packages can be distributed via Lucid’s Developer Portal, and can have one or more shape libraries. Instructions for creating extension packages can be found in the Extension API documentation.

A shape library contains custom shapes, and they can be created through the Extension API, which will be covered below. A custom shape contains two key pieces:

  • A shape definition, which is a single .shape file. Shape definitions are responsible for the majority of shape properties as well as how it is rendered.
  • A shape entry, which is a listing in the library manifest file. Each shape entry defines default properties for the shape, as well as which shape definition it refers to. Note that a shape definition can be referenced by multiple shape entries.

HJSON

HJSON example
{ // No quotes on the value below. a: b c: ["d", "e", "f"] g: 123 }

All code files in the custom shape format allow HJSON, a superset of JSON which allows comments, unquoted object keys, and flexibility around commas.

Note the missing trailing commas and the unquoted string value for b. The unquoted string value for b illustrates a potential gotcha in HJSON, where commas or other characters (anything after the ":") will be included in the string. For example, consider the following HJSON:

{"a": b}

This actually represents an object with the key/value pair of a -> b, } instead of a -> b, and results in a syntax error.

For more information about the HJSON format, see https://hjson.github.io/.

Getting started with the Extension API

Creating and testing shape libraries through the Extension API requires using commands from Lucid's CLI and editing files directly. To start, you can create a shape library using the following command in an existing package:

npx lucid-package create-shape-library <name>

This will create a shape library with one custom shape (a simple rectangle) and a default library.manifest.

For creating a library just from images, see create-image-shape-library.

File organization

In a new shape library directory, you will have the following structure:

> my-library > images └── ... > shapes └── first.shape └── library.manifest

first.shape
{ "properties": [], "geometry": [ { "type": "rect" } ] }
  • The images folder is where you can store images to be referenced in your .shape files.

  • The shapes folder is where your .shape files will be stored, which house shape definitions.

    • first.shape is a boilerplate shape file that contains a basic shape definition.
  • The library.manifest file lists the name of the shape library and which shapes are included in the shape library. It lists its shapes through shape entries. Take note that each shape entry must reference a valid .shape file (e.g. shape: shape1 for the file /shapes/shape1.shape) and any .shape file that is not referencd by a shape entry won't show up in your library.

Library manifest
library.manifest
{ "name": "Test Library", "shapes": [ { "shape": "first", "name": "Test Shape", "defaults": { "fillColor": "#ff0000", "strokeColor": "#00ffff", "strokeWidth": 3, "width": 300, "height": 300 } } ] }
Property Description
name string or TranslatableString
The name of the shape library
shapes ShapeEntry[]
The list of shapes in this shape library. Only shapes in this list are shown
ShapeEntry
Property Description
shape string
The basename for the shape file, found in /shapes/shapeId.shape
key string (optional)
If specified, can be used to differentiate between multiple shape entries with the same shape value when calling EditorClient.getCustomShapeDefinition
name string or TranslatableString
The name displayed for this shape in the shape library
defaults ShapeDefaults
Default block properties, like width, height, fill, etc.
properties {string:string}
Default values for custom shape data, defined in the shape definition
ShapeDefaults
Property Description
width number
The initial shape width
height number
The initial shape height
rounding number
The initial shape rounding
fillColor number
The initial fill color for the shape
strokeColor number
The initial line color for the shape
strokeWidth number
The initial line width for the shape
opacity number
The initial opacity for the shape
rotation number
The initial rotation for the shape
aspectRatio number
fixed aspect ratio. See also locked
link string
Starting url link on the shape

TranslatableString

Property Description
default string
The fallback value to display if the i18n value is not found
i18n string
The i18n key from the i18n files

Testing

Any time you are running npx lucid-package test-editor-extension, all shape libraries in the package will be automatically displayed in the Lucidchart toolbox. You can also use npx lucid-package test-shape-libraries to test your shape libraries without needing any editor extension to exist.

Custom shapes are kept up to date as you make code changes to the shape definitions. You do not need to reload the page to see your changes, but note that shapes on the canvas will not be updated.

This does not happen for released extensions. It is only a convenience while developing locally.

Custom shape library

A shape definition describes the components needed to render a shape. At the most basic level, a shape definition provides the geometry of the shape, which allows Lucidchart to produce path data. More complex shapes can contain sub-shapes that each have their own unique geometry, as well as sub-shapes of their own.

Manifest file

The manifest file defines the name and defaults for your shape:

{ "name": "Test Library", "shapes": [ { "shape": "first", "name": "Test Shape", "defaults": { "aspectRatio": .5, "fillColor": "#ff0000", "link": "your.url.com", "opacity": .5, "rotation": 1.5, "strokeColor": "#00ffff", "strokeWidth": 3, "height": 300, "width": 600 } } ] }

Shape file

Shape files are defined with JSON (or HJSON):

{ "locked": ["aspectRatio"], "images": { "imageReference": { "path": "file", "type": "file", }, }, "geometry": [ { "type": "rect", }, ], "style": { "fill": { "type": "image", "ref": "imageReference", "mode": "stretch", }, }, }

Geometry

A shape's geometry, which is defined through a shape definition, is what creates the data that is rendered to the screen. Geometry can be created from a standard geometric shape, a custom path, or a combination of the two (achieved with boolean operations). Geometry is drawn using relative coordinates, which are defined based on the bounds of the shape that the geometry is contained in (either a sub-shape or a rectangle if it is top-level geometry).

Conditional geometry

For complex geometry, formulas can be used to determine whether or not to include the geometry in the shape's render output. Conditional geometry evaluates the formula and if the result is false, the geometry is not rendered. These formulas work for standard shapes, paths, and boolean operations.

Repeating geometry

Geometry that is made up of multiple instances of the same shape can be defined using formulas. This is true even when those shapes have different sizes or positions (e.g. a venn diagram). There are two ways to use repeating geometry: ForRepeats and MapRepeats.

ForRepeat

ForRepeats are used to repeat geometry multiple times according to a min value and a max value. Be default, this will be looped from min to max with a step of 1, but you are able to adjust the step if you want. For example, a ForRepeat with a min of 1, a max of 7, and a step of 2 would loops 4 times, resulting in index values of 1, 3, 5, and 7 for each respective loop.

Property Description
type for
index string
min number or formula
max number or formula
step number or formula
MapRepeat

MapRepeats are used to repeat geometry by looping over the items in a given array. For example, a MapRepeat with a list equal to [2, 4, 6, 8] would repeat 4 times, resulting in item values of 2, 4, 6, and 8 for each respective loop. If you defined a variable for index, then it would have values of 1, 2, 3, and 4 for each repsective loop.

Property Description
type map
index string
item string
list number[]

Geometry schema

All geometry defined in the custom shape format follows this general schema:

Geometry Example
{ "type": "rect", "repeat": { "min": 5, "max": 20 }, "localFill": false }
Property Description
type string
The type of geometry.
Options: rect, ellipse, polygon, path, BooleanOperation
condition formula
A formula which controls whether or not the geometry is included in the output.
repeat ForRepeat or MapRepeat
localFill boolean
Defines whether or not this geometry starts a gradient.
defs Definition
Allows local definition of formula (see Definitions below).

Each gemoetry type is explained in detail below:

Rectangle

Property Description
type 'rect'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
x number or formula
The x-coordinate of this rectangle, relative to its bounds (default: 0)
y number or formula
The y-coordinate of this rectangle, relative to its bounds (default: 0)
w number or formula
The width of this rectangle, relative to its bounds (default: 1)
h number or formula
The height of this rectangle, relative to its bounds (default: 1)
Rect example
{ type: "rect" // NOTE: Unspecified x and y default to 0. // NOTE: Unspecified w and h default to 1. }

Rect

Rect example
{ type: "rect", x: 0.5, y: 0.5, w: 0.5, h: 0.5 }

Rect

Ellipse

Property Description
type 'ellipse'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
x number or formula
The x-coordinate of this rectangle, relative to its bounds (default: 0)
y number or formula
The y-coordinate of this rectangle, relative to its bounds (default: 0)
w number or formula
The width of this rectangle, relative to its bounds (default: 1)
h number or formula
The height of this rectangle, relative to its bounds (default: 1)
Ellipse example
{ type: "ellipse" }

Ellipse

Polygon

Property Description
type 'polygon'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
n number or formula
The number of sides of this polygon
x number or formula
The x-coordinate of this rectangle, relative to its bounds (default: 0)
y number or formula
The y-coordinate of this rectangle, relative to its bounds (default: 0)
w number or formula
The width of this rectangle, relative to its bounds (default: 1)
h number or formula
The height of this rectangle, relative to its bounds (default: 1)
Polygon example
{ type: "polygon" n: 6 }

Polygon

Polygon example
{ type: "polygon", n: 5, inset: 0.5, w: 0.5, h: 0.5 }

Star

Path

Property Description
type 'path'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
path interpolated string
The defined path of the Path geometry. Uses SVG paths and assumes relative coordinates.
Path example
{ type: "path", path: "M 0 0 L 1 1 L 0 1 Z" }

Path

You can learn more about SVG paths online

Boolean operation

Boolean geometry operations require the geometry it operates on to be defined:

Property Description
type 'union', 'intersection', 'difference', 'xor'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
geometry Geometry[]
The geometry used in the Boolean operation
Union example
{ type: "union", geometry: [ { type: "ellipse", x: 0.25, w: 0.75, h: 0.75 } { type: "ellipse", y: 0.25, w: 0.75, h: 0.75 } ] }

Union




Intersection example
{ type: "intersection", geometry: [ { type: "ellipse", x: 0.25, w: 0.75, h: 0.75 } { type: "ellipse", y: 0.25, w: 0.75, h: 0.75 } ] }

Intersection




Difference example
{ type: "difference", geometry: [ { type: "ellipse", x: 0.25, w: 0.75, h: 0.75 } { type: "ellipse", y: 0.25, w: 0.75, h: 0.75 } ] }

Difference




XOR example
{ type: "xor", geometry: [ { type: "ellipse", x: 0.25, w: 0.75, h: 0.75 } { type: "ellipse", y: 0.25, w: 0.75, h: 0.75 } ] }

XOR

Render style

Geometry is rendered using a styling defined within the shape definition or the sub-shape. The highest-level style is used unless it is overridden by a lower-level style. So, for example, if the style is only defined in the top-level of the shape definition, all geometry in sub-shapes would be rendered using that same style.

Formatting

The style defines the formatting used to render the sub-shape's geometry. There are 3 main aspects of the style formatting that can be changed:

Property Definition
stroke Stroke
StrokeSpecifies the geometries' line color and line size.
fill Fill
Specifies the fill type of the geometries.This can be a solid color (type: color), a gradient (type: linear-gradient or type: radial-gradient), or an image fill (type: image).
rounding number or formula
Specifies rounding that is applied to the geometries when rendering. A rectangle with rounding set will be rendered as a rounded rectangle.
order geometry (default) or shapes
Indicates whether top-level geometry or subshapes are rendered first.

Image map

An example of image map entries
{ "images": { "image1": { "type": "file", "path": "image1.png" } "image2": { "type": "url", "path": "https://www.website.com/image2.jpg" } "image3": { "type": "file", "path": "image3.jpg" } }, }

Images referenced in image fills must be added into the shape's image map within the shape definition in order to identify where the image file comes from (either a URL or internal image added to the LCSZ file).

The image map is a collection of names which reference an image file or URL; the names are then referenced within the image fills themselves.

In a shape definition, you can add a new field: images: { "myImage": Image }.

Note: SVG files are supported, however, these vector images will be rasterized.

Image schema
Property Description
type file or url
path string
Indicates either the URL of the image or the filename for internal images (e.g. image1.png).

Note: External URLs (i.e. https://www.fakeurl.com/image2.jpg, above) directly reference URLs and do not add images to the Lucidchart image manager. URLs that produce images like single-pixel image trackers can be used for tracking shape usage (as it will operate as a normal web-based image), however if the URL stops being valid or the image referenced by the URL changes, that will affect the shape in the future.

Examples

A rectangle styled with a red fill and a 3 pixel blue stroke and no rounding

Example
{ style: { fill: { type: 'color', color: '#ff0000' } stroke: { color: '#0000ff', width: 3 } rounding: 0 } geometry: [ { type: rect } ] }

Border

A rectangle with an image fill using an internal image file

Example
{ ... images: { "logo": { type: 'file' path: 'logo.png' } } ... style: { fill: { type: 'image' ref: 'logo' mode: 'stretch' } stroke: { color: '#00000000', width: 1 } rounding: 0 } geometry: [ { type: rect } ] }

Border

A rectangle styled with a transparent fill and a 1 pixel black stroke and 10px rounding

Example
{ style: { fill: { type: 'color', color: '#00000000' } stroke: { color: '#000000', width: 1 } rounding: 10 } geometry: [ { type: rect } ] }

Border

Sub-shapes

The top-level shape definition can provide geometry, but oftentimes having smaller sub-shapes within the shape definition can simplify the definition and allow for more interesting shapes. Sub-shapes have a similar definition as the top-level shape definition, but allow for some additional functionality, such as condition rules, repeating rules, and configurable bounds.

Conditional shapes

If a sub-shape should only be shown under certain conditions, a condition can be defined which will only display the sub-shape(s) when the condition evaluates to true. This is useful to enable things like badges which are only displayed under certain conditions. If the conditional expression is false, the sub-shape is not rendered at all.

Repeating shapes

Sub-shapes can be repeated in a similar way as geometry, using ForRepeats or MapRepeats. For more information about the general repeat types, see Repeating Geometry.

Bounds

Lucid products will calculate the placement and size of a shape's geometry using the bounds of the shape (or sub-shape) definition that contains it. By default, the bounds of a parent shape will be applied to its sub-shapes, but you can use shape data or another formula to modify a sub-shape’s bounds and describe a new position or size for the sub-shape's geometry. Bounds is defined by specifying four parts: Anchor Position, Anchor Offset, Size, and Rotation.

ShapeBounds
Property Description
x formula
y formula
w formula
h formula
absolute boolean or string
rotation formula
anchor string or Anchor
Anchor
Property Description
x number
y number

Anchor position

The anchor position of a shape’s bounds specifies the anchor/rotation point for the shape. Combined with the anchor offset, anchor position dictates where to place the shape relative to its container. If an anchor position is not defined for the shape, it will default to anchoring to the top-left corner of its parent container.

Anchor position is defined using relative or absolute coordinates. See the Absolute vs. Relative Coordinates section below to learn more.

Anchor offset

The anchor offset of a shape’s bounds describes where the anchor position will be placed on the shape. For example, an anchor offset of top-left will place its top-left corner at the anchor position.

Anchor offset can be specified directly as a relative coordinate within the sub-shape [from (0, 0) to (1, 1)] or can be specified using one of the predefined anchor types (defined below).

As an illustration, in the example below there are 3 sub-shapes (A, B, and C), each with different anchor types and positions.

Anchor Points

The following values of each sub-shape's anchor offset are as follows:

  • Sub-shape A has an anchor offset of left, positioned at anchor point (0, 0.5), which is the left of the container.
  • Sub-shape B has an anchor of center, positioned at anchor point (0.5, 0.5), which is the center of the container.
  • Sub-shape C has an anchor of top-right, positioned at anchor point (1, 0), which is the top-right corner of the container.

Note that changing the anchor offset would move each sub-shape around their respective green points, and not the container itself.

Anchor Type Description
top-left Uses the top-left corner of the sub-shape as the anchor point. Equivalent to an anchor of (0, 0).
top Uses the top center point of the sub-shape as the anchor point. Equivalent to an anchor of (0.5, 0).
top-right Uses the top-right corner of the sub-shape as the anchor point. Equivalent to an anchor of (1, 0).
left Uses the left center point of the sub-shape as the anchor point. Equivalent to an anchor of (0, 0.5).
center Uses the center of the sub-shape as its anchor point. Equivalent to an anchor of (0.5, 0.5).
right Uses the right center point of the sub-shape as the anchor point. Equivalent to an anchor of (1, 0.5).
bottom-left Uses the bottom-left corner of the sub-shape as the anchor point. Equivalent to an anchor of (0, 1).
bottom Uses the bottom center point of the sub-shape as the anchor point. Equivalent to an anchor of (0.5, 1).
bottom-right Uses the bottom-right corner of the sub-shape as the anchor point. Equivalent to an anchor of (1, 1).

Size

The size of a shape’s geometry describes the width and height of the shape, specifying the extent of the shape’s bounds. Like anchor position, shape size can be specified using absolute or relative values. See the Absolute vs. Relative Coordinates section below to learn more.

Rotation

A shape’s bounds can specify a rotation, which will be applied to shapes after they are rendered, using the anchor point as the point of rotation. The geometry itself operates purely on a non-rotated coordinate system for simplicity.

Rotation

Absolute vs. relative coordinates

You can use absolute or relative coordinates to specify anchor position and shape size.

Absolute coordinates are measured in pixels in screen space. When you use absolute coordinates to specify anchor position, you are placing the anchor point at an exact pixel location. Absolute coordinates are measured relative to the parent bounds. For example, if the absolute position (5, 5) was specified with an anchor of top-left, the anchor point would be calculated as 5 pixels to the right and 5 pixels down from the parent's top-left corner.

Relative coordinates specify a percentage of the bounds in which the shape is contained. Relative coordinates are always in the range 0 to 1, with relative coordinates (0.5, 0.5) always positioning the shape in the center of its parent’s bounds. For example, if a shape’s container is 300 x 200 pixels and you specify a relative position of (0.1, 0.1), this will translate to absolute coordinates of (30, 20) in pixels. If the container is resized to 400 x 400 pixels, the relative position (0.1, 0.1) would become (40, 40).

Anchor positions are specified as x and y in the bounds, and are made absolute by including the characters x or y, respectively, in the absolute string field. Width and height are specified as w and h, and are made absolute by including the characters w or h, respectively, in the absolute string field.

If bounds are not specified for a sub-shape, the sub-shape's bounds will default to the anchor point in the top left, and the bounds fills its container.

Example bounds

Top Left
bounds: { x: 5 y: 5 w: 5 h: 5 anchor: 'top-left' // All coordinates are absolute. // "absolute: true" is equivalent. absolute: 'xywh' }

Top Left

Center
bounds: { x: 0.5 y: 0.5 w: 5 h: 5 anchor: 'center' // Width and Height are absolute absolute: 'wh' }

Center

Left
bounds: { x: 0, y: 0.5, w: 0.5, h: 0.5, anchor: 'left', // None of the properties are absolute. // - "absolute: ''" is equivalent. // - No absolute field is also equivalent. absolute: false }

Left

Bottom Right
bounds: { x: 1 y: 1 w: 25 h: 25 anchor: 'bottom-right' absolute: 'wh' }

Bottom Right

Center
bounds: { x: 0.5 y: 0.5 w: 0.9 h: 0.9 anchor: 'center' }

Center

Formulas

You can use formulas in your shape definition to conditionally display and/or repeat a geometry or sub-shape description.

You can apply formulas to field values in your shape definitions to make the values change dynamically with data. Documentation for the Lucid formula system can be found online on Lucid's help site Use formulas in Lucidchart or in the internal documentation.

In the custom shape format, formula expressions always start with =, such as in the example: =1 + 2.

Interpolated strings

You can add interpolated string values of the form {{=<formula>}} to some field values, which act like inline formulas. Lucidchart will replace these strings with the formula's calculated value when building the string. Interpolated strings with no interpolated values will be treated as normal string field values.

For example, when the string:

"Here is 1 + 2: {{=1 + 2}}"

is evaluated, it will produced the string:

"Here is 1 + 2: 3"

Shape data properties

Shape data can be defined on a custom shape to allow users to configure the data used by the custom shape. Shape data can be used in any formula within the shape, using the syntax @PropertyName. For more information about Lucidchart's formula system, see the formulas page.

Shape controls

Controls directly modify the linked dynamic properties of a shape – these can be used to modify the shapes properties or geometry. They use the location of the control (represented by ControlX and ControlY as built-in variables within shape controls) within the shape’s bounding box as the thing that powers these changes.

The shape controls only exist inside the Lucidchart editor, in the advanced shape editor preview, the controls will not render or be interactable.

Shape Controls Wide

For example, in the above shape, the left spatial control is about 0.3 (30%) of the width of the shape. If I move it to look like this:

Shape Controls Narrow

The built-in variables representing the location of the control become ControlX = 0.6, ControlY=0.4 which updates my dynamic properties to also have that value, causing the stencil to re-evaluate its geometry.

For all control locations, the points are relative to the shape’s bounding box (0 - 1). Absolute points are not supported at this time.

Control example
{ 'uri': 'control', 'schema': { 'type': 'object', 'required': ['location', 'onmove'], 'properties': { // all values here are relative to shape bounds 'location': {'x': {'$ref': 'numberFormula'}, 'y': {'$ref': 'numberFormula'}}, // defaults to shape bounding box if undefined 'constraint': { 'oneOf': [{'$ref': 'areaConstraint'}, {'$ref': 'pathConstraint'}], }, 'onmove': {'type': 'array', 'items': {'$ref': 'setPropertyAction'}}, }, }, }, { 'uri': 'pathConstraint', 'schema': { 'type': 'object', 'required': ['type', 'points'], 'properties': { 'type': {'enum': ['path']}, 'points': {'type': 'array', 'items': {'x': {'$ref': 'numberFormula'}, 'y': {'$ref': 'numberFormula'}}}, }, 'additionalProperties': false, }, }, { 'uri': 'areaConstraint', 'schema': { 'type': 'object', 'required': ['type', 'x', 'y', 'w', 'h'], 'properties': { 'type': {'enum': ['area']}, 'x': {'$ref': 'numberFormula'}, 'y': {'$ref': 'numberFormula'}, 'w': {'$ref': 'numberFormula'}, 'h': {'$ref': 'numberFormula'}, }, 'additionalProperties': false, }, }, { 'uri': 'formulaDefinition', 'schema': { 'type': 'object', 'required': ['name', 'value'], 'properties': { 'name': { 'type': 'string', }, 'value': {'$ref': 'formula'}, }, 'additionalProperties': false, }, },
Control Schema
Property Description
location This is where the control lies on the shape.
constraints This is where the control is allowed to be moved — essentially the set of points that are valid inputs for this control. If left undefined, defaults to the shape’s bounding box.
onmoved This is the set of actions to be done when the control is moved. Here is where you can utilize the built-in “ControlX” and “ControlY” variables that represent the new dropped location of the control.
Location
Property Description
x Number or Formula that determines the relative x location. Relative meaning 0 is the far left of the shapes bounding box, 1 is the far right.
y Number or Formula that determines the relative y location. Relative meaning 0 is the top border of the shapes bounding box, 1 is the bottom.
Constraint - Path
Property Description
type 'Path'
points The discrete points that the spatial control is allowed to move in. Essentially the values that ‘ControlX’ and ‘ControlY’ are allowed to be. The x and y values in all of these points are allowed to be numbers or formulas.
Constraint - Area
Property Description
type 'area'
x Number or Formula that determines the minimum x value that is an acceptable location for the given controls.
y Number or Formula that determines the minimum y value that is an acceptable location for the given controls.
w The width of the bounding box for the control (number or formula that evaluates to a number).
h The height of the bounding box for the control (number or formula that evaluates to a number).
Validation

Controls require that you have a dynamic property whose ‘name’ matches the ‘field’ of the SetPropertyAction.

Shape controls only have access to the localDefs on the stencil, the dynamic shapes on the stencil, and the built in ControlX and ControlY.

ShapeProperty schema

Property Description
name string
The internal name of the field, used in formulas.
label string or TranslatableString
The label displayed when the shape property is shown in the editor.
type number, string, color, date, boolean, array, object, formula
The type of data this field stores.
default boolean, number, string, formula
The default value for the field. This can be a formula or a constant value.
constraints [Constraint
Constraints which determine whether or not the property is valid.

Constraint schema

Property Description
condition formula
The condition checked for the constraint, which fails when this evaluates to false.
resolution number, string, formula
The value used to resolve this constraint when it fails.
message string or TranslatableString
The message shown to the user when the constraint fails.

Types

Property Type Description Example
boolean True or false. true
false
number A number value, including both integers and decimal values. 5
-3
3.14
string A text value. "ABC"
"123-456-7890"
color A color value, constructed using color functions or a hex color. "=RGB(255, 0, 0)"
"#ff0000"
date A date value, constructed using date functions. "=DATE(2019, 1, 1)"
picklist A value selected from a list of possible values, displayed in the editor as a picklist. The value must be one of the available values in the.picklist.
array Multiple data values supplied as an array. Can be constructed using array functions. "=ARRAY(1, 2, 3)"
object A collection of key-value pairs. Can be constructed using object functions. '=OBJECT("A", 1, "B", 2)'
formula Allows any formula value. "=1+2"

Constraints

Shape data properties can have an optional list of constraints which restrict the values of data allowed. For example, a percentage field could have a constraint where the value must be less than or equal to 100.

Constraints do not prevent saving shape data, but rather will indicate the value is invalid and allow an optional resolution value, which is used in place of the specified value. In the above example, if the percentage field exceeds 100, the resolution value might be 100 (i.e. if the value were to go above 100, then the resolution value (100) is used instead). If no resolution is provided, the shape will be shown as an error state.

Definitions

Definitions can be used to simplify complex calculations by storing data to be referenced in formulas. These definitions act like shape data properties that are not editable and are purely calculated using other values.

For example, if a shape data property named @Value was defined, and the square root of that property was caluvalted multiple times, a definition called @ValueSquareRoot could be created to simplify the formulas for the geometry that needed the square root of @Value. This usage prevents the need to constantly reference SQRT(@Value) in formulas and allows the formula to be changed in the future in a single spot, instead of multiple places.

Definitions can be defined in the top-level of the shape definition, making it available to all geometry and sub-shape, or within a sub-shape, which would be available to that sub-shape's geometry and further sub-shapes.

Definition

Property Description
name string
value formula

Types

Definitions must be defined with a data type, using one of the data types described in the shape data properties section above.

Scope and evaluation order

As definitions are created, they are available for use within the shape they are defined in or any sub-shapes below that shape. This is referred to as the scope of the variable, which describes where the variable may be used. The scope for sub-shapes in called a child scope or a lower scope, and the scope of the parent sub-shape (or top-level shape definition) is called a parent scope or a higher scope.

As an example, in the following figure there are 3 scopes (A, B, and C):

  • A is the top-level scope and the parent scope of B.
  • B is a child scope of A and a parent scope of C.
  • C is a only a child scope of B.
  • Scope B is a higher scope than scope C, and a lower scope than scope A.

Scopes

There are some exceptions to the general rule that definitions are available within the shape they are defined in, when working with sub-shapes. This is due to the order in which formulas are evaluated when generating the geometry to render the shape definition.

Formulas in a shape definition are evaluated in the following order:

  • Conditions - The sub-shapes condition is evaluated first and do not use the local definitions.
  • Repeat - Repeat fields do not use the local definitions. The repeat definitions (for index / value) are evaluated and added to a new local scope.
  • Definitions - Definitions are evaluated and added to the current local scope.
  • Bounds - Bounds are evaluated, using both repeat and definitions.
  • Text Areas - Text areas are evaluated, using both repeat and definitions.
  • Link Points - Link points are evaluated, using both repeat and definitions.
  • Geometry - Geometry are evaluated, using both repeat and definitions.
  • Sub-Shapes - Sub-shapes are evaluated using the same evaluation order (with the current scope becoming the new parent scope).

Built-in definitions

When using formulas in the custom shape format, there are some properties that are pre-filled with metadata about the shape. These can be used anywhere in the custom shape format and will always be available (unless overridden with a definition of the same name).

Definition Description
@Width The width of the shape in pixels.
@Height The height of the shape in pixels.

Textareas

Text can be displayed on the shape using text areas. Text areas are defined in either the shape definition or in sub-shapes, which fill the bounds in which it is defined. Sub-shapes containing only text can be defined to place text on a specific area of the overall shape.

Text areas only define the placement and rendering of text. If a border is needed for a text area, for example, additional geometry must be used.

Textarea schema

Property Description
name string
this must be unique
text string or formula
margins number or TextMargins
editable boolean: if true, properties of style cannot be formula
style TextStyle
align string
valign string

TextMargins schema

Property Description
top number
right number
bottom number
left number

TextStyle schema

Property Description
color string
or formula: if editable is false or not specified
size number
or formula: if editable is false or not specified
sizeUnits string: either pt or px. If not specified, then px is used.
bold boolean
or formula: if editable is false or not specified
underline boolean
or formula: if editable is false or not specified
italic boolean
or formula: if editable is false or not specified
font string
or formula: if editable is false or not specified

Clipping

In some cases, shape geometry may need to be clipped to fit to a specific outline. For example, a movie ranking shape (i.e. rating that uses stars) could be constructed using a rectangle shape that grows based on the value, which is then clipped to a shape consisting of the 5 stars.

Clipping for a shape is performed by taking all geometry created for the shape (both geometry in the shape and that shape's sub-shapes) and intersecting it with the clipping mask defined by the shape clipping settings.

Clipping schema

Property Description
geometry Geometry[]
stroke Stroke

Stroke

Clipping removes all geometry that falls outside of the clipping path, but sometimes a geometry that equals the clipping path is needed. For example, in the star rating example above, if you want a line to be placed around each star, you would need to add new geometry that matches the same path as the clip path. Instead, a stroke can be added to the clipping path which adds the extra geometry with the specific stroke settings without needing a separate copy of the geometry.

Property Description
color string or formula
pattern string or formula
width string or formula

Shape constraints

Shape constraints allow the shape to define limits that prevent the shape from going below a minimum size or above a maximum size. For both minimum and maximum size, both width and height are optional, so it is possible to have a shape that is limited in only one dimension in either direction or has different limits for height and width. Shape constraints also allow locking controls on the shape. The shapes horizontal or vertical resizing can be limited to prevent the shape from being resized in that dimension. Minimum size can be specified using a formula, which allows the minimum dimensions of the shape to be dynamically updated based on formula values. If resizing is also locked for that dimension, the shape's dimension will be fully dynamic. Lastly, you are able to alter the image that represents your shape in the shapes panel. This is useful if you have many shapes that look the same or have the same structure, but serve a different purpose.

ShapeDefinition schema

Property Description
minSize ConstraintSizeFormula
A constraint that limits the minimum size of the shape.
maxSize ConstraintSizeFormula
A constraint that limits the minimum size of the shape.
locked string[]
A list of fields to lock. Options are aspectRatio, horizontalResize, and verticalResize
thumbnail string (SVG)
An SVG that replaces the thumbnail image of the shape in the toolbar.

If you include aspectRatio in the locked list without having a default aspect ratio, the width and height will be used to calculate it.

ConstraintSizeFormula schema

Property Description
width number or formula
height number or formula

ConstraintSize schema

Property Description
width number
If present, constrains the width to this number
height number
If present, constrains the height to this number

Extension click handler

Property Description
type extensionHandler
Specifies the type of click handler.
packageId string
Package Id in which the action is registered in.
extensionName string
Name of the extension in which the action is registered in.
action string
Name of the action to be triggered on click.
parameters JSON object
A JSON object whose values can be of type boolean, null, number or Interpolated strings. The object will be passed to the action when the action is called.

Example Shapes

Rounded Progress Bar

Rounded Progress Bar
{ properties: [ { name: 'Min', label: 'Min', type: 'number', default: 1, constraints: [{ condition: '=@Min < @Max', message: 'Min constraint' }] } { name: 'Max', label: 'Max', type: 'number', default: 100, constraints: [{condition: '=@Max > @Min', message: 'Max constraint' }] } { name: 'Value', label: 'Value', type: 'number', default: 20, constraints: [ { condition: '=@Value >= @Min', resolution: '=@Min', message: 'Min value constraint' } { condition: '=@Value <= @Max', resolution: '=@Max', message: 'Max value constraint' } ] } { name: 'Foreground', label: 'Foreground', type: 'color', default: '=fillColor()' }, { name: 'Background', label: 'Background', type: 'color', default: '#D7E9FF' } ] defs: [ { name: 'Rounded', type: 'number', value: '=@Height / @Width' } ] clip: { geometry: [ { type: 'union' geometry: [ { type: 'ellipse', w: '=@Rounded' } { type: 'rect', x: '=@Rounded / 2', w: '=1 - @Rounded' } { type: 'ellipse', x: '=1 - @Rounded', w: '=@Rounded' } ] } ] } shapes: [ // Background { style: { fill: { type: 'color', color: '=@Background' } } geometry: [{ type: 'rect' }] } // Progress { style: { fill: { type: 'color', color: '=@Foreground' } } geometry: [ { type:'rect', w: '=(@Value - @Min) / (@Max - @Min)' } ] } ] }

The rounded progress bar defines a shape which represents a progress bar with rounded edges showing a current value in a range defined by the user as shape data. This example uses clipping to ensure that the path doesn't warp when resized and to simplify the geometry used in the progress bar.

image

Signal Strength

Signal Strength
{ properties: [ { name: 'Min', label: 'Min', type: 'number', default: 0, constraints: [{ condition: '=@Min < @Max', message: 'Max must be larger than Min' }] }, { name: 'Max', label: 'Max', type: 'number', default: 100, constraints: [{ condition: '=@Max > @Min', message: 'Max must be larger than Min' }] }, { name: 'Value', label: 'Value', type: 'number', default: 70, constraints: [ { condition: '=@Value >= @Min', resolution: '=@Min', message: 'Value must be greater or equal to Min' }, { condition: '=@Value <= @Max', resolution: '=@Max', message: 'Value must be less or equal to Max' } ], }, { name: 'Foreground', label: 'Foreground', type: 'color', default: '=fillColor()' }, { name: 'Background', label: 'Background', type: 'color', default: '#D7E9FF' } ], shapes: [ { shapes: [ // Outer Path { style: {fill: {type: 'color', color: '=@Background'}} geometry: [{type: 'rect'}] } // Inner Path { style: {fill: {type: 'color', color: '=@Foreground'}} geometry: [{type: 'rect', w: '=(@Value - @Min) / (@Max - @Min)'}] } ] } ] clip: { geometry: [{ type: 'intersection', geometry: [ // Triangle Path {type: 'path', path: 'M 0,1 L 1,0 L 1,1 Z'} { type: 'union' geometry: [ // Bars {type: 'rect', x: 0, w: 0.233}, {type: 'rect', x: 0.256, w: 0.233}, {type: 'rect', x: 0.512, w: 0.233}, {type: 'rect', x: 0.767, w: 0.233}, ] } ] }] } }

Shows a progress bar shape in the style of a signal strength meter. The existing shape is continuous, however the shape could be modified to round to the nearest 0.5 if needed.

image

Star Rating

Star Rating
{ properties: [ { name: 'Min', label: 'Min', type: 'number', default: 0 } { name: 'Max', label: 'Max', type: 'number', default: 100 } { name: 'Value', label: 'Value', type: 'number', default: 20 constraints: [ { condition: '=@Value >= @Min', resolution: '=@Min', message: 'Too low!' } { condition: '=@Value <= @Max', resolution: '=@Max', message: 'Too high!' } ] } { name: 'Foreground', label: 'Foreground', type: 'color', default: '=fillColor()' } { name: 'Background', label: 'Background', type: 'color', default: '#D7E9FF' } ], templates: [ { name: 'stars' geometry: [ { type: union geometry: [ { type: 'polygon', n: 5, inset: 0.4, x: 0.0, y: 0, w: 0.2, h: 1 } { type: 'polygon', n: 5, inset: 0.4, x: 0.2, y: 0, w: 0.2, h: 1 } { type: 'polygon', n: 5, inset: 0.4, x: 0.4, y: 0, w: 0.2, h: 1 } { type: 'polygon', n: 5, inset: 0.4, x: 0.6, y: 0, w: 0.2, h: 1 } { type: 'polygon', n: 5, inset: 0.4, x: 0.8, y: 0, w: 0.2, h: 1 } ] }, ] } ], style: { fill: { type: 'color', color: '=@Background' } }, // Draw the background geometry: [ { type: 'template', template: 'stars' } ] shapes: [ // Draw the foreground { style: { fill: { type: 'color', color: '=@Foreground' } }, geometry: [ { type: intersection geometry: [ { type: 'template', template: 'stars' } { type: 'rect', w: '=(@Value - @Min) / (@Max - @Min)' } ] } ], }, // Outline the stars (using the foreground color darkened by 20%). { style: { fill: { type: 'color', color: '#00000000' }, stroke: { color: '=DARKEN(@Foreground, 20%)', width: 1 }, }, geometry: [ { type: 'template', template: 'stars' } ] } ] }

The star rating defines a shape which represents a progress bar as a star rating (in the vein of reviews). The progress bar is continuous and clips to the geometry of the stars. A discrete version could be implemented which rounds the input to the nearest 0.5 to make it map to whole or half stars.

image

Multiplexer Shape

Multiplexer Shape
{ properties: [ { name: 'In', label: 'In', type: 'number', default: 5, constraints: [ {condition: '=@In > 0', resolution: 1, message: 'Must be greater than 0.'} ] }, { name: 'Out', label: 'Out', type: 'number', default: 3, constraints: [ {condition: '=@Out > 0', resolution: 1, message: 'Must be greater than 0.'} ] }, ], style: { fill: {type: 'color', color: '#f5f5f5'}, stroke: {color: '#999999', width: 2}, }, shapes: [ { geometry: [{type: 'rect' }] } { repeat: {type: 'for', index: 'i', min: 1, max: '=@In', step: 1} bounds: {x: '=@i / (@In + 1)', y: 0, w: '=1 / (@In + 1)', h: 0.2, anchor: 'top'} style: { fill: {type: 'color', color: '#cccccc'} stroke: {color: '#999999', width: 2} } geometry: [{type: 'rect'}] linkpoints: [ {x: 0.5, y: 0} ] } { repeat: {type: 'for', index: 'i', min: 1, max: '=@Out', step: 1} bounds: {x: '=@i / (@Out + 1)', y: 1, w: '=1 / (@Out + 1)', h: 0.2, anchor: 'bottom'} style: { fill: {type: 'color', color: '#cccccc'} stroke: {color: '#999999', width: 2} } geometry: [{type: 'rect'}] linkpoints: [ {x: 0.5, y: 1} ] } ] }

The multiplexer defines a shape that has a variable number of link points on the top and bottom, which are controlled by two shape data fields (In and Out). Link points are created using repeated shapes.

image

Center-Growing Progress Bar

Center-Growing Progress Bar
{ properties: [ { name: 'PositiveColor', label: 'Positive Color', type: 'color', default: '#03adfc' }, { name: 'NegativeColor', label: 'Negative Color', type: 'color', default: '#fc0362' }, { name: 'Min', label: 'Minimum', type: 'number', default: -100 }, { name: 'Max', label: 'Maximum', type: 'number', default: 100 }, { name: 'Value', label: 'Value', type: 'number', default: 50 }, ], templates: [ { name: 'bar' geometry: [ { type: 'rect', x: 0.5, w: '=@Value / (@Max - @Min)' }, ], } ], shapes: [ { style: { fill: { type: 'color', color: '=fillColor()' } stroke: { color: '=DARKEN(fillColor(), 20%)', width: 1 }, }, geometry: [ { type: 'rect' }, { type: 'path', path: 'M 0.5 0 L 0.5 1'} ], } { style: { fill: { type: 'color', color: '=IF(@Value < 0, @NegativeColor, @PositiveColor)' } }, geometry: [ { type: 'template' template: 'bar' }, ], }, ], }

image

Greeting Shape

Controls
{ "properties": [ { "name": "Name", "label": "Name", type: "string", "default": "You" } ], "style": { "fill": { "type": "color", "color": "=fillColor" }, "stroke": { "color": "#000000", "width": 2 } }, "shapes": [ { "geometry": [ { "type": "rect" } ], "textarea": { "name": "t0", "text": "Hello, {{=@Name}}!", "editable": true, "style": { "color": "#000000" } } } ] }

The greeting shape is a simple example of a textarea on a shape. The shape is a simple rectangle with a fill of the current fill color, a black border, and a textarea that fills the shape.

image

Shape with controls

Controls Example
{ "geometry": [ { "type": "difference", "geometry": [ { "type": "rect" }, { "type": "rect", "x": "=@left", "y": 0, "w": "=@right - @left", "h": "=@center" } ] } ], "properties": [ { "name": "left", "type": "number", "label": "left width control", "default": 0.3 }, { "name": "center", "type": "number", "label": "center height control", "default": 0.75 }, { "name": "right", "type": "number", "label": "right width control", "default": 0.7 } ], "controls": [ { "location": { "x": "=@left", "y": "=@center/2" }, "onmove": [ { "type": "set", "field": "left", "formula": "=@ControlX" } ] }, { "location": { "x": "=@right", "y": "=@center/2" }, "onmove": [ { "type": "set", "field": "right", "formula": "=@ControlX" } ] }, { "location": { "x": "=@left + (@right-@left)/2", "y": "=@center" }, "onmove": [ { "type": "set", "field": "center", "formula": "=@ControlY" } ] } ] }

Control example

Appendices

Definitions

Term Definition
LCSZ The file format this document describes. LCSZ is intended to mean Lucid Custom Shape ZIP.
Shape Library A collection of shape definitions, images, text translations and shape library entries stored in a ZIP file format that can be uploaded to Lucidchart as a new shape library.
Shape Library Entry A shape shown to a user in the list of shapes in a shape library (in Lucidchart). Each shape library entry is composed of a shape definition and a list of settings for that shape library entry; each entry can be a unique set of default properties using a single shape definition.
Shape Definition A file that defines the structure of a shape, properties associated to that shape and interactions. A shape definition specifies how shapes are drawn; a shape entry matches up a shape definition with it's default properties and settings.
Shape An entity which includes scoped definitions, geometries for rendering, and a group of subshapes.
Rendering a shape renders both geometries and subshapes; the order field defines whether geometries or shapes are rendered on top or not.
Sub-Shape A shape contained within another shape, which can have its own render style, bounds and definitions. Sub-shapes can be contained within the shape itself, or within another sub-shape.
Geometry A path defined within a shape. Helpers like rect and ellipse give the user easier tools to add standard geometry to a shape.
Top-Level Geometry The geometry defined on shape definition itself and not in sub-shapes.
Template A collection of geometry that can be reused multiple times using a set of passed in parameters.
i18n Internationalization. Provides translations for text labels used by shapes and the shape library. Not yet implemented.
Clipping Mask A clipping mask defines the bounds of the shape; no geometry should be present outside of the clipping mask. At a technical level, all generated shape geometry is intersected with the clipping mask to produce the final geometry.
Link point A connection point that is shown when users are dragging a line to connect with the shape to allow the line to snap to a specific point.
Textarea A block of optionally editable text displayed for a shape. Textareas are positioned relative to the current shapes offset.