Skip to content

Modal

KModal is a pop-up modal component with a background overlay.

NOTE

If your use-case matches one of the scenarios below, consider using KPrompt instead:

  • you need a simple pop-up dialog to ask user to confirm their action
  • the user performed an action and you first require them to provide input text
  • you don't need many customization options for a simple modal with action buttons
html
<KModal
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  Modal is a pop-up element that temporarily interrupts the user's interaction with the main content.
</KModal>

Props

visible

A boolean that defines whether the modal is shown.

vue
<template>
  <KButton @click="modalVisible = true">Modal</KButton>

  <KModal
    :visible="modalVisible"
    title="Modal"
    @cancel="handleModalClose"
    @proceed="handleModalClose"
  />
</template>

<script setup lang="ts">
const modalVisible = ref<boolean>(false)

const handleModalClose = () => {
  modalVisible.value = false
}
</script>

title

A string to be displayed as modal title. Can also be slotted.

html
<KModal
  title="Long modal title gets truncated with an ellipsis"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

actionButtonText

Text to be displayed in action button. Defaults to "Submit".

html
<KModal
  action-button-text="Ok"
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

actionButtonAppearance

Appearance of action button. See KButton appearance prop for more details. Defaults to primary.

html
<KModal
  action-button-appearance="danger"
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

actionButtonDisabled

By default the action button is enabled, but you can pass true to this prop should you want to change that.

html
<KModal
  :action-button-disabled="false"
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

cancelButtonText

Text to be displayed in cancel button. Defaults to "Cancel".

html
<KModal
  cancel-button-text="Leave"
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

cancelButtonAppearance

Appearance of cancel button. See KButton appearance prop for more details. Defaults to tertiary.

html
<KModal
  cancel-button-appearance="secondary"
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

cancelButtonDisabled

By default the cancel button is enabled, but you can pass true to this prop should you want to change that.

html
<KModal
  :cancel-button-disabled="false"
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

hideCancelButton

Use this prop to hide the cancel button. Defaults to false.

html
<KModal
  hide-cancel-button
  :visible="modalVisible"
  title="Modal"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

hideCloseIcon

Prop that allows you to hide close icon next to the title. Defaults to false. When no title is passed and hideCloseIcon is true, the modal header section will be hidden.

html
<KModal
  hide-close-icon
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  Modal header section is omitted when no header content is present.
</KModal>

maxWidth

Max width of the modal. Defaults to 500px.

html
<KModal
  max-width="90%"
  title="Modal"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

maxHeight

Maximum height of the content area. When content overflows, content area becomes scrollable. Default value is viewport height minus 200px (calc(100vh - 200px)).

html
<KModal
  max-height="200px"
  title="Modal"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  <p>Lorem ipsum dolor sit amet...
</KModal>

zIndex

z-index value for the modal. Defaults to 1100.

html
<KModal
  z-index="9999"
  title="Modal"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

fullScreen

Use this prop to make modal window take up almost the whole screen. When set to true, maxWidth and maxHeight props will have no effect.

html
<KModal
  full-screen
  title="Modal Full Screen"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
/>

closeOnBackdropClick

Whether clicking on backdrop should close the modal (by emitting the cancel event). Defaults to false.

html
<KModal
  :close-on-backdrop-click="false"
  hide-close-icon
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  Try clicking on modal backdrop.
</KModal>

closeOnEscape

Whether pressing the Escape key should close the modal (by emitting the cancel event). Defaults to true.

tabbableOptions

Options to be passed to focus-trap, which is responsible for trapping focus inside the modal box.

inputAutofocus

Boolean to control whether KModal should set focus on the first visible input field as soon as modal opens. Defaults to false.

html
<KModal
  input-autofocus
  title="Modal"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  <KInput
    label="Slotted input"
    :label-attributes="{ info: 'Focus trap set focus on this input field for you as soon as modal opened.' }"
    required
  />
</KModal>

NOTE

Sometimes you may need to asynchronously fetch data before displaying the form inside a modal. In that case, you can bind inputAutofocus to your local loading state variable in order to set inputAutofocus prop to true as soon as content loads so that KModal could set focus on input fields as soon as they are available.

vue
<template>
  <KModal
    :input-autofocus="!loading"
    title="Modal"
    :visible="modalVisible"
    @cancel="handleModalClose"
    @proceed="handleModalProceed"
  >
    <KInput
      v-if="!loading"
      label="Slotted input"
      :label-attributes="{ info: 'Focus trap set focus on this input field for you as soon as it became available.' }"
      required
    />
    <ProgressIcon v-else />
  </KModal>
</template>

<script setup lang="ts">
const modalVisible = ref<boolean>(false)
const loading = ref<boolean>(true)

watch(modalVisible, (newValue): void => {
  if (newValue) {
    setTimeout(() => {
      loading.value = false
    }, 3000)
  } else {
    loading.value = true
  }
})
</script>

Slots

default

Slot for modal content. Not to be confused with the content slot which takes presense over all other slots when provided.

html
<KModal
  title="Modal"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  <p>Lorem ipsum dolor sit amet...
</KModal>

title

Slot for title string.

html
<KModal
  title="Title"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  <template #title>
    Slotted title
  </template>
</KModal>

Slot for footer content.

html
<KModal
  title="Title"
  :visible="modalVisible"
  @cancel="handleModalClose"
  @proceed="handleModalProceed"
>
  <template #footer>
    All prices are in USD.
  </template>
</KModal>

Use this slot should you need to provide cusom buttons in modal footer. Ommited when footer slot is used.

vue
<template>
  <KModal
    :visible="modalVisible"
    title="Modal"
    @cancel="handleModalClose"
    @proceed="handleModalProceed"
  >
    <template #footer-actions>
      <KButton
        appearance="tertiary"
        @click="handleModalClose">
        <BackIcon />
        Back
      </KButton>
      <KButton @click="handleModalClose">
      Proceed
      <ForwardIcon />
      </KButton>
    </template>
  </KModal>
</template>

<script setup lang="ts">
const modalVisible = ref<boolean>(false)

const handleModalClose = () => {
  modalVisible.value = false
}
</script>

NOTE

KModal takes care of placement and spacing between the buttons when using footer-actions slot. However, should you want to mimic that container behaviour using, for example, footer slot, we recomend you use kui-space-40 design token or 8px for spacing between the buttons and place the container containing the buttons on the right using margin-left: auto; rule.

content

By default KModal provides you with the standard layout: modal header (optional), content section and footer. However, should you need to make a custom modal, you can use content slot for your content. It will provide you with just a wrapper container with white background.

IMPORTANT

Focus-trap requires at least one tabbable element to be present in modal at all times. You can learn more about what elements are considered tabbable here. Make sure your modal has at least one element with non-negative tabindex attribute (for example close icon with role="button" and tabindex="0" attributes).

vue
<template>
  <KModal :visible="modalVisible" @proceed="handleModalProceed">
    <template #content>
      <div class="custom-modal-content">
        <img src="/img/gateway-manager.png" alt="Gateway Manager" />
        <div class="info-container">
          <h3>Welcome to Gateway Manager!</h3>
          <p>Optimize Kong Gateway and Ingress Controller deployments across hybrid, multi-cloud, and Kubernetes environments. Get single-pane management, a 99.99% SLA, and seamless day-2 operations.</p>
          <KButton @click="modalVisible = false">Show me around</KButton>
        </div>
      </div>
    </template>
  </KModal>
</template>

<script setup lang="ts">
const modalVisible = ref<boolean>(false)
</script>

<style lang="scss" scoped>
.custom-modal-content {
  img {
    border-top-left-radius: $kui-border-radius-40;
    border-top-right-radius: $kui-border-radius-40;
  }

  .info-container {
    display: flex;
    flex-direction: column;
    gap: $kui-space-60;
    padding: $kui-space-80;
    text-align: center;

    h3, p {
      margin: 0;
    }

    p {
      color: $kui-color-text-neutral;
    }
  }
}
</style>

NOTE

By default when using content slot KModal comes with a wrapper container with no padding. However, should you want to customize that behaviour, we suggest you use kui-space-80 design token or 24px for any container padding and kui-border-radius-40 token or 8px for rounding the corners.

Events

proceed

Emitted when action button is clicked.

cancel

Emitted when cancel button or close icon (when not hidden) is clicked.

Released under the Apache-2.0 License.