Skip to content

TicFrame v5.7.0-alpha.1 is available - read the changelog.

TicFrame

Components

Modal

TicFrame modals are native dialog elements styled and enhanced by the framework. Use them for blocking decisions, focused forms, destructive confirmations, and short flows that need the user’s full attention.

Extensive application-style modal patterns now live in Modal patterns. This reference page focuses on the component contract, core variants, source code, and API.

How It Works

A modal opens from a trigger with data-tf-toggle="modal" and data-tf-target, or from TicFrame.Modal.show(). TicFrame updates optional content slots, calls the native dialog open path, locks background scrolling, focuses the first useful control, and restores focus to the opener when the dialog closes.

Lifecycle

Open, close, static-backdrop, and update events use the tf:modal:* namespace. Dismiss buttons use data-tf-dismiss="modal".

Native dialog

The component uses <dialog>, including the platform backdrop and Escape behavior. TicFrame adds tokens, layout variants, media cleanup, and predictable data APIs.

Stacking

Avoid placing modals inside transformed or clipped containers. Modal-to-modal triggers close the current dialog before opening the next one.

Motion

Use tf-modal-fade for motion and tf-modal-no-animation when the dialog should appear immediately. Reduced-motion users receive an immediate transition.

Anatomy

The rendered anatomy below is a static documentation preview, not an overlay. A live modal uses the same structure inside a dialog.tf-modal.

Modal title

The body contains the decision, form, or supporting content.

HTML
<dialog id="settings-modal" class="tf-modal tf-modal-md" aria-labelledby="settings-modal-title" aria-describedby="settings-modal-description">
  <div class="tf-modal-header">
    <h2 id="settings-modal-title" class="tf-modal-title">Modal title</h2>
    <button type="button" class="tf-btn tf-btn-icon tf-btn-ghost" data-tf-dismiss="modal" aria-label="Close modal">
      <i class="ti ti-028069" aria-hidden="true"></i>
    </button>
  </div>
  <div id="settings-modal-description" class="tf-modal-body">Modal body text goes here.</div>
  <div class="tf-modal-footer">
    <button type="button" class="tf-btn" data-tf-dismiss="modal">Cancel</button>
    <button type="button" class="tf-btn tf-btn-primary" data-tf-dismiss="modal">Save changes</button>
  </div>
</dialog>

Live Examples

Each example includes the rendered trigger and complete source needed to reproduce it.

Basic live modal

Open a modal, close it from the header, footer, backdrop, or Escape key.

Save workspace

Review your changes before returning to the documentation.

HTML
<button type="button" class="tf-btn tf-btn-primary" data-tf-toggle="modal" data-tf-target="#demo-modal">Open modal</button>
<dialog id="demo-modal" class="tf-modal tf-modal-md" aria-labelledby="demo-modal-title" aria-describedby="demo-modal-description">
  <div class="tf-modal-header">
    <h2 id="demo-modal-title" class="tf-modal-title">Save workspace</h2>
    <button type="button" class="tf-btn tf-btn-icon tf-btn-ghost" data-tf-dismiss="modal" aria-label="Close modal"><i class="ti ti-028069" aria-hidden="true"></i></button>
  </div>
  <div id="demo-modal-description" class="tf-modal-body">Review your changes before continuing.</div>
  <div class="tf-modal-footer"><button type="button" class="tf-btn" data-tf-dismiss="modal">Cancel</button><button type="button" class="tf-btn tf-btn-primary" data-tf-dismiss="modal">Save changes</button></div>
</dialog>

Static backdrop and keyboard lock

Use these options when the modal needs an explicit decision.

Complete required step

Backdrop clicks dispatch tf:modal:static and keep the dialog open.

Keyboard dismissal disabled

Escape is prevented for this modal. Provide an explicit close or completion action.

HTML
<dialog class="tf-modal tf-modal-md" data-tf-modal-backdrop="static" data-tf-modal-keyboard="false" aria-labelledby="required-step-title">
  <div class="tf-modal-header"><h2 id="required-step-title" class="tf-modal-title">Complete required step</h2></div>
  <div class="tf-modal-body">Backdrop clicks and Escape are blocked until the user chooses an explicit action.</div>
  <div class="tf-modal-footer"><button type="button" class="tf-btn tf-btn-primary" data-tf-dismiss="modal">I understand</button></div>
</dialog>

Scrolling and centered variants

Long content can scroll as a whole surface, or only the modal body can scroll while header and footer stay visible.

Long content

Use the base modal when the entire dialog can scroll as one surface.

This paragraph adds height for the scroll boundary.

Viewport constraints keep the modal inside the screen.

Actions remain after the content in source order.

Scrollable body

Use tf-modal-scrollable for terms, long forms, or review screens.

The body scrolls independently.

The footer stays visible.

This variant remains hidden while closed.

Centered modal

Add tf-modal-centered for vertical centering.

Centered scrollable

Combine the centered and scrollable classes when both behaviors are needed.

Header and footer stay pinned.

The middle row scrolls.

HTML
<dialog class="tf-modal tf-modal-md tf-modal-scrollable" aria-labelledby="review-title">...</dialog>
<dialog class="tf-modal tf-modal-md tf-modal-centered" aria-labelledby="centered-title">...</dialog>
<dialog class="tf-modal tf-modal-md tf-modal-centered tf-modal-scrollable" aria-labelledby="centered-scroll-title">...</dialog>

Rich and dynamic content

Tooltips, popovers, grids, varying trigger content, and modal-to-modal transitions work inside dialogs.

Floating content

Grid content

Column one
Column two
Column three

Message recipient

Choose a recipient to update this copy.

First modal

Open the second modal from this dialog.

Second modal

TicFrame closes the current modal before opening the target.

HTML
<button data-tf-toggle="modal" data-tf-target="#varying-modal" data-tf-modal-title="Message Ada" data-tf-modal-body="Compose a note for Ada.">Message Ada</button>
<dialog id="varying-modal" class="tf-modal tf-modal-md" aria-labelledby="varying-modal-title">
  <h2 id="varying-modal-title" data-tf-modal-slot="title" class="tf-modal-title">Message recipient</h2>
  <p data-tf-modal-slot="body">Choose a recipient to update this copy.</p>
</dialog>

Animation, media cleanup, sizes, and fullscreen

Use explicit classes for motion, sizing, and responsive fullscreen behavior.

Fade animation

Uses tokenized modal animation duration and easing.

No animation

Appears immediately.

Dynamic height

Media modal

On close, TicFrame pauses media and resets embedded frames when data-tf-modal-reset-media is present.

18.75rem max width.

Uses the base --tf-modal-inline-size token.

31.25rem max width.

50rem max width.

71.25rem max width.

Always fullscreen

Covers the viewport at every breakpoint.

Fullscreen sm down

Fullscreen below the small breakpoint.

Fullscreen md down

Fullscreen below the medium breakpoint.

Fullscreen lg down

Fullscreen below the large breakpoint.

Fullscreen xl down

Fullscreen below the extra-large breakpoint.

Fullscreen 2xl down

TicFrame uses 2xl naming and keeps xxl as a migration alias.

HTML and JS
<dialog class="tf-modal tf-modal-fade">...</dialog>
<dialog class="tf-modal tf-modal-no-animation">...</dialog>
<dialog class="tf-modal tf-modal-lg" data-tf-modal-reset-media>...</dialog>
<dialog class="tf-modal tf-modal-sm">...</dialog>
<dialog class="tf-modal tf-modal-md">...</dialog>
<dialog class="tf-modal tf-modal-lg">...</dialog>
<dialog class="tf-modal tf-modal-xl">...</dialog>
<dialog class="tf-modal tf-modal-fullscreen-md-down">...</dialog>
<script>TicFrame.Modal.update("#dynamic-height-modal");</script>

Representative Patterns

Use these small examples as common starting points. The complete 19-pattern gallery is available in Modal patterns.

Publish changes?

Your updates will be visible to everyone with access.

Delete release?

This removes the draft from the release queue.

Invite teammate

Sign in

HTML
<dialog class="tf-modal tf-modal-md tf-modal-center" aria-labelledby="confirm-title">
  <div class="tf-modal-hero">
    <span class="tf-modal-icon tf-modal-icon-success"><i class="ti ti-004292" aria-hidden="true"></i></span>
    <h2 id="confirm-title" class="tf-text-2xl tf-font-semibold">Publish changes?</h2>
    <p class="tf-text-muted">Your updates will be visible to everyone with access.</p>
    <div class="tf-flex tf-justify-center tf-gap-3"><button type="button" class="tf-btn" data-tf-dismiss="modal">Review</button><button type="button" class="tf-btn tf-btn-primary" data-tf-dismiss="modal">Publish</button></div>
  </div>
</dialog>

Accessibility

RequirementTicFrame guidance
NameUse aria-labelledby pointing at the modal title, or provide an equivalent accessible name.
DescriptionUse aria-describedby for important explanatory copy.
Close controlIcon-only close buttons must include an aria-label. Use Ticons UID classes for the visible icon.
KeyboardEscape closes normal modals. Static or keyboard-disabled modals must provide a visible action.
FocusTicFrame focuses the first useful control and restores focus to the opening trigger on close.
Destructive actionsPlace the safe action first and use autofocus on it when the risk is high.
Motion and contrastAnimations honor reduced motion. Tokens inherit dark, RTL, and high-contrast theme behavior.

API Reference

Structure and classes

ClassPurpose
tf-modalRequired dialog surface.
tf-modal-header, tf-modal-body, tf-modal-footerOptional structure regions.
tf-modal-titleTitle styling for the labelled heading.
tf-modal-centered, tf-modal-scrollableVertical centering and body-only scrolling.
tf-modal-fade, tf-modal-no-animationAnimation variants.
tf-modal-sm, tf-modal-md, tf-modal-lg, tf-modal-xlExplicit modal widths.
tf-modal-fullscreen*Always or responsive fullscreen behavior.

Options

PropertyData attributeTypeDefaultDescription
backdropdata-tf-modal-backdrop"dismiss" | "static""dismiss"Controls backdrop click behavior.
keyboarddata-tf-modal-keyboardbooleantrueSet to false to prevent Escape dismissal.
titledata-tf-modal-titlestringnullTrigger-provided text for title slots.
bodydata-tf-modal-bodystringnullTrigger-provided text for body slots.
resetMediadata-tf-modal-reset-mediabooleanfalsePauses audio/video and reloads iframes on close.

Methods

MethodArgumentsReturnTiming
TicFrame.Modal.show(target, trigger?)Selector or dialog, optional triggerDialog or nullSynchronous open request.
TicFrame.Modal.hide(target)Selector or dialogDialog or nullSynchronous close request.
TicFrame.Modal.toggle(target, trigger?)Selector or dialog, optional triggerDialog or nullSynchronous open/close request.
TicFrame.Modal.update(target)Selector or dialogDialog or nullDispatches update for dynamic content.

Events

EventWhen it firesTargetDetailCancelable
tf:modal:openAfter TicFrame opens a dialog.Dialog{ component, modal, trigger }No
tf:modal:closeAfter the dialog closes.Dialog{ component, modal }No
tf:modal:staticWhen backdrop/Escape dismissal is refused.Dialog{ component, modal }No
tf:modal:updateWhen update() is called.Dialog{ component, modal }No

Sizes and Fullscreen

NameClassMaximum widthRecommended use
Defaulttf-modal--tf-modal-inline-size, 36rem by defaultGeneral dialogs.
Smalltf-modal-sm18.75remShort confirmations.
Mediumtf-modal-md31.25remForms and focused decisions.
Largetf-modal-lg50remTables, grids, media previews.
Extra largetf-modal-xl71.25remWide application flows.

Fullscreen variants

ClassBreakpointBehavior
tf-modal-fullscreenAllAlways fills the viewport.
tf-modal-fullscreen-sm-downSmall and belowFullscreen on small screens.
tf-modal-fullscreen-md-downMedium and belowFullscreen on medium screens and smaller.
tf-modal-fullscreen-lg-downLarge and belowFullscreen on large screens and smaller.
tf-modal-fullscreen-xl-downExtra-large and belowFullscreen below the xl boundary.
tf-modal-fullscreen-2xl-down2xl and belowFullscreen below the widest breakpoint.
tf-modal-fullscreen-xxl-downAliasMigration alias for teams using xxl wording.

CSS Variables

TokenDefaultPurpose
--tf-modal-inline-size36remBase modal width.
--tf-modal-max-inline-size92vwViewport width cap.
--tf-modal-max-block-sizecalc(100dvb - var(--tf-space-8))Viewport height cap.
--tf-modal-paddingvar(--tf-space-6)Body and structural padding.
--tf-modal-gapvar(--tf-space-3)Header/footer item gap.
--tf-modal-bordervar(--tf-border-default)Surface border.
--tf-modal-radiusvar(--tf-radius-6)Surface radius.
--tf-modal-shadowvar(--tf-shadow-2)Surface shadow.
--tf-modal-backdroprgb(15 23 42 / 0.62)Native backdrop color.
--tf-modal-animation-durationvar(--tf-duration-200)Fade duration.
--tf-modal-animation-easingvar(--tf-ease-standard)Fade easing.

Migration Mapping

Use this table when translating a modal from another framework into TicFrame naming. Rebuild the markup with TicFrame classes rather than copying the original implementation.

Common patternTicFrame equivalent
Modal rootdialog.tf-modal
Header/body/footer regionstf-modal-header, tf-modal-body, tf-modal-footer
Titletf-modal-title plus aria-labelledby
Static backdropdata-tf-modal-backdrop="static"
Keyboard disableddata-tf-modal-keyboard="false"
Centeredtf-modal-centered
Scrollable bodytf-modal-scrollable
Responsive fullscreentf-modal-fullscreen-*-down

Troubleshooting

SymptomFix
Modal appears behind contentKeep the dialog near the end of the document and out of transformed, clipped, or isolated stacking contexts.
Title is not announcedPoint aria-labelledby at the visible title or provide an accessible name.
Media keeps playing after closeAdd data-tf-modal-reset-media.
Dynamic content changes the sizeCall TicFrame.Modal.update(target) after injecting custom content.
Closed scrollable modal is visibleThe framework rule dialog.tf-modal:not([open]) keeps all closed dialog variants hidden.