Breaking: #107443 - Migrate Modal component from Bootstrap to native dialog 

See forge#107443

Description 

The TYPO3 Modal component has been migrated from Bootstrap's modal implementation to use the native HTML <dialog> element. This change improves accessibility, reduces bundle size, and eliminates dependency on Bootstrap's JavaScript for modal functionality.

As part of this migration, Bootstrap's native modal events (such as show.bs.modal, shown.bs.modal, hide.bs.modal, and hidden.bs.modal) are no longer dispatched. Additionally, direct Bootstrap modal API usage (such as new Modal() from Bootstrap or $(element).modal()) is no longer supported.

Impact 

Bootstrap modal events ( *.bs.modal) are no longer available. Extensions that listen to these events must migrate to TYPO3's custom modal events.

The modal now uses the native <dialog> element with updated CSS classes and structure. Direct manipulation of Bootstrap modal APIs will no longer work.

Extensions using data-bs-toggle="modal", data-bs-content="..." or data-bs-target attributes to trigger modals will need to migrate to TYPO3's Modal API.

Affected installations 

All installations with custom extensions that:

  • Listen to Bootstrap modal events ( show.bs.modal, shown.bs.modal, hide.bs.modal, hidden.bs.modal)
  • Use Bootstrap's modal JavaScript API directly ( new bootstrap.Modal())
  • Use jQuery to control modals ( $(element).modal('show'))
  • Use data-bs-toggle="modal" attributes to trigger modals
  • Use data-bs-content="..." attributes to set modal contents
  • Manipulate modal DOM structure or classes expecting Bootstrap's markup

Migration 

Event Migration 

Replace Bootstrap modal event listeners with TYPO3's custom modal events. Note that event listeners must be attached to the modal instance returned by the Modal API, not queried from the DOM:

Before:

const modalElement = document.querySelector('.modal');
modalElement.addEventListener('show.bs.modal', (event) => {
  console.log('Modal is about to be shown');
});
modalElement.addEventListener('shown.bs.modal', (event) => {
  console.log('Modal is now visible');
});
modalElement.addEventListener('hide.bs.modal', (event) => {
  console.log('Modal is about to be hidden');
});
modalElement.addEventListener('hidden.bs.modal', (event) => {
  console.log('Modal is now hidden');
});
Copied!

After:

import Modal from '@typo3/backend/modal';
import Severity from '@typo3/backend/severity';

const modal = Modal.show(
  'My Modal Title',
  'This is the modal content',
  Severity.info
);

// Attach event listeners to the modal instance
modal.addEventListener('typo3-modal-show', (event) => {
  console.log('Modal is about to be shown');
});
modal.addEventListener('typo3-modal-shown', (event) => {
  console.log('Modal is now visible');
});
modal.addEventListener('typo3-modal-hide', (event) => {
  console.log('Modal is about to be hidden');
});
modal.addEventListener('typo3-modal-hidden', (event) => {
  console.log('Modal is now hidden');
});
Copied!

Bootstrap API Migration 

Replace Bootstrap modal API calls with TYPO3's Modal API. Do not attempt to instantiate Bootstrap modals directly or manipulate modal DOM elements.

Before:

import { Modal } from 'bootstrap';

const modalElement = document.querySelector('.modal');
const bsModal = new Modal(modalElement);
bsModal.show();
bsModal.hide();
Copied!

After:

import Modal from '@typo3/backend/modal';
import Severity from '@typo3/backend/severity';

// Show a simple modal
Modal.show(
  'My Modal Title',
  'This is the modal content',
  Severity.info,
  [
    {
      text: 'Close',
      btnClass: 'btn-default',
      trigger: (event, modal) => modal.hideModal()
    }
  ]
);

// Dismiss the current modal
Modal.dismiss();
Copied!

Data Attribute Migration 

Replace Bootstrap's data-bs-toggle and data-bs-target attributes with TYPO3's Modal trigger API:

Before:

<button type="button"
        data-bs-toggle="modal"
        data-bs-target="#myModal"
        data-bs-content="Are you sure?">
  Open Modal
</button>
Copied!

After:

<button type="button"
        class="t3js-modal-trigger"
        data-title="Confirmation"
        data-content="Are you sure?"
        data-severity="warning"
        data-button-close-text="Cancel"
        data-button-ok-text="Confirm">
  Open Modal
</button>
Copied!

Or use the JavaScript API directly:

import Modal from '@typo3/backend/modal';

document.querySelector('button').addEventListener('click', (event) => {
  Modal.confirm(
    'Confirmation',
    'Are you sure?',
    Severity.warning
  );
});
Copied!