Bartender is a library for creating accessible off-canvas bars. Any number of bars is supported, and they can be located on any side of the viewport.
Check out the demo in Bartender.js playground.
- Add any number of bars to any side of the viewport
- Bar properties, such as position or mode can be changed on the fly
- Fully stylable
- TypeScript is supported
- Comprehesive API, integrable to frameworks
- ARIA-attributes are being used for all relevant elements
- After closing the bar the focus will return to the element which was used to open the bar
- If bar is closed, it's child elements are not focusable
- If focus trap is enabled and bar is open, only it's child elements are focusable
- All transitions are disabled if user prefers reduced motion
All major browsers are supported. Library is transpiled to ES2015.
npm i @fokke-/bartender.js
Note that it's highly recommended to define the following viewport meta tag to avoid quirks when using bars with push
or reveal
modes.
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
- Create main wrap for Bartender. Ideally it would be your
<body>
element. - Create wrap element for your page content as a direct child of the Bartender main element.
- Add any number of bar elements as a direct children of the Bartender main element.
- If you need to use fixed positioned elements in your page content, add them as direct children of the bartender main element.
Note that the class names in the example below are defaults. You can use any classes as they are configurable when you initialize the library later. However, using default classes in addition to your own classes is recommended to avoid FOUC.
<!-- Main wrap for bartender -->
<body class="bartender">
<!-- Wrap for page content -->
<div class="bartender__content">
<button class="toggleMobileNav">Toggle mobile navigation</button>
</div>
<!-- Add any number of bars -->
<div class="mobileNav bartender__bar">
<button class="closeMobileNav">Close mobile navigation</button>
</div>
<!-- Place your fixed positioned elements here -->
</body>
@import '@fokke-/bartender.js/dist/bartender.scss';
// ...or
@import '@fokke-/bartender.js/dist/bartender.css';
import { Bartender } from '@fokke-/bartender.js'
// Create main instance
const bartender = new Bartender({
el: '.bartender',
contentEl: '.bartender__content',
})
// Add a new bar
bartender.addBar('mobileNav', {
el: '.mobileNav',
position: 'left',
mode: 'float',
})
// Toggle button
document.querySelector('.toggleMobileNav').addEventListener('click', (event) => {
// Pass button as second argument to return focus after closing the bar.
bartender.toggle('mobileNav', event.target)
})
// Close button
document.querySelector('.closeMobileNav').addEventListener('click', (event) => {
bartender.close()
})
Bartender constructor accepts two object arguments. The first argument defines main options. The second argument allows you to override default options for new bars.
const bartender = new Bartender({
// main options
}, {
// default options for new bars
});
Type: boolean
, Default: false
If enabled, Bartender will log it's activity to console. Note that these messages will be outputted at debug log level and you need to configure your console to show these messages.
Type: string | Element
, Default: '.bartender'
Main element as selector string or reference to the element.
Type: string | Element
, Default: '.bartender__content'
Page content element as selector string or reference to the element.
Type: number
(milliseconds), Default: 150
If bar is opened when there's already another active bar, the open bar will be closed and the library will pause for the given time before opening the another bar.
Add a new bar.
Argument | Type | Description |
---|---|---|
name | string | Unique name for the bar |
options | object | Bar options. Available options are listed below. |
bartender.addBar('mobileNav', {
el: '.mobileNav',
position: 'left',
mode: 'float',
})
Bar options can be modified on the fly, except for the el
property.
bartender.getBar('mobileNav').position = 'right'
Type: string | Element
Bar element as selector string or reference to the element.
Type: string
, Default: 'left'
Bar position as string. Possible values are 'left'
, 'right'
, 'top'
and 'bottom'
.
Type: string
, Default: 'float'
Bar mode as string. Possible values are:
float
- The bar will slide in and float over the content.push
- The bar will slide in, and the content wrap will be pushed away from the bar.reveal
- Content wrap will be pushed away, revealing the bar underneath
Type: boolean
, Default: true
Show shading overlay over content wrap when bar is open. If disabled, overlay element will still be rendered, but it will be transparent.
Type: boolean
, Default: false
If enabled, the bar is not closable by clicking overlay of pressing esc
key.
IMPORTANT: If you enable this, remember to provide a way to close the bar.
Type: boolean
, Default: true
If enabled, bar will be scrolled to top when opening it.
Type: boolean
, Default: false
If enabled, keyboard focus will be trapped to the currently open bar.
IMPORTANT: If you enable this, you must provide a way to close the bar with keyboard. Even though by default esc
key closes the bar, adding a dedicated close button to the bar is highly recommended.
Get bar instance by name.
Argument | Type | Description |
---|---|---|
name | string | Bar name |
bartender.getBar('mobileNav')
Remove bar instance by name.
Argument | Type | Description |
---|---|---|
name | string | Bar name |
bartender.removeBar('mobileNav')
Open bar by name. If you specify reference to the element as a second argument, the focus will be returned to given element after bar is closed.
Argument | Type | Description |
---|---|---|
name | string | Bar name |
returnFocus | HTMLElement | Reference to the element to which focus will be restored after closing the bar |
// Open bar 'mobileNav'
bartender.open('mobileNav')
// Open bar 'mobileNav' and return focus after closing it
bartender.open('mobileNav', document.querySelector('.toggleMobileNav'))
Close bar by name. If name is undefined, any open bar will be closed.
Argument | Type | Description |
---|---|---|
name | string | Bar name |
// Close bar 'mobileNav'
bartender.close('mobileNav')
// Close any open bar
bartender.close()
Toggle bar open/closed state. If you specify reference to the element as a second argument, the focus will be returned to given element after bar is closed.
Argument | Type | Description |
---|---|---|
name | string | Bar name |
returnFocus | HTMLElement | Reference to the element to which focus will be restored after closing the bar |
// Toggle bar 'mobileNav'
bartender.toggle('mobileNav')
// Toggle bar 'mobileNav' and return focus after closing it
bartender.toggle('mobileNav', document.querySelector('.toggleMobileNav'))
Specify additional element you want to be pushed when bar is opened. This can be useful for fixed elements.
By default element is pushed by all bars, modes and positions, but you can fine-tune this behaviour by options.
Argument | Type | Description |
---|---|---|
el | string | Element | Element as selector string or reference to the element. |
options | object | Pushable element options. Available options are listed below. |
// Always push the element, regardless of bar configuration
bartender.addPushElement('.myFixedElement')
// Push element only if bar mode is 'push' or 'reveal' AND position is 'left' or 'right'.
bartender.addPushElement('.myFixedElement', {
modes: [
'push',
'reveal',
],
positions: [
'left',
'right',
],
})
Note that if you specify multiple options, they all have to match to the bar being opened.
Type: Array<Bar>
, Default: []
An array of bar instances.
Type: Array<string>
, Default: []
An array of bar modes.
Type: Array<string>
, Default: []
An array of bar positions.
Remove pushable element by reference.
Argument | Type | Description |
---|---|---|
el | Element | Reference to the element. |
bartender.removePushElement(document.querySelector('.myFixedElement'))
Destroy Bartender instance.
bartender.destroy()
Bartender has been initialized. Instance will be included in detail
object.
window.addEventListener('bartender-init', (event) => {
console.log(event.detail.bartender)
})
Bartender instance has been destroyed. Instance will be included in detail
object.
window.addEventListener('bartender-destroyed', (event) => {
console.log(event.detail.bartender)
})
A new bar has been added. Bar will be included in detail
object.
window.addEventListener('bartender-bar-added', (event) => {
console.log(event.detail.bar)
})
A bar has been removed. Bar name will be included in detail
object.
window.addEventListener('bartender-bar-removed', (event) => {
console.log(event.detail.name)
})
This event is dispatched when one of the following bar properties change:
- position
- mode
- overlay
- permanent
- scrollTop
Bar, updated property name and property value will be included in detail
object.
window.addEventListener('bartender-bar-updated', (event) => {
console.log(`Updated bar '${event.detail.bar.name}' property '${event.detail.property}' to '${event.detail.value}'`)
})
Bar has started to open. Bar will be included in detail
object.
window.addEventListener('bartender-bar-before-open', (event) => {
console.log(event.detail.bar)
})
Bar is open. Bar will be included in detail
object.
window.addEventListener('bartender-bar-after-open', (event) => {
console.log(event.detail.bar)
})
Bar has started to close. Bar will be included in detail
object.
window.addEventListener('bartender-bar-before-close', (event) => {
console.log(event.detail.bar)
})
Bar is closed. Bar will be included in detail
object.
window.addEventListener('bartender-bar-after-close', (event) => {
console.log(event.detail.bar)
})
Note that all transitions are disabled, if user prefers reduced motion. Read more about reduced motion.
These scss variables will be used as defaults for all transitions:
$bartender-transition-duration: 250ms !default;
$bartender-transition-timing-function: ease !default;
Each bar will receive class names based on it's position and mode.
/* Styles for all bars */
.bartender__bar {
background: #dadada;
}
/* Styles for all bars with position "left" */
.bartender__bar--left {
background: #dadada;
}
/* Styles for all bars with mode "float" */
.bartender__bar--float {
background: #dadada;
}
Each bar has it's own overlay element, so you can style overlays per bar basis.
/* Styles for all overlays */
.bartender__overlay {
background-color: rgba(128, 0, 0, 0.5);
}
/* Styles for the overlay of bar named "mobileNav" */
.bartender__overlay--mobileNav {
background-color: rgba(128, 0, 0, 0.5);
}