Grid
Component for creating responsive, fluid and nestable grid layout
The class .grid
collapses whitespace and makes children inline-block. This lets us use all text-properties
(like text-align, vertical-align, white-space) to generate complex layouts. Any element can be a grid-cell.
Grid automatically wrap cells across multiple lines when they don't fit a single row. Also, you can add the .grid--border
modifier, to have nicely collapsed borders as shown below:
extra content
Use .w100
helper to render cells at fill-width:
Grid supports nesting:
Build the grid using mobile-first approach: treat mobile as your default and build more complex layouts up from there. --m
and --l
modifiers indicate responsive breakpoints. First cell will span full width on mobile, 70% width at the medium breakpoint, and 30% at the large breakpoint. See width helpers.
Grid arranges cells of different heights into clear rows. You can use alignment helpers to control vertical alignment of individual cells:
You can automatically add gutters (in the form of padding around the cells) by applying .grid--withGutter
modifier. Note that this can trigger a horizontal scrollbar if the grid is as wide as
the viewport. Use padding on a container to protect against it.
Use .grid--reverse
to reverse rendered order of grid cells. Grid cells 1, 2, 3 in the markup will display in order 3, 2, 1:
Use .grid--center
to center-align all grid cells except their content:
Use .grid--right
to right-align all grid cells except their content:
Use .grid--justify
to justify-align all grid cells except their content. Excess horizontal space gets distributed among the gaps:
Use .grid-cell--center
to set a specific cell to be horizontally centered:
.grid--arrange
makes use of CSS table layout. Use it to arrange rows of equal-height cells. If you don't specify cell widths, they'll render equal-width.
.grid--arrange
is very useful when building form layouts, as each cell in a row has the same height. This allows us to ditch cumbersome JavaScript solutions.
ancetta chicken shank ribeye leberkas jerky beef ribs tri-tip strip steak short ribs bacon capicola. Fatback strip steak ribeye meatloaf swine kevin cow tail turkey bacon shank shankle frankfurter biltong chuck. Andouille beef ribs leberkas, frankfurter venison ground round swine landjaeger tri-tip.
ancetta chicken shank ribeye leberkas jerky beef ribs tri-tip strip steak short ribs bacon capicola. Fatback strip steak ribeye meatloaf swine kevin cow tail turkey bacon shank shankle frankfurter biltong chuck. Andouille beef ribs leberkas, frankfurter venison ground round swine landjaeger tri-tip.
ancetta chicken shank ribeye leberkas jerky beef ribs tri-tip strip steak short ribs bacon capicola. Fatback strip steak ribeye meatloaf swine kevin cow tail turkey bacon shank shankle frankfurter biltong chuck. Andouille beef ribs leberkas, frankfurter venison ground round swine landjaeger tri-tip.
Extend with .grid--position
if you want the grid to stay centered within the bounds of a container. The container should be positioned, which means it should either have position: relative
, position: absolute
or position: fixed
on it. Position of the grid is determined by its container's dimensions (width & height).
.grid--position
doesn't play well with .grid--justify
modifier.
To set width limit and center you can wrap your layout in a containing div with a class of wrap
:
Nav
Component for various navigational constructs
When used on an <ol>
or <ul>
, .nav
throws the list into horizontal mode:
Use .nav--stacked
to throw the list into vertical mode:
Use .nav--block
to give nav links a big, blocky clickable area with hover state. .is-active
class highlights link:
Hover-state semi-transparent backgrounds are automatically derived from the parent's text color:
Combine .nav--block
with .nav--rounded
to make nav links corners rounded:
Combine .nav--block
with .nav--tabs
to create a tab-style navigation:
Simple navbar that collapses for smaller viewports:
List block
Component for presenting multiple items in a vertical arrangement
List block is a very versatile component and can hold content ranging from basic text to icons, buttons and UI controls.
-
Item one (not actionable)
-
Item two
-
Item three
The majority of space on a list item should be dedicated to the primary action (i.e. selecting an item). Text within a list item should be considered part of the primary action target. You can place supplemental action represented via, for example, icon on the right side of a list item. Supplemental actions are always a separate target from the primary action.
In some cases the primary distinguishing content may not be actionable. You can place a button on the right side of the item to represent a related action:
-
Item one Bacon ipsum dolor sit amet chuck prosciutto landjaeger ham hock filet mignon shoulder hamburger pig venison
-
Item two Bacon ipsum dolor sit amet chuck prosciutto landjaeger ham hock filet mignon shoulder hamburger pig venison
Definition list
Component for displaying name/value pairs
Useful for glossaries and such.
- Owner
- John Doe
- Contact information
- John Doe
- Phone
- +47 22222424
- Text
- Fusce vitae dologr a nulla rhoncus vulputate sed nec mauris. Mauris condimentum pretium arcu sed molestie. Suspendisse posuere vitae sapien sit amet tristique. Praesent non est augue. In hac habitasse platea dictumst. Curabitur quis diam ante. Ut at vestibulum leo. Integer porta posuere tincidunt. Mauris at eleifend risus.
Use .definitionList--striped
to render striped definition list:
- Owner
- John Doe
- Contact information
- John Doe
- Phone
- +47 22222424
- Text
- Fusce vitae dologr a nulla rhoncus vulputate sed nec mauris. Mauris condimentum pretium arcu sed molestie. Suspendisse posuere vitae sapien sit amet tristique. Praesent non est augue. In hac habitasse platea dictumst. Curabitur quis diam ante. Ut at vestibulum leo. Integer porta posuere tincidunt. Mauris at eleifend risus.
Buttons
Buttons are transparent and sized by content, but can also be combined with color styles and sizing helpers. They can include an icon, text, or both. Mainly buttons and a-elements and input-labels (described in forms-section) are used as buttons, to make sure we follow focus-requirements in the WCAG spesification ›
Primary button
Use this type of button if you have main action(s) that you want to promote to the user.
If the user have to choose between primary and secondary action, consider using outline button or ghost button for the secondary action.
Outline (secondary) button
This type of button can be used as an alternative to the primary button if the actions requires less focus or attention.
Can also be used next to the primary button, if there is a secondary action.
Ghost (tertiary) button
This type of button, similarly to outline button, can be used for less-pronounced actions. Because ghost buttons don’t have a container, they don’t distract from nearby content.
Button groups
You can group together buttons related by function. A button group is a series of buttons laid out next to each other, joined together to create one continuous UI.
Button groups are useful to create "toggles" that allow to switch between two or more options. This can be used to filter content for example.
Using flexbox helpers, the items within a group can be justified:
Success/danger buttons
You can add .btn--success
or .btn--danger
modifiers to imply the consequence of the button's action:
Buttons with lone icons
Buttons with lone icon should have .btn--icon
modifier alongside .btn
class. Because the button with lone icon doesn’t have a label, use a <span class="txtAssistive">
with descriptive text or include aria-label
attribute for accessibility reasons:
Inverse buttons
Buttons can come in inverse variation, for instance when used on a dark background:
Button states
You can apply disabled state for a button either using .is-disabled
class or disabled
attribute. disabled
attribute works only for form elements, i.e. <button>
. If you want to disable other btn
elements, use .is-disabled
class.
Buttons can have waiting state:
Icons
See Icons page for the list of available icons and comprehensive guide on usage.
Symbol in header
To show circle outlined symbols with medium size and button style with hover effect, use the .iconCircle
class with .iconCircle--outline
and .iconCircle--medium
modifiers.
This will make the symbol double size, and add some extra padding.
Labels
Component for labelling content with certain keywords, showing unread content, highlighting new content and displaying status
Standard labels are great for drawing attention without visual overload.
Labels are available in outline variation. Outlined labels have more subtle appearance.
Labels can also be themed using color helpers.
A label can be circular. Circular labels are used for counts, so should only contain numbers:
Labels can come in small size:
Labels can be used within block lists, buttons, navigation and more:
Heading New
-
Mango Fruit
-
Poodle Dog 5
-
Item one In progress
-
Item two Moved
Kbd
Component for showing which key or combination of keys performs a given action.
shift + H
alt or option
⌘D
⌘⇧U
Avatar
Component for representing people or objects
Avatars support images and profile pictures:
If the user hasn't uploaded profile picture, you may use their initials (up to two letters) for avatars.
If the record name contains two words (e.g. first and last name) use the first capitalized letter of each. For records that only have a single word name, use the first two letters of that word using one capital and one lower case letter.
When an image is unavailable (for example, due to an error) or if the initials cannot be rendered, a placeholder silhouette icon should be shown instead:
Avatars support presence indicators. You can use active
, busy
or away
modifiers for different status colors. The default is gray which means offline.
Avatars are available in 4 sizes to accommodate different use cases and can be themed using fill helpers:
Avatars can be grouped together in a stack to show for example multiple contributors:
Notification
Notifications show up in task flows, to prominently communicate important information to the user. They usually appear close to the specific area in the UI needing attention.
Notifications can come in small size, with help of .notification--small
modifier.
Notifications can optionally include a dismiss button for messages that can be closed and a contextual action to take. The action content will be displayed on the end side of the notification, except when screen size is small in which case the layout will stack vertically to better accommodate the content.
Notification supports switching layout direction to vertical with .notification--vertical
modifier. It can come handy when a message longer than two lines is required and/or you want to include multiple action elements.
Notifications that appear as temporary, dismissable popups (toast notifications) or those displayed in a dialog overlay, should use .notification--headless
modifier class to remove default outline styles.
These type of notifications are generally used for communicating feedback on a user’s action.
Use modal component to show notification dialogs on top of the application and to confirm or request action from the user:
var warningMessage = [
'<div class="notification notification--warning notification--headless notification--vertical">',
'<div class="notification-icon">',
'<div class="svgIcon svgIcon--stroked">',
'<svg focusable="false" aria-hidden="true">',
'<use xlink:href="#svg--warning" />',
'</svg>',
'</div>',
'</div>',
'<div class="notification-message">',
'<h3 class="delta">Delete data?</h3>',
'<p class="mTs mBn fg-neutral--moderate">Are you sure you want to delete this data? This action cannot be undone.</p>',
'</div>',
'<div class="notification-actions dF aiC">',
'<button type="button" class="btn btn--danger mRs js-modalAction">Delete</button>',
'<button type="button" class="btn btn--outline js-modalClose">Cancel</button>',
'</div>',
'</div>'
].join('\n');
var warningDialog = Wave.Modal({
content: warningMessage,
contentClass: 'pAn bdr--noRounding',
closable: false,
modalAction: function (e) {
e.preventDefault();
console.log('[Wave.Modal] time changed');
this.hide();
}
});
$(document.querySelector('.js-triggerWarning')).on('click', function (e) {
e.preventDefault();
warningDialog.show();
});
Notification toaster
Provide feedback to the user through a timed popup
Notification toaster is handy for providing simple feedback or grabbing the user's attention, for operations or actions that he or she has performed. Toast notifications are self-dismissing by default and they do not interrupt the current activity. Multiple toast notifications can be shown at the same time stacking top-to-bottom. You should realize that due to transient nature of toast notifications, the user might not see them. Don't use them for critical level notifications and those that require action from the user.
Customizable options
var toaster = Wave.Toaster({
position: 'topLeft', // where to position notifications. Available options: 'topLeft', 'topCenter', 'topRight', 'centerLeft', 'centerRight', 'bottomLeft', 'bottomCenter', 'bottomRight' ('bottomLeft' is default)
animation: 'slideDown', // animation to use when notifications are added. Available options: 'slideUp', 'slideDown', 'slideLeft', 'slideRight', 'fade' ('slideUp' is default)
wrapperClass: '', // any additional class to append to the notifications wrapper
duration: 6000, // number in miliseconds before a notification disappears (default is 5000)
template: '' // individual notification markup (String), defaulting to '<div class="toaster-item"></div>' or based on type option (see below)
type: undefined // individual notification type. When specified the template for specific type will be used automatically. Available types:
// Wave.Toaster.TOAST_TYPES.SUCCESS, Wave.Toaster.TOAST_TYPES.ERROR, Wave.Toaster.TOAST_TYPES.INFO, Wave.Toaster.TOAST_TYPES.WARNING
closable: false // when type is provided, controls whether dismiss functionality should be added to the notification
action: { // optional action definition
label: '', // label of the action
onAction: function(toastElement, event) {} // callback function to trigger when action is triggered by the user
}
});
Component's API
.create(content, options)
creates a toast notification, sets the content (HTML code or simply a non formatted string) and initializes timers. Original options can be customized by passing a second argument, which, for example, allows for setting variable types or timeouts of individual notifications.
.getElements()
gets the list of notification nodes as an array.
.getElementsById()
gets the dictionary of notification nodes by their unique identifiers.
.show(element)
shows the given notification (called automatically after the notification is created).
.hide(element)
hides the given notification and removes it after the transition is complete (called automatically when the duration is over).
.on('create', function(element))
event that triggers after a notification is created but before it's shown.
.on('showing', function(element))
event that triggers when the show()
method is called.
.on('show', function(element))
event that triggers after a notification is shown.
.on('hiding', function(element))
event that triggers when the hide()
method is called.
.on('hide', function())
event that triggers after a notification is hidden and removed.
Usage example
var toaster = Wave.Toaster({
position: 'topLeft',
animation: 'slideDown'
});
toaster.on('show', function (element) {
console.log('[Wave.Toaster] notification shown');
});
$(document.querySelector('.js-createToast')).on('click', function (e) {
e.preventDefault();
toaster.create('This is an info toast notification.', { type: Wave.Toaster.TOAST_TYPES.INFO });
});
$(document.querySelector('.js-createSlideRightToast')).on('click', function (e) {
e.preventDefault();
toaster.create('This is a success toast notification with slideRight animation.', {
animation: 'slideRight',
type: Wave.Toaster.TOAST_TYPES.SUCCESS
});
});
$(document.querySelector('.js-createDismissableToast')).on('click', function (e) {
e.preventDefault();
toaster.create('This is a warning toast notification that will persist until dismissed.', {
duration: 0,
type: Wave.Toaster.TOAST_TYPES.WARNING,
closable: true
});
});
$(document.querySelector('.js-createToastWithAction')).on('click', function (e) {
e.preventDefault();
toaster.create('Request failed!', {
duration: 0,
type: Wave.Toaster.TOAST_TYPES.ERROR,
closable: true,
action: {
label: 'Try again',
onAction: function(toastElement) {
alert('Toast action!');
toaster.hide(toastElement);
}
}
});
});
$(document.querySelector('.js-createToastWithCustomTemplate')).on('click', function (e) {
e.preventDefault();
toaster.create('This is a toast with custom template.', {
template: '<div class="toaster-item fill-neutral--minimal bdrAm pAm"></div>'
});
});
Modal
Component for displaying dialog prompts
Customizable options
var modal = Wave.Modal(
{
content: '', // the content to be shown inside a modal (String | HTMLElement)
contentClass: '', // any additional class to append to the content wrapper
closable: false, // whether the user can close a modal (Boolean)
modalAction: function(e) {} // callback function to run after action trigger element (.js-modalAction) is clicked
}
);
Component's API
.show()
shows the modal.
.hide(data)
hides the modal. Accepts optional data
argument that will be passed to the hide
event.
.content(content)
sets a new content for the modal.
.$(selector)
returns DOM node inside the modal that matches the specified selector (String).
.on('showing', function())
event that triggers when the show()
method is called.
.on('show', function())
event that triggers after transitions on the modal are finished (modal is shown).
.on('hiding', function())
event that triggers when the hide()
method is called.
.on('hide', function(data))
event that triggers after transitions on the modal are finished (modal is destroyed and hidden). Callback is passed an object with wasDismissed
property and merged data, if any, passed to hide()
method by the user.
.on('content', function(content))
event that triggers when the new content is set.
Usage example
var modal = Wave.Modal({ content: '<h2>Hello world</h2>' });
var modalWithActionsContent = [
'<div class="pTm pLl pRm dF jcSB">',
'<h1 class="beta">Modal header</h1>',
'<button type="button" aria-label="Close" class="btn btn--small btn--ghost btn--fullRounded mAn js-modalClose">',
'<span class="svgIcon svgIcon--medium svgIcon--stroked">',
'<svg aria-hidden="true" focusable="false">',
'<use xlink:href="#svg--close"></use>',
'</svg>',
'</span>',
'</button>',
'</div>',
'<div class="pTm pBl pHl">',
'<p class="mAn">Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis. Cillum sunt ad dolore quis aute consequat ipsum magna exercitation reprehenderit magna. Tempor cupidatat consequat elit dolor adipisicing.</p>',
'</div>',
'<div class="modal-footer fill-neutral--moderate pVm pHl txtR">',
'<button type="button" class="btn btn--ghost mAn mRm js-modalClose">Cancel</button>',
'<button type="button" class="btn mAn js-modalAction">Save</button>',
'</div>'
].join('\n');
var modalWithActions = Wave.Modal({
content: modalWithActionsContent,
contentClass: 'pAn measure',
modalAction: function(e) {
e.preventDefault();
console.log('[Wave.Modal] action clicked');
this.hide();
}
});
$(document.querySelector('.js-showModal')).on('click', function (e) {
e.preventDefault();
modal.show();
});
$(document.querySelector('.js-updateModalContent')).on('click', function (e) {
e.preventDefault();
modal.content('<h2>Hallo verden</h2>');
});
$(document.querySelector('.js-showModalWithActions')).on('click', function (e) {
e.preventDefault();
modalWithActions.show();
});
Use .js-modalClose
class on any element inside the modal that should trigger the close/hide action:
<a href="#" class="js-modalClose">Close Modal</a>
<button class="js-modalClose">Close Modal</button>
Use .js-modalAction
class on any element inside the modal that should trigger the modalAction
callback:
<a href="#" class="js-modalAction">Trigger modal action</a>
<button class="js-modalAction">Trigger modal action</button>
<button class="js-showModalWithActions">Show modal with actions</button>
If you want to set focus to specific element inside the active modal use autofocus
attribute:
<input type="text" autofocus />
You can also populate modals with any in-page content. Add class .js-triggerModal
, along with data-target
attribute on the element that should trigger the modal. data-target
should use the valid id or class name of associated in-page element which contents is to be displayed in the modal.
Help
Bacon ipsum dolor sit amet chuck prosciutto landjaeger ham hock filet mignon shoulder hamburger pig venison. Ham bacon corned beef, sausage kielbasa flank tongue pig drumstick capicola swine short loin ham hock kevin.
Modal component can be easily customised (see popup notifications).
Overlay
Create overlays over elements
Overlays are generated dynamically using JavaScript, so they don't require any markup. You can combine overlays with spinner and toggle components.
Customizable options
var overlay = Wave.Overlay(
element, // a DOM node to which to append the overlay (defaulting to document.body)
{
closable: true, // whether the overlay is closeable by clicking on it
overlayClass: '', // any additional class to append to the overlay
zIndex: 100 // set the stack order of the overlay (default is 10)
}
);
Component's API
.show()
shows the overlay.
.hide()
hides the overlay.
.destroy()
cleans up instance data and detaches event listeners.
.on('showing', function())
event that triggers when the show()
method is called.
.on('show', function())
event that triggers after transitions on the overlay are finished (overlay is shown).
.on('hiding', function())
event that triggers when the hide()
method is called.
.on('hide', function())
event that triggers after transitions on the overlay are finished (overlay is hidden and destroyed).
.on('click', function())
event that triggers when the overlay is clicked.
Usage example
var overlay = Wave.Overlay({
closable: true,
zIndex: 1000
});
overlay.on('click', function() {
overlay.hide();
});
$(document.querySelector('.js-showOverlay')).on('click', function(e) {
overlay.show();
e.preventDefault();
});
Spinner
Spinning loaders to let the user know that a background process is being executed
Spinners are based on CSS3 transitions and animations. They will degrade gracefully on IE9 (just no animation).
Spinners are generated dynamically using JavaScript, so they don't require any markup. You can combine spinners with the overlay component to disable interactions while loading something.
Customizable options
var spinner = Wave.Spinner(
element, // a DOM node to which to append (or prepend) the spinner
{
position: 'fixed', // can choose "absolute", "relative", "fixed", "static"
offset: '50%', // offset from the chosen element (e.g. "20px" or "50%")
color: 'fg-inverse' // color you want to use ("fg-accent--moderate" is default)
inline: false, // whether the spinner is inline or not (false is default)
insertionType: 'append' // "prepend" for adding the spinner as a first child or "append" for adding as a last child ("append" is default)
}
);
Component's API
.show()
shows the spinner.
.hide()
hides the spinner.
.isShown()
returns a boolean specifying if the spinner is shown.
.on('show', function())
event that triggers when the spinner is shown.
.on('hide', function())
event that triggers when the spinner is destroyed and hidden.
Usage example
var spinner = Wave.Spinner(document.querySelector('.js-spinner'), {
position: 'absolute',
offset: '50%'
}).overlay({ closable: false });
spinner.show();
Chain with .overlay()
to combine the spinner with the overlay component.
Bacon ipsum dolor sit amet chuck prosciutto landjaeger ham hock filet mignon shoulder hamburger pig venison. Ham bacon corned beef, sausage kielbasa flank tongue pig drumstick capicola swine short loin ham hock kevin.
Bacon ipsum dolor sit amet chuck prosciutto landjaeger ham hock filet mignon shoulder hamburger pig venison. Ham bacon corned beef, sausage kielbasa flank tongue pig drumstick capicola swine short loin ham hock kevin.
You can also add inline spinning wheels. Inline spinners are meant to fit in the flow of the text.
To further indicate progress state you can also append an animated ellipsis at the end of the text using <span class="txtAnimatedEllipsis">
.
var inlineSpinner = Wave.Spinner(document.querySelector('.js-inlineSpinner'), {
inline: true,
insertionType: 'prepend'
});
inlineSpinner.show();
Inline spinner
You can create spinners manually by inserting their markup into your HTML. To change the color of the spinner, you can use color helpers. By default it will take on the color of the text in it's parent container.
Toggle
Component for hiding, switching or changing the appearence of different contents
Customizable options
var toggle = Wave.Toggle(
'selector', // CSS-like trigger selector (String)
{
toggleClass: '' // the class name to be toggled on the target element to switch between different styles
}
);
Toggles relies on data-target
attribute, so be sure to add it to the trigger element. You can use any selector with the target attribute: the component will add or remove the class from the target. Note that the target must be a parent container of the trigger element.
By itself, nothing will happen; But create a CSS animation for the specified class or even something as simple as display: none
then watch the component come to life.
Component's API
.open()
adds the specified class name to the traget.
.close()
removes the specified class name from the target.
.toggle()
toggles the specified class name on the target.
.onClickToggle()
calls toggle()
when the trigger element is clicked.
.destroy()
cleans up event listeners.
.on('open', function())
event that triggers when the specified class name is added to the target.
.on('close', function())
event that triggers when the specified class name is removed from the target.
.on('toggle', function())
event that triggers when the specified class name is toggled on the target.
.on('onClickToggle', function())
event that triggers when the trigger element is clicked.
Chain with .overlay()
to combine the toggle with the overlay component. The overlay will be appended to the target.
var toggle = Wave.Toggle('.js-triggerName', {
toggleClass: 'is-shown'
}).overlay({ closable: true });
toggle.open();
toggle.onClickToggle();
See side panel example.
Dropdown
Dropdown menus are used to group secondary or sensitive actions behind a two-step progressive disclosure, while conserving screen real estate.
Usage:
- Wrap the dropdown's trigger and the dropdown menu within
<div class="dropdown"></div>
. - Add class
.js-triggerDropdown
on the element that should trigger dropdown behavior (usually a button or a link). Remember to includedata-target=".dropdown"
attribute.data-target
is necessary because dropdowns are built with the toggle component. - Wrap the contents of dropdown menu into
<div class="dropdown-menu"></div>
.
Use .dropdown--right
to align dropdown menu to the right:
Example of collapsible navbar with dropdown sub items:
Example of a dropdown menu mixed with a series of buttons:
Collapsible panel
Usage:
- Add
.collapsiblePanel
class to the parent element that holds the trigger element and collapsed content. - Add class
.js-triggerExpand
on the element that should trigger expand/collapse behavior (usually a button or a link). Remember to includedata-target=".collapsiblePanel"
attribute.data-target
is necessary because collapsible panels are built with the toggle component. - Wrap the contents that should be collapsed into
<div class="collapsiblePanel-collapse"></div>
.
Adding .collapsiblePanel--is-expanded
class to the parent element makes the content expanded by default:
If you need to collapse/expand really long block of content, it is recommended to add .collapsiblePanel--noTransition
modifier class. This will result in no height transition effect, but the collapsible content will not be constrained to any explicit max-height
.
Side panel
A special panel that slides out from the right or left side of the screen built using the toggle component. Side panels allow the user to focus on the content of the webapp without distractions. They are useful for bringing the features that are important to the user into focus, but only when they are needed.
Wave.Toggle('.js-exampleSidePanel', { toggleClass: 'sideDrawer--is-open' }).onClickToggle();
Side panel content
You can combine side panel with the overlay component to show the mask above the content when panel is opened:
Wave.Toggle('.js-exampleSidePanelWithOverlay', { toggleClass: 'sideDrawer--is-open' }).overlay({ closable: true }).onClickToggle();
Side panel with overlay content
Drill-down navLimited browser support
Component for creating infinitely nestable navigation
Drill-down nav allows you to organize navigation in lists over multiple levels. Clicking on an item makes the user move deeper into the hierarchy.
This component can be used to create mobile-friendly, complex navigation structures. See multi-level navigation example.
Drill-down nav internally relies on flexbox, which is not supported by older versions of Internet Explorer (IE < 11).
Customizable options
var drillDownNav = Wave.DrillDownNav(
element, // a DOM node to bind the drill-down nav to
{
rootSelector: false, // selector for top-level navigation container (defaults to the `element` argument when not specified)
subNavToggleSelector: '' // selector for node that upon clicking will toggle sub navigation it belongs to (defaults to '.drillDownNav-trigger')
}
);
Component's API
.close()
collapses drill-down nav back to the root level.
.openPanel(panel)
programatically opens a panel (you can obtain flattened list of panels by accessing panels
property on the DrillDownNav
instance).
Usage example
var drillDownNav = Wave.DrillDownNav(
document.querySelector('.js-exampleDrillDownNav')
);
$(document.querySelector('.js-closeDrillDownNav')).on('click', function (e) {
e.preventDefault();
drillDownNav.close();
});
$(document.querySelector('.js-openPanelDrillDownNav')).on('click', function (e) {
e.preventDefault();
var panelToOpen = drillDownNav.panels.find(panel => panel.el.getAttribute('data-name') === 'nav-header-1');
if (panelToOpen && panelToOpen.state !== true) {
drillDownNav.openPanel(panelToOpen);
}
});
Please follow the markup structure of example below.
Sidebar
Component for creating a mobile-friendly, responsive sidebar
Sidebar shows up if the horizontal space is sufficient, such as on a desktop screen and automatically collapses off-canvas on smaller width devices.
Customizable options
var sidebar = Wave.Sidebar(
element, // a DOM node to bind the sidebar to
{
staticClass: '' // custom class(es) for static version (sidebar is always visible)
}
);
Component's API
.show()
programatically slides sidebar into view in overlay mode (mobile layout only).
.close()
programatically sends sidebar back off-canvas (mobile layout only).
.destroy()
cleans up instance data and detaches all event listeners.
Usage example
Wave.Sidebar(
document.querySelector('.js-sidebar'),
{
staticClass: 'bdrRs bdr--gray20'
}
);
Please follow the markup structure of example below.
Remember to specify id
attribute on the sidebar that toggle button(s) should point to with aria-controls
attribute.
You can control at which breakpoint static variation of the sidebar kicks in using .sidebar--static@[breakpoint]
classes, i.e. .sidebar--static@large
, .sidebar--static@xLarge
.
For a more full featured use case, check out the sidebar navigation template.
Range bar
Component for creating range sliders
Customizable options:
var r = Wave.RangeBar(
{
initialRanges: [], // array of value pairs; each pair is the min and max of the range it creates
min: 0, // value at start of bar
max: 100, // value at end of bar
valueFormat: function(v) {return v;}, // format a value on the bar for output
valueParse: function(v) {return v;}, // parses an output value for the bar
step: 0, // clamps range ends to multiples of this value (in bar units)
minSize: 0, // smallest allowed range (in bar units)
bgMarks: 0, // number of marks/cells to paint in the background of the bar
maxRanges: Infinity, // maximum number of ranges
readonly: false, // if true, ranges can't be changed with the UI, but can be changed with the api
label: function(v) {return v;} // function that transforms range value to text inside the label below the bar
// or plain string
}
);
Component's API
.add(value, options)
adds a new range, options
is optional and let's you pass range color.
.remove(rangeId)
removes range specified by rangeId
.
.removeAll()
removes all ranges.
.val()
returns array of pairs of min and max values of range.
.setVal(values)
updates the ranges in the bar with the values.
.rangeValue(rangeId)
returns a value of range specified by rangeId
. If range doesn't exists returns false.
.rangeValue(rangeId, value)
sets a value of range specified by rangeId
. Returns true if range exists, otherwise false.
.data()
returns list with data of all ranges.
.on('changing' function(data))
Event that triggers constantly as the value changes. Useful for reactively triggering things in the UI. Callback is passed an object with data of all the ranges and the range that is changing.
.on('change' function(data))
Event that triggers after the user has finished changing a range. Callback is passed an object with data of all the ranges and the range that has changed.
.on('add' function(data))
Event that triggers when range is added. Callback is passed an object with data of all the ranges and the range that is added.
.on('remove' function(data))
Event that triggers when range is removed. Callback is passed an object with data of all the ranges and the range that is removed.
Usage example
var r1 = Wave.RangeBar({
min: 0,
max: 10,
step: 1,
bgMarks: 10
});
$(document.querySelector('.js-rangeBar1')).append(r1.el);
r1.on('change', function (data) {
console.log('[Wave.RangeBar] changed: ' + JSON.stringify(data, null, 4));
});
r1.add([3, 6]);
$(document.querySelector('.js-updateRange1')).on('click', function (e) {
console.log('[Wave.RangeBar] original val: ' + JSON.stringify(r1.val(), null, 4));
r1.removeAll();
r1.add([2, 8], { color: 'success--strong' });
console.log('[Wave.RangeBar] updated val: ' + JSON.stringify(r1.val(), null, 4));
e.preventDefault();
});
See console log for the output.
Example below demonstrates usage of valueParse
and valueFormat
functions when operating with non-integer values:
This example uses Day.js library for manipulating dates.
var r2 = Wave.RangeBar({
min: dayjs().startOf('day').format(),
max: dayjs().startOf('day').add(1, 'day').format(),
step: 1000 * 60 * 15,
minSize: 1000 * 60 * 60,
initialRanges: [
[
dayjs().startOf('day').format(),
dayjs().startOf('day').add(7.5, 'hours').format()
]
],
valueFormat: function (value) {
return dayjs(value).format();
},
valueParse: function (date) {
return dayjs(date).valueOf();
},
label: function (minMaxTuple) {
var minDate = dayjs(minMaxTuple[0]);
var maxDate = dayjs(minMaxTuple[1]);
return maxDate.diff(minDate, 'hours') + ' hours';
}
});
$(document.querySelector('.js-rangeBar2')).append(r2.el);
var getTotalHours = function getTotalHours(data) {
var result = 0;
for (var i = 0; i < data.length; i++) {
var range = data[i];
result += dayjs(range.val[1]).diff(dayjs(range.val[0]), 'hours');
}
return result;
};
var r2ResultChange = document.querySelector('.js-rangeBar2ResultChange');
var r2ResultChanging = document.querySelector('.js-rangeBar2ResultChanging');
r2.on('change', function (eventData) {
r2ResultChange.innerHTML = String(getTotalHours(eventData.data));
});
r2.on('changing', function (eventData) {
r2ResultChanging.innerHTML = String(getTotalHours(eventData.data));
});
Example below shows how to generate a read-only, static bar with multiple ranges:
var r3 = Wave.RangeBar({
readonly: true, // whether the bar should be read-only
initialRanges: [
{
value: [14, 20],
color: 'info--strong' // set different color for each range
},
{
value: [10, 14],
color: 'warning--strong'
},
{
value: [6, 10],
color: 'danger--strong'
}
],
barClass: 'rangeBar--small', // any additional class to append to the range bar
min: 0,
max: 26,
step: 1,
bgMarks: 26
});
$(document.querySelector('.js-rangeBar3')).append(r3.el);
Progress
Progress bar component for showing the user a visualization of some percentage
Customizable options
var progressBar = Wave.ProgressBar(
element, // a DOM node to which to append the progress bar
{
progressBarClass: 'fill-neutral progressBar--rounded', // any additional class to append to the progress bar
color: 'success--strong', // initial color of the progress line ("info--strong" is default)
caption: '', // initial caption to be displayed below the progress line
htmlCaption: false, // whether the caption should be written as html (defaulting to text)
captionClass: '' // any additional class to append to the caption
}
);
Component's API
.setProgress(number)
sets the progress bar completion percentage. Should be numeric value between 0 and 100.
.getProgress()
gets the current progress bar completion percentage.
.setFill(color)
sets the background fill color of the progress line.
.setCaption(caption)
sets the caption to be displayed below the progress line.
.status(status)
sets the status of the progress bar (for example "error").
.hide()
hides the progress bar.
.show()
shows the progress bar.
.on('update', function(progress))
event that triggers when the completion percentage is updated.
.on('status', function(status))
event that triggers when the status is changed.
Usage example
// Create progress bar
var progressBar = Wave.ProgressBar(document.querySelector('.js-progressBar'), {
progressBarClass: 'fill-neutral',
color: 'accent--strong'
});
progressBar.on('progress', function (progress) {
console.log('[ProgressBar] progress: ' + progress + '%');
});
var progress = 0,
progressInterval = null;
$(document.querySelector('.js-simulateProgress')).on('click', function (e) {
e.preventDefault();
if (progressInterval !== null) {
clearInterval(progressInterval);
progressInterval = null;
// We've encountered an error
progressBar.status('error');
} else {
// Set back the progress value and show the progress bar
progressBar.setCaption(progress + '%').setProgress(progress).show();
progressInterval = setInterval(function () {
progress += Math.floor(Math.random() * 20);
// Progress bar is running and moving to completion
progressBar.status('is-running').setCaption(progress + '%').setProgress(Math.min(progress, 100));
if (progress >= 100) {
// Progress bar is completed
progressBar.status('is-completed');
clearInterval(progressInterval);
progressInterval = null;
progress = 0;
if ($(document.querySelector('.js-hideProgress')).prop('checked')) {
// Hide the progress bar
progressBar.hide();
}
}
}, 350);
}
});
Simulate loading progress
You can also create static progress bars in the markup. .progressBar--rounded
modifier class applies rounded corners to the progress bar.
Progress 30%
Select field
A wrapper around native select field providing consistent look across browsers. Select fields allow the user to select a value from a list of options. On mobile devices they are tightly-coupled with the OS itself, which ensures more tailored, native-like user experience. For example, Android often has a radio-button list popup, whereas iOS has a custom scroller covering the bottom half of the window.
Use .selectField--is-disabled
along with disabled
attribute to apply appropriate "disabled" appearance:
Multi-select field
A dropdown field with selected items styled as list of labels.
Customizable options
var multiSelect = Wave.MultiSelect(
element, // a DOM node to bind the multiselect to
{
itemsList: [item1, item2, ...], // values available for selection. Can be simple string or object (only support for one-level deep)
selectedList: [], // values selected on initialization (empty array is default)
inputId: 'multiSelectId', // id for internal <input> field. Set this to same as for-reference on <label> element you use for this control.
labelColor: 'accent', // color for labels in the selected list (accent is default)
labelOutline: false, // specify if labels in the selected list should be outlined instead of filled (false is default)
itemIsObject: false, // specify if items are objects instead of simple string (automatically set to true if any items in itemsList is typeof object instead of string)
uniqueIdProperty: 'id', // property used as unique ID (key) when items are objects. If not specified, a combination of all the item properties is used as key, which may result in performance issues if list has many items with many properties.
labelProperty: 'name', // field used for display when items are objects
labelTemplate: 'Name: {name}', // template used for display when items are objects, and more complex that just one labelProperty. {} is used as formatter for property name the object.
emptyStateText: 'All options were selected', // text to display when all available items are selected
filteredStateText: 'Some options are hidden by filter', // text to display if some items are hidden by filter
removeSelectedItemText: 'Remove {label}', // tooltip text template for remove button shown on each selected item. {label} is used as placeholder for label text.
removeAllText: 'Remove all items from selection', // tooltip text on the button for unselecting all selected items that will remove all items from selection
groupBy: 'groupProperty', // field to be used as grouping
ungroupedHeader: 'Ungrouped', // Group header to be used for all items that does not have any value in groupBy field
groups: [{ // array with groups instead of specifying groups with itemsList/groupBy field
label: 'Group 1', // display name for group header
items: [item1, item2, ...], // values available in this group
}, ...],
groupDefaultCollapsed: false, // set to true if groups should be collapsed as default
hideGroupCheckbox: false, // set to true if checkbox should not be shown for group header
hideGroupCollapseButton: false, // set to true if groups should not be collapsable/expandable. This will override groupDefaultCollapsed to always be false.
horizontalScroll: false // set to true if selected elements should not wrap, but scroll horizontally instead
}
);
If you have a <label>
element that should activate the dropdown on click, you need to specify the inputId
option
to same value as the for
atttribute on the <label>
element. This will set the id for the internal <input>
element
that is used as a focus element for the MultiSelect control, in addition to being the search field for filtering values.
Alternatively, you can set the for
atttribute on the <label>
element to the id
atttribute on the initial DOM node. If this is done,
the for
atttribute on the <label>
element will be automatically changed to the id for the internal text box inside the multiselect that will handle focus.
Do not put the control inside a label element, because it contains several buttons (for removing items and toggle dropdown list). Doing so will cause strange behaviour, as the buttons will act as triggers for the label.
Label color can be specified with the option labelColor
and can be set to a valid Wave color helper class.
It can also be set to just accent
, which will translate to fill-accent--strong
for filled labels, and fg-accent--moderate
for outlined labels.
Set labelOutline: true
to make the label outlined
instead of the default filled.
All the label options can be overridden per item by having an object property on the item object named _multiSelect
with the label options present to override the main options.
itemsList: [
{
id: 'mrHyde',
title: 'Dr.',
firstName: 'Henry',
lastName: 'Jekyll',
_multiSelect: {
labelColor: 'info--strong',
labelOutline: false,
labelTemplate: '{title} {lastName}'
}
}
]
Component's API
.getSelected()
gets array of the selected items that are currently in the selected labels list.
.getUnselected()
gets array of the unselected items that are currently in the dropdown list.
.setSelected(list)
sets new selected items list that is shown in the selected labels list.
.setUnselected(list)
sets new unselected items list that is shown in the dropdown list.
.unselectItem(item)
moves item from selected list to unselected.
.unselectAll()
moves all items from selected list to unselected.
.on('select', function(item))
event that triggers when an item is selected and moved to the .
.on('unselect', function(item))
event that triggers when the switch value changes.
Usage example
var multiSelectSimple = Wave.MultiSelect(
document.getElementById('multiSelectSimple'),
{
itemsList: ['John', 'Jane', 'Michael', 'Sarah-Jane', 'Billy-Joe', 'Patricia', 'Robert', 'Jennifer', 'Billy', 'William', 'Linda', 'David', 'Elizabeth', 'Richard'],
selectedList: ['Mary']
}
);
multiSelectSimple.on('select', function (item) {
console.log('multiSelectSimple on select', item);
});
multiSelectSimple.on('unselect', function (item) {
console.log('multiSelectSimple on unselect', item);
});
var multiSelectLabel = Wave.MultiSelect(
document.querySelector('.js-multiSelectLabel'),
{
itemsList: [{id: 'mrDoe', name: 'John Doe' }, {id: 'mrsDoe', name: 'Jane Doe' }, {id:'jacquie', name: 'Jacqueline Madelina Vandroogenbroeck'}],
selectedList: [{id: 'missSmith', name: 'Mary Smith' }],
inputId: 'multiSelectLabel',
labelColor: 'success--strong',
labelProperty: 'name'
}
);
var multiSelectComplex = Wave.MultiSelect(
document.querySelector('.js-multiSelectComplex'),
{
itemsList: [{id: 'mrDoe', title: 'Mr.', firstName: 'John', lastName: 'Doe' }, {id: 'mrsDoe', title: 'Mrs.', firstName: 'Jane', lastName: 'Doe' }, {id: 'missSmith', title: 'Miss', firstName: 'Mary', lastName: 'Smith' }, { id: 'mrHyde', title: 'Dr.', firstName: 'Henry', lastName: 'Jekyll', _multiSelect: {labelColor: 'success--strong', labelOutline: false, labelTemplate: '{title} {lastName}'} }, {id: 'jacquie', title: 'de', firstName: 'Jacqueline Madelina', lastName: 'Volkenstrasse Vandroogenbroeck' }],
selectedList: ['missSmith', 'mrDoe'],
inputId: 'multiSelectComplex',
labelOutline: true,
labelColor: 'fg-neutral',
labelTemplate: '<small>{title}</small> {lastName}, {firstName}',
uniqueIdProperty: 'id',
removeAllText: 'Remove all names from selection',
emptyStateText: 'All names were selected',
filteredStateText: 'Some names are filtered out based on what you write',
horizontalScroll: true
}
);
console.log('multiSelectComplex.getSelected()', multiSelectComplex.getSelected());
console.log('multiSelectComplex.getUnselected()', multiSelectComplex.getUnselected());
var list = multiSelectComplex.getUnselected();
list.unshift({id: 'mrNew', title: 'Mr.', firstName: 'Carl', lastName: 'New' })
multiSelectComplex.setUnselected(list);
First example is simple multi-select where each item is text only.
In next example, each item is object with 2 properties where one property is specified as label.
In next example, each item is object with multiple properties where id is configured, and also
a label template that defines how the properties should be shown. In this example,
all available items are defined in the itemsList
array, and only id's
for selected items are specified in the selectedList
array. Also horizontal scroll option is present.
Grouped in list
In next 2 examples, items are grouped.
First where simple text items are only defined in a groups
array.
Next is items defined like earlier examples as objects in itemsList
array,
but with property specified in groupedBy
that should act as grouping.
When using groups, the selected items are not removed from dropdown list. There are checkboxes that indicate which are selected in addition to the item is added as label in the selected box.
Usage example
var multiSelectGroupSimple = Wave.MultiSelect(
document.querySelector('.js-multiSelectGroupSimple'),
{
groups: [
{ labelColor: 'info--strong', label: 'Male', items: ['John', 'Michael', 'Billy-Joe', 'Robert', 'Billy', 'William', 'David', 'Richard']},
{ labelColor: 'fg-danger--moderate', labelOutline: true, label: 'Female', items: ['Mary', 'Jane', 'Sarah-Jane', 'Patricia', 'Jennifer', 'Linda', 'Elizabeth']}
],
selectedList: ['Mary'],
groupDefaultCollapsed: true,
inputId: 'multiSelectGroupSimple'
}
);
var multiSelectGroupObjects = Wave.MultiSelect(
document.querySelector('.js-multiSelectGroupObjects'),
{
itemsList: [{id: 'mrDoe', name: 'John Doe', job: 'Teacher'}, {id: 'mrsDoe', name: 'Jane Doe'}, {id: 'missSmith', name: 'Mary Smith', job: 'Teacher'}, {id:'jacquie', name: 'Jacqueline Madelina Vandroogenbroeck', job: 'Athlete'}, { id: 'mrHyde', job: 'Scientist', name: 'Jekyll', _multiSelect: {labelColor: 'success--strong', labelOutline: false, labelTemplate: 'Dr. {name}'} }],
groupBy: 'job',
ungroupedHeader: 'Unemployed',
inputId: 'multiSelectGroupObjects',
labelProperty: 'name',
uniqueIdProperty: 'id',
filteredStateText: 'Some names are hidden by filter',
hideGroupCheckbox: true,
hideGroupCollapseButton: true
}
);
Toggle switch
Use Toggle switch if you need to represent the switching between two mutually exclusive states or on-off state. The difference between this component and regular checkbox is that Toggle switch should result in an immediate action. Use it for binary settings when changes take effect right after the user flips the toggle. On the contrary, when the user has to perform some extra steps (like cliking a "Submit" button) to apply changes, use a regular checkbox.
Customizable options
var toggleSwitch = Wave.ToggleSwitch(
element, // a DOM node to bind the toggle switch to
{
value: true, // initial value of the toggle switch (false is default)
isDisabled: false, // whether the toggle switch is initially disabled (false is default)
}
);
Component's API
.activate()
sets switch ON.
.deactivate()
sets switch OFF.
.toggle()
toggles switch.
.getValue()
gets the value of the switch (true
or false
).
.disable()
disables changing the state of the switch.
.enable()
enables changing the state of the switch.
.on('change', function(value))
event that triggers when the switch value changes.
Usage example
Wave.ToggleSwitch(
document.querySelector('.js-toggleSwitchEnabledAndChecked'),
{ value: true }
);
Wave.ToggleSwitch(document.querySelector('.js-toggleSwitchEnabledAndUnchecked'));
Wave.ToggleSwitch(
document.querySelector('.js-toggleSwitchDisabledAndChecked'),
{
value: true,
isDisabled: true
}
);
Wave.ToggleSwitch(
document.querySelector('.js-toggleSwitchDisabledAndUnchecked')
).disable();
Do not replace the "On" or "Off" labels unless there are other labels (no longer than three of four characters) that are more specific for the setting.
File input
Component for presenting custom interface for opening the file picker
Customizable options
var fileInput = Wave.FileInput(
element, // a DOM (input[type="file"]) node to create an instance of fileInput
{
labelTxt: '{number} filer valgt', // the text that will show up in the label when multiple files are selected. {number} is a placeholder that will get replaced with the number of selected files
onChange: function(fileInput, fileList) {} // callback function to trigger when user selects new file(s). Receives instance of fileInput as the first argument and the FileList object as the second
}
);
Component's API
.on('changed', function(files))
receives the onchange
DOM event of the element. FileList
object representing the files selected by the user is passed as the parameter. See FileList for more details.
.enable()
enables file input.
.disable()
disables file input.
.destroy()
reverts the file input back to its original state and removes all event listeners.
Things to keep in mind:
- Internet Explorer 9 does not support HTML5 file API and thus the ability to select multiple files for a single input. The list will contain only one file object with a
name
property.
Usage example
var fileInput = Wave.FileInput(document.querySelector('.js-fileInput'), {
onChange: function (fileInput, fileList) {
var output = [];
for (var i = 0, file; file = fileList[i]; i++) {
output.push('<li><b>', escape(file.name), '</b> (', file.type || 'n/a', ')</li>');
});
document.querySelector('.js-fileListOutput').innerHTML = '<ul>' + output.join('') + '</ul>';
}
});
fileInput.on('changed', function (fileList) {
console.log(fileList);
});
Toggle code examples to view the required HTML code around the file input.
Number stepper
Component for cross-browser number inputs
Number stepper is based upon the native <input type="number">
so it will automatically detect the minimum, maximum and increment values based on the min, max and step attributes. You can also use keyboard arrow keys to increment and decrement the value.
Customizable options
var numberStepper = Wave.NumberStepper(
el, // a DOM node (input[type="number"]) to bind the stepper to
{
stepperClass: '' // any additional class to append to the input wrapper
delay: 200 // delay of the stepper's value update in ms when the mouse is kept pressed on up/down button (default is 150)
}
);
Component's API
.enable()
enables number stepper.
.disable()
disables number stepper.
.destroy()
reverts the input
element back to its original state and removes all event listeners.
Usage example
var numberStepper = Wave.NumberStepper(document.querySelector('.js-numberStepper'), {
stepperClass: 'w20'
});
$(document.querySelector('.js-toggleNumberStepperState')).on('click', function (e) {
e.preventDefault();
return (this.state = !this.state) ? numberStepper.disable() : numberStepper.enable();
});
Autosize textarea
Automatically adjust textarea height based on user input
var textareaAutosize = Wave.TextareaAutosize(
element // a DOM node (textarea) to autosize as text is entered
);
Usage example
$(document.querySelectorAll('.js-autoSize')).each(function(i, el) {
Wave.TextareaAutosize(el);
});
Mobile input
Convert regular text input fields to tap-optimized mobile input types on touch devices
Mobile input types can help improve the experience and accessibility. See a short overview of mobile input types
Configurable options
var mobileInput = Wave.MobileInput(
element, // a DOM (input) node
{
type: 'date' // specifies which input type should be used (e.g. "date" or "number")
}
);
Component's API
.isConverted()
returns a boolean whether the input
element is converted to mobile input type.
.destroy()
reverts the input
element back to its original state.
Things to keep in mind when validating:
- After converting to
<input type="date">
the field value is always represented as ISO-8601 date format (yyyy-MM-dd) regardless of the presentation format andpattern
attribute. Submit the form below to see the returned values.
Usage example
$(document.querySelectorAll('.js-nativeTime')).each(function(i, el) {
Wave.MobileInput(el, {
type: 'time'
});
});
$(document.querySelectorAll('.js-nativeDate')).each(function(i, el) {
Wave.MobileInput(el, {
type: 'date'
});
});
Calendar
Calendar component allows the user to choose a date
Configurable options
var calendar = Wave.Calendar({
date: new Date(), // date to be shown (native Date object), defaulting to now
i18n: { // language configuration object
months: [], // array containing labels for each month
days: [] // array containing labels for each day
},
firstDay: 1 // first day of the week, it can be 1 if week starts from Monday or 0 if from Sunday and so on (defaulting to 1)
});
Component's API
.select(date)
selects the given date (native Date
object).
.show(date)
shows the given date (native Date
object). This does not select the given date, it simply ensures that the date is visible in the current view.
.prev()
shows the previous month.
.next()
shows the next month.
.min(date)
defines earliest valid date (inclusive).
.max(date)
defines latest valid date (inclusive).
.showMonthSelect()
adds month selection input.
.showYearSelect([from], [to])
adds year selection input, with optional range specified by from
and to
, which defaults to the current year -10 / +10.
.on('view change', function(date, action))
Event that triggers when the viewed month/year is changed without modification of the selected date. Callback is passed the Date
object and action that caused the view to change ("prev", "next", "month" or "year").
.on('change', function(date))
Event that triggers when the selected date is modified. Callback is passed the Date
object.
Usage example
var calendarOne = Wave.Calendar();
calendarOne.on('change', function (date) {
console.log('[Wave.Calendar] selected: %s of %s %s',
date.getDate(),
date.getMonth(),
date.getFullYear()
);
});
calendarOne.on('view change', function (date, action) {
console.log('[Wave.Calendar] change %s', action);
});
calendarOne.addClass('pAs bdrAs bdr--gray20');
document.querySelector('.js-calendarOneContainer').appendChild(calendarOne.el);
var calendarTwo = Wave.Calendar({
i18n: {
months: ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'],
days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag']
}
})
.showMonthSelect()
.showYearSelect()
.min(new Date(2008, 5, 12))
.max(new Date(2008, 7, 19))
.select(new Date(2008, 7, 19));
calendarTwo.addClass('pAs fill-neutral--minimal');
document.querySelector('.js-calendarTwoContainer').appendChild(calendarTwo.el);
Date picker
Date picker built on calendar component
Configurable options
var datePicker = Wave.DatePicker(
element, // a DOM (input) node
{
format: 'dd.mm.yyyy', // format of the date you expect datepicker to produce, defaulting to "YYYY-MM-DD"
i18n: { // language configuration object
months: [], // array containing labels for each month
days: [] // array containing labels for each day
},
firstDay: 1 // first day of the week, it can be 1 if week starts from Monday or 0 if from Sunday and so on (defaulting to 1)
useNativePickerOnTouchCapableDevices: true, // whether to switch to native picker on touch capable devices (true by default)
triggerChangeOnEmpty: false, // whether to emit change event with `null` value when date picker's input is emptied (false by default)
popoverClass: '' // any additional class to append to date picker's popover
popoverNode: HTMLElement // a DOM node to which the date picker's popover will be appended (defaulting to document.body)
}
);
When useNativePickerOnTouchCapableDevices
option is enabled, Date picker uses Mobile Input internally to switch to native date picker control on touch devices that support it. API methods below like .show()
, .hide()
or .position()
will have no effect with the native control. Use .isNative()
method to detect whether the native control has actually been invoked.
Component's API
.value(date)
gets or sets current value. Value is being set and retrieved as native Date
object. date
argument is optional.
.show()
makes the date picker visible.
.hide()
hides the date picker.
.position(position)
sets the position of the popover relative to the element, i.e. "top"|"left"|"right"|"bottom right"|"top left" ("bottom" is default).
.isNative()
returns a boolean specifying whether the native mobile date picker is invoked on touch devices.
.min(minDate)
defines minimum selectable date via picker. minDate
argument accepts a Date
object or date string that is parsable.
.max(maxDate)
defines maximum selectable date via picker. maxDate
argument accepts a Date
object or date string that is parsable.
.on('change', function(date))
event that triggers when the date is changed. Callback is passed the Date
object.
.on('show', function())
event that triggers when the date picker becomes visible.
.on('hide', function())
event that triggers when the date picker becomes hidden.
Usage example
var datePicker = Wave.DatePicker(document.querySelector('.js-datePicker'), {
format: 'dd.mm.yyyy',
i18n: {
months: ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'],
days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag']
}
});
console.log(datePicker.value()); // => currently selected date as a Date instance
datePicker.on('change', function (date) {
console.log('[Wave.DatePicker] new date: %d of %d %d',
date.getDate(),
date.getMonth(),
date.getFullYear()
);
});
Time picker
Component for time selection
Configurable options
var timePicker = Wave.TimePicker(
element, // a DOM (input) node
{
i18n: { // language configuration object
hours: '', // label for the hours (String)
minutes: '' // label for the minutes (String)
},
minuteStep: 5 // the amount of time, in minutes, between each minute item. Can be 5, 10 or 15 (defaults to 1 if not specified)
useNativePickerOnTouchCapableDevices: true, // whether to switch to native picker on touch capable devices (true by default)
popoverClass: '' // any additional class to append to time picker's popover
popoverNode: HTMLElement // a DOM node to which the time picker's popover will be appended (defaulting to document.body)
}
);
When useNativePickerOnTouchCapableDevices
option is enabled, Time picker uses Mobile Input internally to switch to native time picker control on touch devices that support it. API methods below like .show()
, .hide()
, .position()
, .min()
or .max()
will have no effect. Use .isNative()
method to detect whether the native control has actually been invoked.
Component's API
.value(time)
gets or sets current value. Value is being set and retrieved as { hour, minute }
object. time
argument is optional.
.show()
makes the time picker visible.
.hide()
hides the time picker.
.position(position)
sets the position of the popover relative to the element, i.e. "top"|"left"|"right"|"bottom right"|"top left" ("bottom" is default).
.min(minTime)
defines minimum selectable time (inclusive). minTime
argument is an object with { hour, minute }
shape.
.max(maxTime)
defines maximum selectable time (inclusive). maxTime
argument is an object with { hour, minute }
shape.
.isNative()
returns a boolean specifying whether the native mobile time picker is invoked on touch devices.
.on('change', function(time, complete))
event that triggers when the time is changed. Callback is passed time
object with hour
and minute
properties, complete
is true only if both hours and minutes have been clicked by the user.
.on('show', function())
event that triggers when the time picker becomes visible.
.on('hide', function())
event that triggers when the time picker becomes hidden.
Usage example
var timePicker = Wave.TimePicker(document.querySelector('.js-timePicker'), {
i18n: {
hours: 'Time',
minutes: 'Minutt'
}
});
console.log(timePicker.value()); // => currently selected time ({ hour, minute })
timePicker.on('change', function (time) {
console.log('[Wave.TimePicker] new time: %d:%d',
time.hour,
time.minute
);
});
Sticky
Component for sticking elements to the top of the viewport when the user scrolls down
Sticky makes elements stick and remain visible as the user scrolls. Elements alternate between being in-flow and having position: fixed
as soon as they reach the top of the viewport.
Configurable options
Wave.Sticky(
element, // a DOM node || a list of DOM nodes
{
top: 50, // offset from the top of the window to stick the element (default is 0)
zIndexBase: 100, // base z-index for elements that are stuck (default is 0)
zIndexManagement: false, // whether z-index is changed when stuck/unstuck (default is true)
stickyClass: 'my-sticky', // class name added to elements that are stuck
placeholderClass: 'my-stickyPlaceholder', // class name for placeholder elements (placeholders are used to keep the original page height)
onStuck: function(element) {}, // callback function to trigger when an element is stuck
onUnstuck: function(element) {} // callback function to trigger when an element is unstuck
}
);
Component's API
.update()
updates position of the element(s). Attach this method to the scroll event of the window (or another element).
.destroy(element)
removes sticky functionality from the given node.
Usage example
var stickyInstance = Wave.Sticky(document.querySelectorAll('.js-sticky'), {
top: 56,
zIndexBase: 100,
onStuck: function (element) {
console.log('[Wave.Sticky] element is stuck!');
}
});
$(window).on('scroll', function() {
stickyInstance.update();
});
Bacon ipsum dolor sit amet chuck prosciutto landjaeger ham hock filet mignon shoulder hamburger pig venison. Ham bacon corned beef, sausage kielbasa flank tongue pig drumstick capicola swine short loin ham hock kevin.
Bacon ipsum dolor sit amet chuck prosciutto landjaeger ham hock filet mignon shoulder hamburger pig venison. Ham bacon corned beef, sausage kielbasa flank tongue pig drumstick capicola swine short loin ham hock kevin.
Image container
Component for filling-in/fitting-in images into containers
This component lets you scale any image to fit in a certain way into a defined container area.
Image container is a cross-browser alternative to the CSS3 image-fit
property that is not supported in every browser, namely Internet Explorer.
By default container will fit the image. The image will not be scaled.
You can scale the image proportionally to fill container's width, height, or both width and height:
You can center the image within the container. If the image is larger than the container all sides are cropped to center the image. If, on the other hand, the image is smaller, it is centered in the empty space of the container.
The image can be contained in the container. If the image has a wider aspect ratio (landscape) than the container, it is scaled to fit container's width and the top and bottom are empty. If the image has a taller aspect ratio (portrait) then the container, it is scaled to fit container's height and the sides are empty.
You can also make the image cover the container. If the image has a wider aspect ratio (landscape) than the container, it is scaled to fit container's height and the sides are cropped evenly. If the image has a taller aspect ratio (portrait) then the container, it is scaled to fit container's width and the top and bottom are cropped evenly.
Responsive embed
Component for responsive, intrinsic ratio embeds
Intrinsic ratio is a technique to fluidly constrain a child element to a ratio set in their parent element. The child element will adapt to different screen dimensions, while also maintaining the specified aspect ratio.
This component utilizes the ratio utilites. Use them to set the intrinsic ratio. Default ratio is 1:1.
4:3 ratio below large breakpoint
You can use this technique to make different types of embedded content (iframes, images etc.) responsive: