Behaviors

XMLUI provides a simple mechanism for attaching a specific behavior to components using attributes, just as you set properties, layout settings, and events for a component. For example, with the tooltip attribute, you can display a tooltip to every visible component:

<App>
    <Button label="Hover the mouse over me!" tooltip="I'm hovered!" />
    <Card title="Tooltip with markdown" tooltip="I'm hovered too!" />
    <Avatar name="XML UI" size="md" tooltip="I am an Avatar!"/>
</App>
Example: using the tooltip behavior
<App>
    <Button label="Hover the mouse over me!" tooltip="I'm hovered!" />
    <Card title="Tooltip with markdown" tooltip="I'm hovered too!" />
    <Avatar name="XML UI" size="md" tooltip="I am an Avatar!"/>
</App>

Another attribute, animation, can be used to attach animations to components. Moreover, it can add options to the applied animation:

<App>
  <Stack 
    width="4em" 
    height="4em" 
    backgroundColor="lightcoral" 
    animation="zoomin"
    animationOptions="{{ duration: 1800, reverse: true, loop: true }}"
    />
</App>
Example: using the animation behavior
<App>
  <Stack 
    width="4em" 
    height="4em" 
    backgroundColor="lightcoral" 
    animation="zoomin"
    animationOptions="{{ duration: 1800, reverse: true, loop: true }}"
    />
</App>

Using a behavior

When you add an attribute to a component that XMLUI recognizes as a "trigger" attribute for a behavior, the framework checks whether the behavior can decorate the component. If so, the behavior is applied; otherwise, the framework ignores it.

For example, the tooltip behavior cannot leverage non-visual components.

The following table summarizes the currently supported XMLUI behaviors.

BehaviorDescriptionTrigger Properties
AnimationApplies visual animations to components with configurable duration, timing, and looping options.animation
BookmarkAdds navigation anchors and table of contents integration to visual components.bookmark
FormBindingBinds input components directly to form data without requiring a FormItem wrapper.bindTo
LabelWraps components with a label element for form inputs and other labeled content.label
TooltipDisplays informational text or markdown content when hovering over visual components.tooltip, tooltipMarkdown
ValidationProvides validation logic and feedback for form input components.bindTo (with validation props)
VariantApplies custom theme-aware styling for non-predefined variant values.variant

Animation

With this behavior, you can add visual animations to components with configurable duration, timing, and looping options.

Trigger Properties

NameDescription
animationThe name of a predefined animation to apply

You can use these built-in animations: fadein, fadeout, slidein, slideout, popin, popout, flipin, flipout, rotatein, rotateout, zoomin, and zoomout.

Additional Properties

NameDescription
animationOptionsThe animation provides several options that influence its behavior and appearance. You can set this property to define these options.

These are the display options you can use with an animation:

  • animateWhenInView: Indicates whether the animation should start when the component is in view.
  • delay: The delay before the animation starts in milliseconds.
  • duration: The duration of the animation in milliseconds.
  • loop: Indicates whether the animation should loop (default: false).
  • once: Indicates whether the animation should only run once (default: false).
  • reverse: Indicates whether the animation should run in reverse after the normal animation completes.

You can define animationOptions as a string or as an object. In the latter case, the object declares name and value pairs describing the visual options:

<App>
  <Stack 
    width="24em" 
    height="4em" 
    backgroundColor="lightcoral" 
    animation="slidein"
    animationOptions="{{ duration: 2000, delay: 200 }}"
    />
</App>
Example: animationOptions as an object
<App>
  <Stack 
    width="24em" 
    height="4em" 
    backgroundColor="lightcoral" 
    animation="slidein"
    animationOptions="{{ duration: 2000, delay: 200 }}"
    />
</App>

The string form of animationOptions is composed of names or name and value pairs separated by semicolons. The properties that allow enumerations (such as side or align) can be set with a name representing a single value. Properties with boolean values can use the property name to represent the true value, or the property name prefixed with an exclamation mark to signify a false value. Numeric values are separated from the property name by a colon, and they do not use units. Here are a few examples:

<App>
  <Stack 
    width="24em" 
    height="4em" 
    backgroundColor="lightcoral" 
    animation="slidein"
    animationOptions="animateWhenInView; duration: 2000;"
    />
</App>
Example: animationOptions as a string
<App>
  <Stack 
    width="24em" 
    height="4em" 
    backgroundColor="lightcoral" 
    animation="slidein"
    animationOptions="animateWhenInView; duration: 2000;"
    />
</App>

Bookmark

You can add bookmark functionality to any visual component. Using the specified identifier, you can scroll the particular component into view.

Trigger Properties

NameDescription
bookmarkThe ID of the bookmark to create for this component

Additional Properties

NameDescription
bookmarkLevelThe heading level for this bookmark (used in table of contents)
bookmarkOmitFromTocWhether to omit this bookmark from the table of contents
bookmarkTitleThe title to use for this bookmark in the table of contents. Defaults to the component's text content.

Here is a sample with links that scroll a particular component into view using bookmark:

<App layout="vertical-full-header">
  <NavPanel padding="$padding-normal">
    <Link to="/#red">Jump to red</Link>
    <Link to="/#green">Jump to green</Link>
    <Link to="/#blue">Jump to blue</Link>
  </NavPanel>
  <VStack>
    <VStack bookmark="red" height="400px" backgroundColor="red" />
    <VStack bookmark="green" height="400px" backgroundColor="green" />
    <VStack bookmark="blue" height="400px" backgroundColor="blue" />
  </VStack>
</App>
Example: bookmark
<App layout="vertical-full-header">
  <NavPanel padding="$padding-normal">
    <Link to="/#red">Jump to red</Link>
    <Link to="/#green">Jump to green</Link>
    <Link to="/#blue">Jump to blue</Link>
  </NavPanel>
  <VStack>
    <VStack bookmark="red" height="400px" backgroundColor="red" />
    <VStack bookmark="green" height="400px" backgroundColor="green" />
    <VStack bookmark="blue" height="400px" backgroundColor="blue" />
  </VStack>
</App>

FormBinding

This behavior can put any components that handle input values directly to the XMLUI Form infrastructure, without requiring a FormItem wrapper. Any component that has a value API to retrieve its value and a setValue method to update it to a new value can be seamlessly integrated into the Form infrastructure.

Trigger Properties

NameDescription
bindToThe name of the form field to bind this input component to

Additional Properties

NameDescription
initialValueThe initial value for this form field when the form is first rendered
noSubmitWhether to exclude this field's value from form submission

The TextBox and TextArea components support the value and setValue APIs, so they can be used in the Form infrastructure:

<App>
  <Form 
    data="{{ 
      firstname: 'Albert', 
      lastname: 'Einstein', 
      comments: 'e=mc^2'
    }}"
    onSubmit="d => toast.success(JSON.stringify(d, null, 2))">
    <FlowLayout >
      <TextBox label="Firstname" bindTo="firstname" width="50%"/>
      <TextBox label="Lastname" bindTo="lastname" width="50%" />
    </FlowLayout>
    <TextArea label="Comments" bindTo="comments" />
  </Form>
</App>
Example: bindTo
<App>
  <Form 
    data="{{ 
      firstname: 'Albert', 
      lastname: 'Einstein', 
      comments: 'e=mc^2'
    }}"
    onSubmit="d => toast.success(JSON.stringify(d, null, 2))">
    <FlowLayout >
      <TextBox label="Firstname" bindTo="firstname" width="50%"/>
      <TextBox label="Lastname" bindTo="lastname" width="50%" />
    </FlowLayout>
    <TextArea label="Comments" bindTo="comments" />
  </Form>
</App>

Label

You can add a label to components using the label property. Though labels are primarily used for input components, with the label property, you can add labels to any visual component.

Trigger Properties

NameDescription
labelThe text to display as the label for the component

Additional Properties

NameDescription
enabledWhether the component should indicate that it is enabled
labelBreakWhether the label should break onto a new line
labelPositionThe position of the label relative to the input component ("top", "start", "end", or "bottom")
labelWidthThe width of the label
readonlyWhether the component should indicate that it is read-only
requiredWhether the component should indicate that it is required
shrinkToLabelWhether the component should shrink to fit the label
<App>
  <Avatar name="My Avatar" label="An avatar" labelPosition="start" />
  <Badge value="Primary" label="Use this badge:" required />
  <Icon name="home" 
    label="Welcome home!" 
    labelPosition="end"
    width="fit-content"
  />
</App>
Example: label
<App>
  <Avatar name="My Avatar" label="An avatar" labelPosition="start" />
  <Badge value="Primary" label="Use this badge:" required />
  <Icon name="home" 
    label="Welcome home!" 
    labelPosition="end"
    width="fit-content"
  />
</App>

Tooltip

This behavior displays informational text or markdown content when hovering over visual components.

Trigger Properties

NameDescription
tooltipPlain text to be displayed as the tooltip for the host component
tooltipMarkdownMarkdown text to be displayed as the tooltip for the host component

Additional Properties

NameDescription
tooltipOptionsThe tooltip provides several options that influence its behavior and appearance. You can set this property to define these options.

These are the display options you can use with a tooltip:

  • delayDuration: The duration from when the mouse enters a tooltip trigger until the tooltip opens (in ms).
  • skipDelayDuration: How much time a user has to enter another trigger without incurring a delay again (in ms).
  • defaultOpen: The open state of the tooltip when it is initially rendered (default: false). Default: 700.
  • showArrow: Whether to show the arrow pointing to the trigger element (default: false). Default: 300.
  • side: The preferred side of the trigger to render against when open ("top", "right", "bottom", or "left"). Default: "top".
  • align: The preferred alignment against the host component ("start", "center", or "end"). Default: "center".
  • sideOffset: The distance in pixels from the trigger (default: 4)
  • alignOffset: An offset in pixels from the "start" or "end" alignment options (default: 0).
  • avoidCollisions: When true, overrides the side and align preferences to prevent collisions with boundary edges (default: true).

You can define tooltipOptions as a string or as an object. In the latter case, the object declares name and value pairs describing the visual options:

<App>
  <CHStack height="100px" verticalAlignment="center" >
    <Button
      label="Hover the mouse over me!"
      tooltip="Use an object"
      tooltipOptions="{{ showArrow: false, side: 'bottom', align: 'start' }}"
    >
    </Button>
  </CHStack>
</App>
Example: tooltipOptions as an object
<App>
  <CHStack height="100px" verticalAlignment="center" >
    <Button
      label="Hover the mouse over me!"
      tooltip="Use an object"
      tooltipOptions="{{ showArrow: false, side: 'bottom', align: 'start' }}"
    >
    </Button>
  </CHStack>
</App>

The string form of tooltipOptions is composed of names or name and value pairs separated by semicolons. The properties that allow enumerations (such as side or align) can be set with a name representing a single value. Properties with boolean values can use the property name to represent the true value, or the property name prefixed with an exclamation mark to signify a false value. Numeric values are separated from the property name by a colon, and they do not use units. Here are a few examples:

<App>
  <VStack height="100px" horizontalAlignment="center" gap="3rem">
    <Card
      title="Tooltip to the left with 800ms delay"
      tooltip="I'm a Tooltip"
      tooltipOptions="left; delayDuration: 800; !showArrow" />
    <HStack>
      <Icon
        name="email"
        width="48px"
        height="48px"
        tooltipMarkdown="**Tooltip** to the bottom with no arrows, aligned left"
        tooltipOptions="bottom; !showArrow; start" />
      <Icon
        name="phone"
        width="48px"
        height="48px"
        tooltipMarkdown="*Tooltip* to the bottom with arrows, 28 pixels away"
        tooltipOptions="bottom; showArrow; sideOffset: 28" />
    </HStack>
  </VStack>
</App>
Example: tooltipOptions as a string
<App>
  <VStack height="100px" horizontalAlignment="center" gap="3rem">
    <Card
      title="Tooltip to the left with 800ms delay"
      tooltip="I'm a Tooltip"
      tooltipOptions="left; delayDuration: 800; !showArrow" />
    <HStack>
      <Icon
        name="email"
        width="48px"
        height="48px"
        tooltipMarkdown="**Tooltip** to the bottom with no arrows, aligned left"
        tooltipOptions="bottom; !showArrow; start" />
      <Icon
        name="phone"
        width="48px"
        height="48px"
        tooltipMarkdown="*Tooltip* to the bottom with arrows, 28 pixels away"
        tooltipOptions="bottom; showArrow; sideOffset: 28" />
    </HStack>
  </VStack>
</App>

Validation

You can add validation functionality to input components with validation-related properties to integrate any component with the XMLUI Form infrastructure. Any component that has a value API to retrieve its value and a setValue method to set a new value can be seamlessly integrated into the Form infrastructure.

Trigger Properties

NameDescription
bindToThe name of the form field to bind this input component to

Additional Properties

NameDescription
lengthInvalidMessageCustom error message to display when input length is invalid
lengthInvalidSeveritySeverity level for length validation errors (e.g., 'error', 'warning', 'info')
maxLengthMaximum length for string inputs
minLengthMinimum length for string inputs
minValueMinimum value for number inputs
maxValueMaximum value for number inputs
patternPredefined pattern name for input validation
patternInvalidMessageCustom error message to display when input does not match the pattern
patternInvalidSeveritySeverity level for pattern validation errors (e.g., 'error', 'warning', 'info')
rangeInvalidMessageCustom error message to display when input value is out of range
rangeInvalidSeveritySeverity level for range validation errors (e.g., 'error', 'warning', 'info')
regexRegex pattern for input validation
regexInvalidMessageCustom error message to display when input does not match the regex
regexInvalidSeveritySeverity level for regex validation errors (e.g., 'error', 'warning', 'info')
requiredWhether this field is required
validationModeThe mode to use for validating the input (e.g., 'onChange', 'onBlur', 'onSubmit')
verboseValidationFeedbackWhether to display verbose validation feedback (e.g., show all validation errors instead of just the first one)

TBD: Add vaildation example

Variant

This behavior applies custom variant styling to components with a variant property. The value of this property names a set of theme variables that you can use to override the default theme of the component.

Trigger Properties

NameDescription
variantThe variant value to apply

You can declare your variant-bound theme variables following this naming convention:

<propertyName>-<part-or-aspect>-<ComponentId>-<variantName>--<state>

XMLUI will apply theme variables where the variantName section is replaced with the value of the variant property assigned to your component. The following example defines a few custom variants for Card:

<App>
  <Theme 
    backgroundColor-Card-fancy="lightyellow"
    backgroundColor-Card-fancy--hover="orange"
    border-Card-fancy="4px dotted purple"
    borderRadius-Card-fancy="20px"

    backgroundColor-Card-rigid="$color-surface-200"
    border-Card-rigid="2px solid $color-primary"
    borderRadius-Card-rigid="0"
  >
    <Card title="Default Card" />
    <Card title="Fancy Card #1 (hover me!)" variant="fancy" />
    <Card title="Fancy Card #2 (hover me!)" variant="fancy" />
    <Card title="Rigid Card #1" variant="rigid" />
    <Card title="Card with non-existing variant" variant="non-existing" />
  </Theme>
</App>
Example: Card variants
<App>
  <Theme 
    backgroundColor-Card-fancy="lightyellow"
    backgroundColor-Card-fancy--hover="orange"
    border-Card-fancy="4px dotted purple"
    borderRadius-Card-fancy="20px"

    backgroundColor-Card-rigid="$color-surface-200"
    border-Card-rigid="2px solid $color-primary"
    borderRadius-Card-rigid="0"
  >
    <Card title="Default Card" />
    <Card title="Fancy Card #1 (hover me!)" variant="fancy" />
    <Card title="Fancy Card #2 (hover me!)" variant="fancy" />
    <Card title="Rigid Card #1" variant="rigid" />
    <Card title="Card with non-existing variant" variant="non-existing" />
  </Theme>
</App>