Panel / Side Panels

Panel Layout

Let's look how to add Side Panels to our App. We may include up to 2 panels to our App, one on left side and another one on right side. We should add panels' HTML right in the beginning of the app root element (or <body> in case there is no root element in use):

<body>
  <!-- App root element -->
  <div id="app">
    <!-- Left panel -->
    <div class="panel panel-left">
        ... panel content goes here ...
    </div>

    <!-- Right panel -->
    <div class="panel panel-right">
        ... panel content goes here ...
    </div>

    ...
  </div>
</body>

After we added panels we need to choose opening effect for each panel. There could be one of the following effects:

If you want to use "Reveal" effect you should add additional panel-reveal class to Panel, panel-cover for cover effect, or panel-push for push effect:

<body>
  <!-- App root element -->
  <div id="app">
    <!-- Left panel, let it be with reveal effect -->
    <div class="panel panel-left panel-reveal">
        ... panel content goes here ...
    </div>

    <!-- Right panel, with cover effect -->
    <div class="panel panel-right panel-cover">
        ... panel content goes here ...
    </div>

    ...
  </div>
</body>

Each Panel can be resizable. To make Panel resizable we just need to add panel-resizable class to Panel element:

<body>
  <!-- App root element -->
  <div id="app">
    <!-- Make left panel resizable -->
    <div class="panel panel-left panel-resizable">
        ... panel content goes here ...
    </div>

    <!-- Make right panel resizable -->
    <div class="panel panel-right panel-resizable">
        ... panel content goes here ...
    </div>
    ...
  </div>
</body>

Panel App Methods

Let's look at related App methods to work with Panel:

app.panel.create(parameters)- create Panel instance

  • parameters - object. Object with panel parameters

Method returns created Panel's instance

app.panel.destroy(el)- destroy Panel instance

  • el - HTMLElement or string (with CSS Selector) or object. Panel element or Panel instance to destroy.

app.panel.get(el)- get Panel instance by HTML element

  • el - HTMLElement or string (with CSS Selector). Panel element.

Method returns Panel's instance

app.panel.open(panel, animate)- open panel

  • panel - HTMLElement or string (with CSS Selector) of panel element to open
  • animate - boolean. Should it be opened with animation or not. Optional, by default is true

app.panel.close(panel, animate)- close panel

  • panel - HTMLElement or string (with CSS Selector) of panel element to close
  • animate - boolean. Should it be closed with animation or not. Optional, by default is true

app.panel.toggle(panel, animate)- toggle panel

  • panel - HTMLElement or string (with CSS Selector) of panel element to toggle
  • animate - boolean. Should it be opened/closed with animation or not. Optional, by default is true

Panel Parameters

If we create Panel manually using app.panel.create method we need to pass object with panel parameters:

ParameterTypeDefaultDescription
elHTMLElement
string
Panel element
resizablebooleanEnables resizable panel. If not passed then will be determined based on panel-resizable class.
visibleBreakpointnumberMinimal app width (in px) when left panel becomes always visible
collapsedBreakpointnumberMinimal app width (in px) when left panel becomes partially visible (collapsed)
swipebooleanfalseEnable if you want to enable ability to open/close panel with swipe
swipeNoFollowbooleanfalseFallback option for potentially better performance on old/slow devices. If you enable it, then swipe panel will not follow your finger during touch move, it will be automatically opened/closed on swipe left/right.
swipeActiveAreanumber0Width (in px) of invisible edge from the screen that triggers panel swipe
swipeOnlyClosebooleanfalseThis parameter allows to close (but not open) panel with swipes. (swipe should be also enabled)
swipeThresholdnumber0Panel will not move with swipe if "touch distance" will be less than this value (in px).
backdropbooleantrueEnables Panel backdrop (dark semi transparent layer behind)
backdropElHTMLElement
string
HTML element or string CSS selector of custom backdrop element
closeByBackdropClickbooleantrueEnable/disable ability to close panel by clicking outside of panel
containerElHTMLElement
string
Allows to mount the panel to custom element rather than app root element
onobject

Object with events handlers. For example:

var panel = app.panel.create({
  el: '.panel-left',
  on: {
    opened: function () {
      console.log('Panel opened')
    }
  }
})

For example:

var panel = app.panel.create({
  el: '.panel-left',
  resizable: true,
  visibleBreakpoint: 1024,
  collapsedBreakpoint: 768,
})

Note that all following parameters can be used in global app parameters under panel property to set defaults for all panels. For example:

var app = new Framework7({
  panel: {
    swipe: true,
    visibleBreakpoint: 1024,
  }
});

Panel Methods & Properties

After we created Panel instance (by calling app.panel.create) or after we got Panel instance (by calling app.panel.get) we may use its useful methods and properties:

Properties
panel.appLink to global app instance
panel.sideString with panel side: left or right
panel.effectString with panel effect: cover, reveal, floating or push
panel.openedBoolean property indicating whether it is opened or not
panel.elPanel HTML element
panel.$elDom7 instance with panel HTML element
panel.backdropElBackdrop HTML element
panel.$backdropElDom7 instance with backdrop HTML element
panel.paramsPanel parameters
panel.containerElElement to mount panel to. (default app.el - root app element)
panel.$containerElDom7 instance with element to mount panel to. (default app.el - root app element)
Methods
panel.open(animate)Open panel. Where
  • animate - boolean (by default true) - defines whether it should be opened with animation or not
panel.close(animate)Close panel. Where
  • animate - boolean (by default true) - defines whether it should be closed with animation or not
panel.toggle(animate)Toggle panel. Where
  • animate - boolean (by default true) - defines whether it should be closed with animation or not
panel.enableVisibleBreakpoint()Enable visible breakpoint
panel.disableVisibleBreakpoint()Disable visible breakpoint
panel.toggleVisibleBreakpoint()Toggle visible breakpoint
panel.enableCollapsedBreakpoint()Enable collapsed breakpoint
panel.disableCollapsedBreakpoint()Disable collapsed breakpoint
panel.toggleCollapsedBreakpoint()Toggle collapsed breakpoint
panel.enableResizable()Enable resizable panel
panel.disableResizable()Disable resizable panel
panel.enableSwipe()Enable swipeable panel
panel.disableSwipe()Disable swipeable panel
panel.destroy()Destroy panel instance
panel.on(event, handler)Add event handler
panel.once(event, handler)Add event handler that will be removed after it was fired
panel.off(event, handler)Remove event handler
panel.off(event)Remove all handlers for specified event
panel.emit(event, ...args)Fire event on instance

Panel Events

Panel will fire the following DOM events on panel element and events on app and panel instance:

DOM Events

EventTargetDescription
panel:openPanel Element<div class="panel">Event will be triggered when Panel starts its opening animation
panel:openedPanel Element<div class="panel">Event will be triggered after Panel completes its opening animation
panel:closePanel Element<div class="panel">Event will be triggered when Panel starts its closing animation
panel:closedPanel Element<div class="panel">Event will be triggered after Panel completes its closing animation
panel:backdrop-clickPanel Overlay Element<div class="panel-backdrop">Event will be triggered when the panel overlay is clicked
panel:swipeopenPanel Element<div class="panel">Event will be triggered in the very beginning of opening it with swipe
panel:swipePanel Element<div class="panel">Event will be triggered for swipe panel during touch swipe action
panel:collapsedbreakpointPanel Element<div class="panel">Event will be triggered when it becomes visible/hidden when app width matches its collapsedBreakpoint
panel:breakpointPanel Element<div class="panel">Event will be triggered when it becomes visible/hidden when app width matches its visibleBreakpoint
panel:resizePanel Element<div class="panel">Event will be triggered on resizable panel resize
panel:beforedestroyPanel Element<div class="panel">Event will be triggered right before Panel instance will be destroyed

App and Panel Instance Events

Panel instance emits events on both self instance and app instance. App instance events has same names prefixed with panel.

EventTargetArgumentsDescription
openpanel(panel)Event will be triggered when Panel starts its opening animation. As an argument event handler receives panel instance
panelOpenapp(panel)
openedpanel(panel)Event will be triggered when Panel completes its opening animation. As an argument event handler receives panel instance
panelOpenedapp(panel)
closepanel(panel)Event will be triggered when Panel starts its closing animation. As an argument event handler receives panel instance
panelCloseapp(panel)
closedpanel(panel)Event will be triggered when Panel completes its closing animation. As an argument event handler receives panel instance
panelClosedapp(panel)
backdropClickpanel(panel)Event will be triggered when the panel backdrop is clicked. As an argument event handler receives panel instance
panelBackdropClickapp(panel)
swipeOpenpanel(panel)Event will be triggered in the very beginning of opening it with swipe. As an argument event handler receives panel instance
panelSwipeOpenapp(panel)
swipepanel(panel, progress)Event will be triggered for swipe panel during touch swipe action. As an argument event handler receives panel instance and opened progress (from 0 to 1)
panelSwipeapp(panel, progress)
collapsedBreakpointpanel(panel)Event will be triggered when it becomes visible/hidden when app width matches its collapsedBreakpoint. As an argument event handler receives panel instance
panelCollapsedBreakpointapp(panel)
breakpointpanel(panel)Event will be triggered when it becomes visible/hidden when app width matches its visibleBreakpoint. As an argument event handler receives panel instance
panelBreakpointapp(panel)
resizepanel(panel, newPanelWidth)Event will be triggered on resizable panel resize
panelResizeapp(panel)
beforeDestroypanel(panel)Event will be triggered right before Panel instance will be destroyed
panelBeforeDestroyapp(panel)

Panel Auto Initialization

If you don't need to use Panel API and your Panel is inside of the app on init or inside of the page and presented in DOM on moment of page initialization then it can be auto initialized with just adding additional panel-init class:

<!-- Add panel-init class -->
<div class="panel panel-left panel-cover panel-init">
  ...
</div>

In this case if you need to access created Panel instance you can use the app.panel.get app method:

var panel = app.panel.get('.panel-left');

if (panel.opened) {
  // do something
}

When using auto init you may need to pass additional parameters. It can be done with data- attributes on panel element.

<!-- parameters set via data- attributes -->
<div
  class="panel panel-left panel-reveal panel-init"
  data-collapsed-breakpoint="768"
  data-visible-breakpoint="1024"
  data-swipe="true"
>
  ...
</div>

Parameters used in camelCase, for example visibleBreakpoint, in data- attributes should be used in kebab-case as data-visible-breakpoint

It is possible to open and close required panel (if you have them in DOM) using special classes and data attributes on links:

  • To open panel we need to add panel-open class to any HTML element (preferred to link)

  • To close panel we need to add panel-close class to any HTML element (preferred to link)

  • To toggle panel we need to add panel-toggle class to any HTML element (preferred to link)

  • If you want to specify which panel should opened/closed, then it could be done via additional data-panel=".panel-left" attribute on this HTML element. This attribute can also receive just left or right value if there is only one panel of such side in DOM.

According to above note:

<body>
  <div id="app">
    <!-- Left Panel with Reveal effect -->
    <div class="panel panel-left panel-reveal panel-init">
      <div class="block">
        ...
        <!-- Clicking on link with "panel-close" class will close panel -->
        <p><a href="#" class="panel-close">Close me</a></p>
        <!-- Click on link with "panel-open" and data-panel=".panel-right" attribute will open Right panel -->
        <p><a href="#" data-panel=".panel-right" class="panel-open">Open Right Panel</a></p>
      </div>
    </div>

    <!-- Right Panel with Cover effect -->
    <div class="panel panel-right panel-cover panel-init">
      <div class="block">
        ...
        <!-- Click on link with "panel-close" class will close panel -->
        <p><a href="#" class="panel-close">Close me</a></p>
        <!-- Click on link with "panel-open" and data-panel=".panel-left" attribute will open Left panel -->
        <p><a href="#" data-panel=".panel-left" class="panel-open">Open Left Panel</a></p>
      </div>
    </div>

    ...
    <div class="page-content">
      <div class="block">
        <!-- Click on link with "panel-open" and data-panel=".panel-left" attribute will open Left panel -->
        <p><a href="#" data-panel=".panel-left" class="panel-open">Open Left Panel</a></p>
        <!-- Click on link with "panel-open" and data-panel=".panel-right" attribute will open Right panel -->
        <p><a href="#" data-panel=".panel-right" class="panel-open">Open Right Panel</a></p>
      </div>
    </div>
  </div>
  ...
</body>

Routable Panels

Panels can also be routable with same features as for routable modals and pages:

routes = [
  ...
  // Creates Panel from passed HTML string
  {
    path: '/left-panel/',
    panel: {
      content: `
        <div class="panel panel-left panel-cover">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      `
    }
  },
  // Load Panel from file via Ajax
  {
    path: '/right-panel-ajax/',
    panel: {
      url: './right-panel.html',
      /* right-panel.html contains:
      <div class="panel panel-right panel-reveal">
        <div class="view">
          <div class="page">
            ...
          </div>
        </div>
      </div>
      */
    },
  },
  // Load Panel from component file
  {
    path: '/panel-component/',
    panel: {
      componentUrl: './panel-component.html',
      /* panel-component.html contains:
      <template>
        <div class="panel panel-left panel-cover">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      </template>
      <style>...</style>
      <script>...</script>
      */
    },
  },
]

According to example above:

Nested Panels

In addition to main app panels, it is also possible to have nested panels, for example inside of the Page. To make Panel nested we need to specify its containerEl parameter pointing on parent container (e.g. Page element) and put it before page-content element:

<div class="page" id="panel-page">
  <div class="navbar">
    <!-- ... -->
  </div>

  <!-- Nested panel has parent page specified in containerEl parameter -->
  <div class="panel panel-left panel-cover panel-init dark" id="panel-nested" data-container-el="#panel-page">
    <div class="page">
      <!-- ... -->
    </div>
  </div>

  <!-- Rest of the page -->
  <div class="page-content">
    <!-- ... -->
  </div>
</div>

CSS Variables

Below is the list of related CSS variables (CSS custom properties).

Note that commented variables are not specified by default and their values is what they fallback to in this case.

:root {
  --f7-panel-width: 260px;
  --f7-panel-backdrop-bg-color: rgba(0, 0, 0, 0.3);
  /*
  --f7-panel-left-width: var(--f7-panel-width);
  --f7-panel-right-width: var(--f7-panel-width);
  --f7-panel-left-collapsed-width: var(--f7-panel-collapsed-width);
  --f7-panel-right-collapsed-width: var(--f7-panel-collapsed-width);
  */
  --f7-panel-bg-color: #fff;
}
:root .dark,
:root.dark {
  --f7-panel-bg-color: #000;
}
.ios {
  --f7-panel-collapsed-width: 58px;
  --f7-panel-transition-duration: 400ms;
  --f7-panel-transition-timing-function: initial;
}
.md {
  --f7-panel-collapsed-width: 60px;
  --f7-panel-transition-duration: 400ms;
  --f7-panel-transition-timing-function: cubic-bezier(0, 0.8, 0.34, 1);
}

Examples

panel.html
<div class="page" id="panel-page">
  <div class="navbar">
    <div class="navbar-bg"></div>
    <div class="navbar-inner sliding">
      <div class="title">Panel / Side panels</div>
    </div>
  </div>
  <div class="panel panel-left panel-cover panel-init" id="panel-nested" data-container-el="#panel-page">
    <div class="page">
      <div class="page-content">
        <div class="block block-strong-ios block-outline-ios">
          <p>This is page-nested Panel.</p>
          <p><a class="panel-close">Close me</a></p>
        </div>
      </div>
    </div>
  </div>

  <div class="page-content">
    <div class="block block-strong-ios block-outline-ios">
      <p>Framework7 comes with 2 panels (on left and on right), both are optional.
        You can put absolutely anything inside: data lists, forms, custom content, and even other isolated app view
        (like in right panel now) with its own dynamic navbar.</p>
    </div>
    <div class="block block-strong-ios block-outline-ios">
      <p class="grid grid-cols-2 grid-gap">
        <a class="button button-raised button-fill panel-open">Open left panel</a>
        <a class="button button-raised button-fill panel-open" data-panel="right">Open right panel</a>
      </p>
      <p>
        <a class="button button-raised button-fill panel-open" data-panel="#panel-nested">Open nested panel</a>
      </p>
    </div>
  </div>
</div>