Skip to content

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

TicFrame

Components

Offcanvas

Offcanvas is the trigger/target/backdrop behavior underneath any slide-in panel — it doesn't require a particular visual treatment. Drawer is that behavior paired with the tf-drawer/tf-sheet styling; this page documents the behavior itself, so it also works with a panel you style yourself.

How It Works

A trigger with data-tf-toggle="offcanvas" and data-tf-target opens any element by ID — the target doesn't need a specific class. Opening inserts a .tf-offcanvas-backdrop, locks background scroll, moves focus into the panel, and traps Tab inside it. Closing removes the backdrop, unlocks scroll, and restores focus to whichever element had it before the panel opened.

Keyboard model

Escape closes the panel. Tab and Shift+Tab cycle only through focusable elements inside it — focus can't escape to the page behind while it's open.

Dismissal

Any element inside the panel with data-tf-dismiss="offcanvas" closes it, in addition to Escape and clicking the backdrop.

Programmatic control

TicFrame.Offcanvas.show/hide/toggle(target) drive a panel from script — useful for opening one from a keyboard shortcut or another flow with no rendered trigger button.

Content-agnostic

The target can be any element — give it your own layout classes, or reach for tf-drawer/tf-sheet for the ready-made side/bottom panel treatment.

Live Examples

Basic panel (no styling opinion)

Offcanvas doesn't require tf-drawer. Here the target is a plain <aside> styled with ordinary utilities, still getting the full backdrop/focus-trap/Escape contract.

HTML
<button type="button" class="tf-btn tf-btn-primary" data-tf-toggle="offcanvas" data-tf-target="#offcanvas-basic">Open panel</button>
<aside id="offcanvas-basic" aria-label="Basic offcanvas panel" class="tf-fixed tf-inset-block-0 tf-end-0 tf-w-64 tf-max-w-full tf-bg-surface-raised tf-shadow-2 tf-p-6"
       style="z-index: var(--tf-z-modal); inline-size: min(20rem, 90vw);">
  <button type="button" class="tf-btn tf-btn-icon tf-btn-ghost" data-tf-dismiss="offcanvas" aria-label="Close panel"><i class="ti ti-028069" aria-hidden="true"></i></button>
  <p>A plain element, no tf-drawer class -- the behavior doesn't care.</p>
</aside>

Multiple independent triggers

Any number of triggers can point at the same or different panels — each registers independently.

HTML
<button type="button" class="tf-btn" data-tf-toggle="offcanvas" data-tf-target="#offcanvas-shared">Open from button A</button>
<button type="button" class="tf-btn" data-tf-toggle="offcanvas" data-tf-target="#offcanvas-shared">Open from button B</button>
<aside id="offcanvas-shared" class="tf-drawer" aria-label="Shared offcanvas panel">...</aside>

Programmatic API

The same panel, driven entirely from TicFrame.Offcanvas instead of a data-tf-toggle trigger.

JS
TicFrame.Offcanvas.show("#offcanvas-basic");
TicFrame.Offcanvas.hide("#offcanvas-basic");
TicFrame.Offcanvas.toggle("#offcanvas-basic");

Accessibility

RequirementTicFrame guidance
RoleTicFrame sets role="dialog" and aria-modal="true" on the target automatically.
NameAdd aria-label or aria-labelledby on the target — TicFrame doesn't infer one.
FocusThe first focusable element inside the panel receives focus on open; focus returns to the trigger (or whichever element had it) on close.
Focus trapTab and Shift+Tab cycle only within the panel while it's open, so keyboard users can't tab into obscured background content.
KeyboardEscape always closes the panel — there's no keyboard-disable option, unlike Modal's static backdrop.
Icon-only dismissGive an icon-only close button its own aria-label; the icon itself stays aria-hidden.

API Reference

Attributes

AttributeWherePurpose
data-tf-toggle="offcanvas"TriggerMarks the element as an offcanvas trigger.
data-tf-targetTriggerSelector for the panel to open (required).
data-tf-dismiss="offcanvas"Any element inside the panelCloses the panel on click.

Methods

MethodArgumentsReturn
TicFrame.Offcanvas.show(target)Selector or elementPanel element or null if already open or not found.
TicFrame.Offcanvas.hide(target)Selector or elementPanel element or null if already closed or not found.
TicFrame.Offcanvas.toggle(target)Selector or elementPanel element or null if not found.

Events

EventWhen it firesTarget
tf:offcanvas:openAfter the panel opens.Panel
tf:offcanvas:closeAfter the panel closes.Panel

CSS Variables

TokenDefaultPurpose
--tf-z-modal1300Panel stacking order (the panel is given this z-index directly when you build your own, as in the basic example above).
--tf-z-modal - 11299Backdrop stacking order — always one layer below the panel.

Troubleshooting

SymptomFix
Clicking the trigger does nothingdata-tf-target must match an element that exists in the DOM at TicFrame.init() time — check the selector and that the panel isn't rendered later by other script.
Backdrop stays after the panel is removed by other scriptCall TicFrame.Offcanvas.hide(panel) before removing the panel from the DOM, rather than removing it directly.
Panel appears behind other contentGive a custom (non-tf-drawer) panel z-index: var(--tf-z-modal) directly, as shown in the basic example.
Focus trap conflicts with a nested dialogDon't open a Modal from inside an offcanvas panel without closing the panel first — two independent focus traps will fight over Tab.