Modal

Modals are overlays used to display content on a layer above the page, preventing users from interacting with content behind the modal.


React Component

Variants

Basic

Basic modals contain a title, a body of content, and a footer with buttons.

Modal title

Add a description of the content you're showing in the modal.


Destructive

Contain a title, a body of content, and a footer with buttons. The call to action button in the footer contain error styling to indicate that the action is destructive.

Delete item

Are you sure you want to delete this item? This action cannot be undone.


Header Icon

Modals can contain an icon in the header.

Modal header

This is subheader copy for your viewing pleasure

Add a description of the content you're showing in the modal.


Header Image

Modals can contain a thumbnail image in the header.

Modal header

This is subheader copy for your viewing pleasure

Add a description of the content you're showing in the modal.


Subheader

Modals can contain a subheader.

Modal header

This is subheader copy for your viewing pleasure

Add a description of the content you're showing in the modal.


Footer

Modals contain a footer that can contain buttons and links

Modal Header

Add a description of the content you're showing in the modal.


Footer Aside

The footer aside is a section of the footer that can contain additional buttons or links.

Modal Header

Add a description of the content you're showing in the modal.


Scrollable Content

Modals can be configured to allow scrolling of content. This option is useful when the header and footer needs to visible contain actions.

Modal header

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.


Sizes

Modal widths can be configured to be extra small, small, medium, or large.

Modal title

Add a description of the content you're showing in the modal.

Modal title

Add a description of the content you're showing in the modal.

Modal title

Add a description of the content you're showing in the modal.

Modal title

Add a description of the content you're showing in the modal.


Fullscreen

Modals can be configured to take up the full screen.

Modal header

Add a description of the content you're showing in the modal.


Animated

Modals can be configured to animate in from the top, left, or right.

Modal header

Add a description of the content you're showing in the modal.

Modal header

Add a description of the content you're showing in the modal.

Modal header

Add a description of the content you're showing in the modal.

Modal header

Add a description of the content you're showing in the modal.


Asynchronous Modal Setup

  1. Use Case:
    • Load modals asynchronously, ideal for scenarios like editing product details in a list.
  2. Setup:
    • Include a SageModal stub in the view with an ID and configurations.
    • Utilize remove_content_on_close: true for best practices.
    • Add default content (e.g., SageLoader) in the SageModal stub.
    • Create an endpoint delivering modal content (wrapped by SageModalContent).
  3. Trigger Configuration:
    • In the main view, add clickable triggers (e.g., buttons).
    • Configure triggers with:
      • data-js-modaltrigger="[modal-id]": ID of the SageModal stub.
      • data-js-remote-url="[url]": Endpoint delivering the content.
  4. Remote URLs:
    • Set remote URLs on both the main SageModal stub and triggers.
    • Trigger's remote_url overrides the stub's, but stub's is used if trigger has none.
    • Default content inside SageModal is replaced by asynchronous remote_url content.

Remote URLs

  • Modal A demonstrates remote_url prop usage on SageModal
  • Modal B and C demonstrate usage of the data-js-remote-url key within html_attributes. The value of the async_path() equals the all content in this path residing within SageModalContent
Loading...

Events (monitor in browser console)

Use these events for hooking into modal functionality using JS event listeners.

Event name Event type Description

sage.modal.active

global

Fires when any modal has been opened

sage.modal.closeAll

global

Fires when any modal has been closed

sage.modal.opening

instance Fires immediately when a modal has been opened

sage.modal.open

instance

Fires after a modal has loaded. If animate is enabled, this event will run upon completion of the modal's animation. Otherwise, this event will run immediately along with sage.modal.opening.

Example 'Active' Event Modal

Add a description of the content you're showing in the modal.

Example 'Open' Event Modal

Add a description of the content you're showing in the modal.

Example 'Opening' Event Modal

Add a description of the content you're showing in the modal.

Example 'CloseAll' Event Modal

Add a description of the content you're showing in the modal.

Sage Component

SageModal
<h2>Variants</h2>

<h3>Basic</h3>
Basic modals contain a title, a body of content, and a footer with buttons.
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Basic",
  attributes: {
    "data-js-modaltrigger": "cool-modal",
  }
} %>

<%= sage_component SageModal, { id: "cool-modal"} do %>
  <%= sage_component SageModalContent, {
    title: "Modal title",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Destructive</h3>
<p>Contain a title, a body of content, and a footer with buttons. The call to action button 
in the footer contain error styling to indicate that the action is destructive.</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Destructive",
  attributes: {
    "data-js-modaltrigger": "cool-modal-destructive",
  }
} %>

<%= sage_component SageModal, { id: "cool-modal-destructive" } do %>
  <%= sage_component SageModalContent, {
    title: "Delete item",
    help_content: "<p>Are you sure you want to delete this item? This action cannot be undone.</p>",
    help_link: {
      href: "#",
      name: "Link"
    }
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Are you sure you want to delete this item? This action cannot be undone.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "danger",
        icon: { name: "trash", style: "left" },
        value: "Delete",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Header Icon</h3>
<p>Modals can contain an icon in the header.</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Subheader",
  attributes: {
    "data-js-modaltrigger": "cool-modal-header-icon",
  }
} %>

<%= sage_component SageModal, {id: "cool-modal-header-icon" } do %>
  <%= sage_component SageModalContent, {
    header_icon: {
      name: "check-circle",
      color: "sage-400"
    },
    title: "Modal header",
    subheader: "This is subheader copy for your viewing pleasure",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

   <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Header Image</h3>
<p>Modals can contain a thumbnail image in the header.</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Subheader",
  attributes: {
    "data-js-modaltrigger": "cool-modal-header-image",
  }
} %>
<%= sage_component SageModal, {id: "cool-modal-header-image" } do %>
  <%= sage_component SageModalContent, {
    header_image: "avatar/joshua_wilson.png",
    title: "Modal header",
    subheader: "This is subheader copy for your viewing pleasure",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

   <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Subheader</h3>
<p>Modals can contain a subheader.</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Subheader",
  attributes: {
    "data-js-modaltrigger": "cool-modal-subheader",
  }
} %>

<%= sage_component SageModal, { allow_scroll: true, id: "cool-modal-subheader" } do %>
  <%= sage_component SageModalContent, {
    title: "Modal header",
    subheader: "This is subheader copy for your viewing pleasure",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

   <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Footer</h3>
<p>Modals contain a footer that can contain buttons and links</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Footer",
  attributes: {
    "data-js-modaltrigger": "cool-modal-footer",
  }
} %>

<%= sage_component SageModal, { id: "cool-modal-footer" } do %>
  <%= sage_component SageModalContent, {
    title: "Modal Header",
    help_content: "<p>Add a description of the content you're showing in the modal.</p>",
    help_link: {
      href: "#",
      name: "Link"
    }
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <% content_for :sage_footer_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Link",
          attributes: { "data-js-modal": true }
        } %>
      <% end %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Footer Aside</h3>
<p>The footer aside is a section of the footer that can contain additional buttons or links.</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Footer Aside",
  attributes: {
    "data-js-modaltrigger": "cool-modal-footer-aside",
  }
} %>

<%= sage_component SageModal, { id: "cool-modal-footer-aside" } do %>
  <%= sage_component SageModalContent, {
    title: "Modal Header",
    help_content: "<p>Add a description of the content you're showing in the modal.</p>",
    help_link: {
      href: "#",
      name: "Link"
    }
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <% content_for :sage_footer_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Link",
          attributes: { "data-js-modal": true }
        } %>
      <% end %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Scrollable Content</h3>
<p>Modals can be configured to allow scrolling of content. This option is useful when the header and footer needs to visible contain actions.</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Scrollable Content",
  attributes: {
    "data-js-modaltrigger": "cool-modal-scrolling",
  }
} %>

<%= sage_component SageModal, { allow_scroll: true, id: "cool-modal-scrolling" } do %>
  <%= sage_component SageModalContent, {
    title: "Modal header",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.</p>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.</p>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.</p>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna leo, condimentum nec pellentesque finibus, ultricies pulvinar ante. Donec eu interdum ligula. Pellentesque aliquam ullamcorper orci, nec tempor libero tristique in. Aliquam vitae felis at leo condimentum placerat eget id libero. In dictum tortor ac accumsan aliquam. Donec sit amet tortor porttitor, tincidunt nisl at, egestas lacus. Integer metus augue, aliquet accumsan vulputate eget, tristique id erat. Donec a venenatis nibh, ac molestie risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus in orci vitae ex tempor ultrices in a leo. Sed purus magna, vulputate aliquet ligula eget, consectetur sagittis nunc.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Sizes</h3>
<p>Modal widths can be configured to be extra small, small, medium, or large.</p>
<%= sage_component SageButtonGroup, { gap: :sm, spacer: { bottom: :sm }} do %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "Size xs",
    attributes: {
      "data-js-modaltrigger": "cool-modal-size-xs",
    }
  } %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "Size sm",
    attributes: {
      "data-js-modaltrigger": "cool-modal-size-sm",
    }
  } %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "Size md",
    attributes: {
      "data-js-modaltrigger": "cool-modal-size-md",
    }
  } %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "Size lg",
    attributes: {
      "data-js-modaltrigger": "cool-modal-size-lg",
    }
  } %>
<% end %>

<%= sage_component SageModal, { id: "cool-modal-size-xs", size: "xs"} do %>
  <%= sage_component SageModalContent, {
    title: "Modal title",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageModal, { id: "cool-modal-size-sm", size: "sm"} do %>
  <%= sage_component SageModalContent, {
    title: "Modal title",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageModal, { id: "cool-modal-size-md", size: "md"} do %>
  <%= sage_component SageModalContent, {
    title: "Modal title",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageModal, { id: "cool-modal-size-lg", size: "lg"} do %>
  <%= sage_component SageModalContent, {
    title: "Modal title",
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <%= sage_component SageButton, {
        style: "secondary",
        value: "Cancel",
      } %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Fullscreen</h3>
<p>Modals can be configured to take up the full screen.</p>
<%= sage_component SageButton, {
  style: "primary",
  icon: { name: "launch", style: "right" },
  value: "Fullscreen",
  attributes: {
    "data-js-modaltrigger": "cool-modal-fullscreen",
  }
} %>

<%= sage_component SageModal, {
  id: "cool-modal-fullscreen",
  fullscreen: true
} do %>
  <%= sage_component SageModalContent, {
    title: "Modal header"
  } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<h3>Animated</h3>
<p>Modals can be configured to animate in from the top, left, or right.</p>
<%= sage_component SageButtonGroup, { gap: :sm, spacer: { bottom: :sm }} do %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "Default",
    attributes: {
      "data-js-modaltrigger": "cool-modal-animate-default",
    }
  } %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "To top",
    attributes: {
      "data-js-modaltrigger": "cool-modal-animate-top",
    }
  } %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "To left (blur off)",
    attributes: {
      "data-js-modaltrigger": "cool-modal-animate-left",
    }
  } %>
  <%= sage_component SageButton, {
    style: "primary",
    icon: { name: "launch", style: "right" },
    value: "To right (blur off)",
    attributes: {
      "data-js-modaltrigger": "cool-modal-animate-right",
    }
  } %>
<% end %>

<%# Animated Modals %>
<%= sage_component SageModal, { id: "cool-modal-animate-default", animate: true, css_classes: "custom-class-name-here" } do %>
  <%= sage_component SageModalContent, { title: "Modal header" } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <% content_for :sage_footer_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Close Modal",
          attributes: { "data-js-modal": true }
        } %>
      <% end %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageModal, { id: "cool-modal-animate-top", animate: { direction: "top" } } do %>
  <%= sage_component SageModalContent, { title: "Modal header" } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <% content_for :sage_footer_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Link",
          attributes: { "data-js-modal": true }
        } %>
      <% end %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageModal, { id: "cool-modal-animate-left", disable_background_blur: true, animate: { direction: "left" } } do %>
  <%= sage_component SageModalContent, { title: "Modal header" } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <% content_for :sage_footer_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Link",
          attributes: { "data-js-modal": true }
        } %>
      <% end %>
      <%= sage_component SageButton, {
        style: "primary",
        value: "Confirm",
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageModal, { id: "cool-modal-animate-right", disable_background_blur: true, animate: { direction: "right" } } do %>
  <%= sage_component SageModalContent, { title: "Modal header" } do %>
    <% content_for :sage_header_aside do %>
      <%= sage_component SageButton, {
        style: "secondary",
        subtle: true,
        value: "Close Modal",
        icon: { name: "remove", style: "only" },
        attributes: { "data-js-modal": true }
      } %>
    <% end %>

    <p>Add a description of the content you're showing in the modal.</p>

    <% content_for :sage_footer do %>
      <% content_for :sage_footer_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Link",
          attributes: { "data-js-modal": true }
        } %>
      <% end %>
      <%= sage_component SageButton, {
        style: "primary",
        icon: { name: "check", style: "left" },
        value: "Confirm"
      } %>
    <% end %>
  <% end %>
<% end %>

<%= sage_component SageDivider, {} %>

<%= md("
<h2>Asynchronous Modal Setup</h2>

1. Use Case:
  - Load modals asynchronously, ideal for scenarios like editing product details in a list.
2. Setup:
  - Include a `SageModal` stub in the view with an ID and configurations.
  - Utilize `remove_content_on_close: true` for best practices.
  - Add default content (e.g., `SageLoader`) in the `SageModal` stub.
  - Create an endpoint delivering modal content (wrapped by `SageModalContent`).
3. Trigger Configuration:
  - In the main view, add clickable triggers (e.g., buttons).
  - Configure triggers with:
      - `data-js-modaltrigger=\"[modal-id]\"`: ID of the `SageModal` stub.
      - `data-js-remote-url=\"[url]\"`: Endpoint delivering the content.
4. Remote URLs:
  - Set remote URLs on both the main `SageModal` stub and triggers.
  - Trigger's `remote_url` overrides the stub's, but stub's is used if trigger has none.
  - Default content inside `SageModal` is replaced by asynchronous `remote_url` content.
", use_sage_type: true) %>

<h3>Remote URLs</h3>
<ul>
  <li>Modal A demonstrates <code>remote_url</code> prop usage on <code>SageModal</code></li>
  <li>Modal B and C demonstrate usage of the <code>data-js-remote-url</code> key within <code>html_attributes</code>.
  The value of the <code>async_path()</code> equals the all content in this path residing within <code>SageModalContent</code></li>
</ul>
<%= sage_component SageButtonGroup, { gap: :sm } do %>
  <%= sage_component SageButton, {
    html_attributes: {
      "data-js-modaltrigger": "test-modal",
    },
    style: "primary",
    value: "Open modal A",
  } %>
  <%= sage_component SageButton, {
    html_attributes: {
      "data-js-modaltrigger": "test-modal",
      "data-js-remote-url": async_path("modal-demo-2"),
    },
    style: "primary",
    value: "Open modal B",
  } %>
  <%= sage_component SageButton, {
    html_attributes: {
      "data-js-modaltrigger": "test-modal",
      "data-js-remote-url": async_path("modal-demo-3"),
    },
    style: "primary",
    value: "Open modal C",
  } %>
<% end %>

<%= sage_component SageModal, { id: "test-modal", remote_url: async_path("modal-demo"), remove_content_on_close: true, } do %>
  <%= sage_component SageModalContent, {} do %>
    <%= sage_component SageLoader, { type: "spinner" } %>
  <% end %>
<% end %>

<%= sage_component SagePanelStack, {} do %>
  <h3 class="<%= SageClassnames::TYPE::HEADING_6 %>">Events (monitor in browser console)</h3>
  <p>Use these events for hooking into modal functionality using JS event listeners.</p>

  <%= sage_component SageTable, {
    striped: true,
    responsive: true,
    headers: [
      "Event name",
      "Event type",
      "Description"
    ],
    rows: [
      {
        col_1: md("`sage.modal.active`"),
        col_2: "global",
        col_3: md("Fires when **any** modal has been opened")
      },
      {
        col_1: md("`sage.modal.closeAll`"),
        col_2: "global",
        col_3: md("Fires when **any** modal has been closed")
      },
      {
        col_1: md("`sage.modal.opening`"),
        col_2: "instance",
        col_3: "Fires immediately when a modal has been opened"
      },
      {
        col_1: md("`sage.modal.open`"),
        col_2: "instance",
        col_3: md("Fires after a modal has loaded. If `animate` is enabled, this event will run upon completion of the modal\'s animation. Otherwise, this event will run immediately along with `sage.modal.opening`.")
      },
    ]
  } %>

  <%= sage_component SageButtonGroup, { gap: :sm, spacer: { bottom: :sm }} do %>
    <%= sage_component SageButton, {
      style: "primary",
      icon: { name: "launch", style: "right" },
      value: "'sage.modal.active'",
      attributes: {
        "data-js-modaltrigger": "cool-modal-event-active",
      }
    } %>
    <%= sage_component SageButton, {
      style: "primary",
      icon: { name: "launch", style: "right" },
      value: "'sage.modal.opening'",
      attributes: {
        "data-js-modaltrigger": "cool-modal-event-opening",
      }
    } %>
    <%= sage_component SageButton, {
      style: "primary",
      icon: { name: "launch", style: "right" },
      value: "'sage.modal.open'",
      attributes: {
        "data-js-modaltrigger": "cool-modal-event-open",
      }
    } %>
  <% end %>

  <%# Event-enabled Modals %>
  <%= sage_component SageModal, { id: "cool-modal-event-active", disable_background_blur: true, animate: true } do %>
    <%= sage_component SageModalContent, { title: "Example 'Active' Event Modal" } do %>
      <% content_for :sage_header_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Close Modal",
          icon: { name: "remove", style: "only" },
          attributes: { "data-js-modal": true }
        } %>
      <% end %>

      <p>Add a description of the content you're showing in the modal.</p>

      <% content_for :sage_footer do %>
        <% content_for :sage_footer_aside do %>
          <%= sage_component SageButton, {
            style: "secondary",
            subtle: true,
            value: "Close Modal",
            attributes: { "data-js-modal": true }
          } %>
        <% end %>
        <%= sage_component SageButton, {
          style: "primary",
          icon: { name: "check", style: "left" },
          value: "Take An Action",
        } %>
      <% end %>
    <% end %>
  <% end %>

  <%= sage_component SageModal, { id: "cool-modal-event-open", disable_background_blur: true, animate: true, css_classes: "custom-class-name-here" } do %>
    <%= sage_component SageModalContent, { title: "Example 'Open' Event Modal" } do %>
      <% content_for :sage_header_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Close Modal",
          icon: { name: "remove", style: "only" },
          attributes: { "data-js-modal": true }
        } %>
      <% end %>

      <p>Add a description of the content you're showing in the modal.</p>

      <% content_for :sage_footer do %>
        <% content_for :sage_footer_aside do %>
          <%= sage_component SageButton, {
            style: "secondary",
            subtle: true,
            value: "Close Modal",
            attributes: { "data-js-modal": true }
          } %>
        <% end %>
        <%= sage_component SageButton, {
          style: "primary",
          icon: { name: "check", style: "left" },
          value: "Take An Action",
        } %>
      <% end %>
    <% end %>
  <% end %>

  <%= sage_component SageModal, { id: "cool-modal-event-opening", disable_background_blur: true, animate: true } do %>
    <%= sage_component SageModalContent, { title: "Example 'Opening' Event Modal" } do %>
      <% content_for :sage_header_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Close Modal",
          icon: { name: "remove", style: "only" },
          attributes: { "data-js-modal": true }
        } %>
      <% end %>

      <p>Add a description of the content you're showing in the modal.</p>

      <% content_for :sage_footer do %>
        <% content_for :sage_footer_aside do %>
          <%= sage_component SageButton, {
            style: "secondary",
            subtle: true,
            value: "Close Modal",
            attributes: { "data-js-modal": true }
          } %>
        <% end %>
        <%= sage_component SageButton, {
          style: "primary",
          icon: { name: "check", style: "left" },
          value: "Take An Action",
        } %>
      <% end %>
    <% end %>
  <% end %>

  <%= sage_component SageModal, { id: "cool-modal-event-close", disable_background_blur: true, animate: true } do %>
    <%= sage_component SageModalContent, { title: "Example 'CloseAll' Event Modal" } do %>
      <% content_for :sage_header_aside do %>
        <%= sage_component SageButton, {
          style: "secondary",
          subtle: true,
          value: "Close Modal",
          icon: { name: "remove", style: "only" },
          attributes: { "data-js-modal": true }
        } %>
      <% end %>

      <p>Add a description of the content you're showing in the modal.</p>

      <% content_for :sage_footer do %>
        <% content_for :sage_footer_aside do %>
          <%= sage_component SageButton, {
            style: "secondary",
            subtle: true,
            value: "Close Modal",
            attributes: { "data-js-modal": true }
          } %>
        <% end %>
        <%= sage_component SageButton, {
          style: "primary",
          icon: { name: "check", style: "left" },
          value: "Take An Action",
        } %>
      <% end %>
    <% end %>
  <% end %>

<% end %>

<script>
  (function() {
    const modalOpeningExample = document.querySelector("[data-js-modal=cool-modal-event-opening]");
    const modalOpenExample = document.querySelector("[data-js-modal=cool-modal-event-open]");

    document.addEventListener("sage.modal.active", function(e) {
      console.info("sage.modal.active global event fired", e);
    });
    modalOpeningExample.addEventListener("sage.modal.opening", function(e) {
      console.info("sage.modal.opening event fired", e);
    });
    modalOpenExample.addEventListener("sage.modal.open", function(e) {
      console.info("sage.modal.open event fired", e);
    });
    document.addEventListener("sage.modal.closeAll", function(e) {
      console.info("sage.modal.closeAll global event fired", e);
    });
  })();
</script>
Property Description Type Default

active

Enabling this property will return the JS early to not initialize any handlers.

Boolean

nil

animate

When true, in addition to the base fade transition assigned to all modals, the modal will animate into view while being displayed. Default direction of this animation runs from the top of the page down, and can be overridden using animate_direction. All animations are disabled when the user has "reduce motion" enabled in their OS or browser.

Boolean

nil

css_classes

Outputs additional CSS classes as specified.

String

nil

direction

Sets the direction of the animation when animate has been enabled. Options include "bottom" (default), "top", "left", and "right". Use caution when setting multiple direction modals within a page or view to ensure a consistent user experience. This option requires animate to be set to true.

String

nil

disable_background_blur

Disables the background blur filter, with a darkened background color. Recommended for increased animation performance when animate is enabled. This setting is ignored when "reduce motion" is enabled by the user's OS.

Boolean

nil

disable_background_dismiss

Enabling this property will return the JS early to not initialize any handlers.

Boolean

nil

disable_close

Enabling this property will return the JS early to not initialize any handlers.

Boolean

nil

fullscreen

Toggles whether to use the fullscreen variant of the modal by attaching .sage-modal--fullscreen

Boolean

nil

id

Unique identifier for component. Should match the data-js-modaltrigger property on the corresponding button

String

`nil

large

Toggles whether to use the large variant of the modal by attaching .sage-modal--large

Boolean

nil

remove_content_on_close

Toggles whether to delete the innerHTML when closing the modal.

Boolean

nil

remote_url

Specify a url which from which to load the modal content on open. This will be requested & content replaced every time the modal is opened.

String

nil

size

Presets a size for the modal.

"xs", "sm", "md", "lg"

nil

Modal Content

header_icon

When present, sets the icon with optional color in the header of the comoponent

    {
      color: SageSchemas::COLOR_SLIDER (optional)
      name: SageSchemas::ICON
    }

nil

help_content

Sets the content for the Popover component

String

nil

help_link

Array containing help link title and href for the subheader Popover

String

nil

help_title

Sets the title for the subheader Popover

String

nil

title

Populates the sage-modal__header with a heading containing the provided content

String

nil

spacing

Optionally enforces either a card- or panel-based spacing grid on the sage-modal__content.

panel or card

nil

subheader

Adds an optional subheader under the modal title.

String

nil

Section: header_aside

Populates the sage-modal__header-aside.

String

nil

Section: header_indicator

Populates the the page indicator in the header of standard modals.

String

nil

Section: header_progress_bar

Populates the header of a fullscreen modal with a progress bar.

String

nil

Section: footer

Populates the sage-modal__footer.

String

nil

Section: footer_aside

Populates the sage-modal__footer-aside for buttons or content set to the left of the panel footer.

String

nil

Events
Global/window events

sage.modal.active

This event is fired immediately when any modal has been opened.

sage.modal.closeAll

Fires on when a modal has been closed, either from actuating a button, esc keypress, or clicking a modal's backdrop.

Individual modal events

sage.modal.opening

This modal event is fired immediately when a modal has been opened.

sage.modal.open

Fired after a modal has completed its loading animation. Note that if animate has not been enabled, this event will fire at the same time as sage.modal.opening.

Sections

Element

sage_header_actions

When present, this will show containing SageButtons in the header of the component

SageButton

sage_header_aside

This area holds the button that closes the modal. This resides in the header of the component.

SageButton

sage_header_image

When present, sets the image in the header of the component

HTML image

sage_header_indicator

This area holds the page indicator for the modal. This is to be used on multi-page modals.

SageIndicator

sage_modal_custom_header

This can be used to provide custom header content, typically in lieue of the title or other header-related configurations for more tailored control of this area.

string/HTML

sage_footer

This area holds the cta buttons for the component.

SageButton

sage_footer_aside

This area holds the cancel or close modal button in the footer of the component.

SageButton

Do
  • Be sure that the <button>'s data-js-modaltrigger and the modal id match to establish the necessary link.
Don't
  • DO NOT use the remove_content_on_close property if your modal's content is not populated using JavaScript. The modal will not have content the next time it's triggered