pinny

A mobile first content fly-in UI plugin

Usage no npm install needed!

<script type="module">
  import pinny from 'https://cdn.skypack.dev/pinny';
</script>

README

Mobify Pinny

A mobile-first content fly-in UI plugin.

NPM Dependency Status Circle CI

Pinny in action

Dependencies

Installation

Pinny can be installed using NPM:

npm install pinny

Usage with Require.js

We highly recommend using Require.js with Pinny. To use Require, you have to reference Pinny, Pinny's effect modules, and Pinny's dependencies inside your require config file (note: if your project already has those external dependencies, such as deckard, and the versions are compatible, it's recommended that you use the one in your project to reduce duplication):


{
    'paths': {
        'plugin': 'node_modules/pinny/node_modules/plugin/dist/plugin.min',
        'pinny': 'node_modules/pinny/dist/pinny.min',
        'modal-center': 'node_modules/pinny/effect/modal-center',
        'sheet-bottom': 'node_modules/pinny/effect/sheet-bottom',
        'sheet-left': 'node_modules/pinny/effect/sheet-left',
        'sheet-right': 'node_modules/pinny/effect/sheet-right',
        'sheet-top': 'node_modules/pinny/effect/sheet-top',
        'shade': 'node_modules/pinny/node_modules/shade/dist/shade.min',
        'lockup': 'node_modules/pinny/node_modules/lockup/dist/lockup.min',
        'deckard': 'node_modules/pinny/node_modules/deckard/dist/deckard.min',
        'bouncefix': 'node_modules/pinny/node_modules/bouncefix.js/dist/bouncefix.min'
        'event-polyfill': 'node_modules/pinny/src/js/utils/event-polyfill',
        'velocity': 'node_modules/pinny/node_modules/velocity-animate/velocity.min',
        'isChildOf': 'node_modules/pinny/node_modules/selector-utils/src/selector/isChildOf',
    }
}

And then require Pinny in as needed:

define([
    '

,
    'modal-center',
    'pinny'
    ],
    function($, modalCenter) {
        $('.pinny').pinny({
            effect: modalCenter
        });
    }
);

Usage

Pinny requires very minimal markup. All Pinny needs is a div with your content and it will automatically transform into what we need.

To avoid any unwanted FOUT, decorate the content you will be passing to Pinny with the hidden attribute. The hidden attribute will be removed when Pinny is initialized.

For accessibility and functional purposes, Pinny will wrap all of your body content in a wrapping container. This could conflict with other plugins that alter your page's markup. If you're seeing issues, try initializing Pinny after your other plugins. If you want to specify your own wrapping container, you can pass Pinny a container option.

<!-- Include the CSS -->
<link rel="stylesheet" href="pinny.min.css">

<!-- Optionally include the Theme file -->
<link rel="stylesheet" href="pinny-theme.min.css">

<!-- Optionally include a wrapping container -->
<div id="bodyContent" class="pinny__body-wrapper">
    Your specified body content
</div>

<!-- Include the markup -->
<div id="yourPinny" hidden>
    Your pinny content
</div>

<!-- Include dependencies -->
<script src="jquery.min.js"></script>
<script src="velocity.min.js"></script>
<script src="plugin.min.js"></script>
<script src="shade.min.js"></script>
<script src="lockup.min.js"></script>
<script src="deckard.min.js"></script>
<script src="bouncefix.min.js"></script>

<!-- Include the effect module you want to use -->
<script src="effect/modal-center.js"></script>
<!-- Include pinny.js -->
<script src="pinny.min.js"></script>

<!-- Construct Pinny -->
<script>$('#yourPinny').pinny()</script>

Initializing the plugin

pinny()

Initializes the pinny.

$('#myPinny').pinny({
    effect: modalCenter
});

You can also initialize the Pinny through the use of a data attribute. The attribute takes a value equal to the effect you want to use.

<div id="myPinny" data-pinny="sheet-bottom">

You must pass Pinny an effect for it to work.

pinny(options)

Initialize with options.

$('#myPinny').pinny({
    effect: sheetBottom,
    container: '#container',
    structure: {
        header: 'My Pinny Title',
        footer: false
    },
    zIndex: 2,
    cssClass: 'my-pinny-class',
    coverage: '100%',
    easing: 'swing',
    duration: 200,
    shade: {
        color: '#404040'
    },
    open: noop,
    opened: noop,
    close: noop,
    closed: noop
});

Options

effect

default: { open: noop, close: noop },

Specifies which effect module Pinny should use when opening. Effect modules allow you to load specific functionality that tell Pinny how to open and close. Available effect modules can be found in the dist/effect folder. Current effect modules include:

  • Modal Center - opens Pinny in the center of the screen
  • Sheet Top - slides down from the top of the screen
  • Sheet Bottom - slides up from the bottom of the screen
  • Sheet Left - slides in from the left of the screen
  • Sheet Right - slides in from the right of the screen
$('#myPinny').pinny({
    effect: sheetLeft
});

container

default: <body>

Specify the container Pinny will be created within

$('#myPinny').pinny({
    container: $('#mainForm') // or container: '#mainForm'
});

appendTo

default: null

Specify the element Pinny will be appended to. By default Pinny will be appended to the lockup container. If you want it to be appended outside the lockup container, specify that element here.

$('#myPinny').pinny({
    appendTo: 'body'
});
structure

default: { header: '', footer: false }

Defines the structure to use for Pinny. Specifically, Pinny tries to build its own HTML structure if passed the default options.

If you want to have full control over the HTML of your Pinny, including the header, footer, and content section, set structure: false. Setting structure: false will still allow the close event to be bound to any element that has the pinny__close class, allowing you to specify the element that should trigger closing your Pinny.

If you are using structure: false, you will need to structure your HTML to include the following elements (missing any elements will cause Pinny to not function):

<div id="myPinny" class="pinny__wrapper" role="document" hidden>
    <div class="pinny__header">
        <button type="button" class="pinny__close">close</button>
    </div>
    <div class="pinny__content pinny--is-scrollable"></div>
    <div class="pinny__footer"></div>
</div>

Please see below for available sub-options for header and footer.

structure.header

default: ''

Sets the header that Pinny should use in its header bar. Valid values are:

  • boolean - specifies no default header generated. If chosen, the user is required to specify the header markup themselves, including the appropriate class, pinny__header. It will be expected that this will be a part of the element that is used to invoke pinny.
  • string - specifies the title text used in the header. The header structure will be generated automatically.
  • html|element - specifies the HTML to be used for the header.
// generates no header
$('#myPinny').pinny({
    structure: {
        header: false
    }
});

or

// generates a default header with the title "My Pinny"
$('#myPinny').pinny({
        structure: {
            header: 'My Pinny'
        }
    });

or

$('#myPinny').pinny({
    structure: {
        header: '<header class="pinny__header">My Pinny<button class="pinny__close">Close</button></header>'
    }
});
structure.footer

default: false

Sets the footer that Pinny should use in its footer. Valid values are:

  • boolean - specifies no default footer generated. If chosen, the user is required to specify the footer markup themselves, including the appropriate class, pinny__footer.
  • string - specifies the title text used in the footer. The footer structure will be generated automatically.
  • html|element - specifies the HTML to be used for the footer.
// generates no footer
$('#myPinny').pinny({
    structure: {
        footer: false
    }
});

or

// generates a default footer with the contents "My Footer"
$('#myPinny').pinny({
    structure: {
        footer: 'My Footer'
    }
});

or

$('#myPinny').pinny({
    structure: {
        footer: '<footer class="pinny__footer">My Footer</footer>'
    }
});
zIndex

default: 2

Sets the z-index value for Pinny. Use this value if you need to specify a specific stacking order.

$('#myPinny').pinny({
    zIndex: 10
});
cssClass

default: ''

Sets a class to apply to the main Pinny parent element for ease of styling.

#('#myPinny').pinny({
    cssClass: 'my-pinny-class'
});
coverage

default: 80%

Sets the coverage value. This will allow you to specify that the pinny covers only a portion of the screen.

$('#myPinny').pinny({
    coverage: '80%'
});
duration

default: 200

Sets the duration for the animation.

$('#myPinny').pinny({
    duration: 600
});
shade

default: {}

Specifies whether pinny should use the shade overlay. You can pass options through to Shade using this property. For more information on available options, see the Shade documentation.

Warning: We currently force Shade's duration to match the one provided to Pinny. This is to limit DOM thrashing as much as possible during Pinny's animation. Pinny hitches a little if we don't do this.

$('#myPinny').pinny({
    shade: {
        color: '#333333'
    }
});
easing

default: swing

Sets the easing for the animation. Pinny takes all of the same easing properties that Velocity.js accepts.

  • jQuery UI's easings and CSS3's easings ("ease", "ease-in", "ease-out", and "ease-in-out"), which are pre-packaged into Velocity. A bonus "spring" easing (sampled in the CSS Support pane) is also included.
  • CSS3's bezier curves: Pass in a four-item array of bezier points. (Refer to Cubic-Bezier.com for crafing custom bezier curves.)
  • Spring physics: Pass a two-item array in the form of [ tension, friction ]. A higher tension (default: 600) increases total speed and bounciness. A lower friction (default: 20) increases ending vibration speed.
  • Step easing: Pass a one-item array in the form of [ steps ]. The animation will jump toward its end values using the specified number of steps.

For more information, check out Velocity's docs on easing.

$('#myPinny').pinny({
    easing: 'ease-in-out'
});
open

default: function(e, ui) {}

Triggered every time the selected pinny item is starting to open.

Parameters

Parameter name Description
e An Event object passed to the callback
ui An object containing any associated data for use inside the callback
$('#myPinny').pinny({
    open: function(e, ui) {
        // ui.item contains the item opening
    }
});
opened

default: function(e, ui) {}

Triggered every time the selected pinny item has finished opening.

Parameters

Parameter name Description
e An Event object passed to the callback
ui An object containing any associated data for use inside the callback
$('#myPinny').pinny({
    opened: function(e, ui) {
        // ui.item contains the item that opened
    }
});
close

default: function(e, ui) {}

Triggered every time an pinny item is starting to close.

Parameter name Description
e An Event object passed to the callback
ui An object containing any associated data for use inside the callback
$('#myPinny').pinny({
    close: function(e, ui) {
        // ui.item contains the item closing
    }
});
closed

default: function(e, ui) {}

Triggered every time an pinny item is finished closing.

Parameter name Description
e An Event object passed to the callback
ui An object containing any associated data for use inside the callback
$('#myPinny').pinny({
    closed: function(e, ui) {
        // ui.item contains the item that closed
    }
});

Methods

Open

Open the selected pinny item by element reference

$pinny.pinny('open');

Close

Close the selected pinny item by element reference

$pinny.pinny('close');

Pinny will automatically trigger close on elements decorated with the class name pinny__close.

<button class="pinny__close">Close</button>

Browser Compatibility

Browser Version Support
Mobile Safari 5.1.x Degraded. Form inputs cause scroll issues while typing.
Mobile Safari 6.0+ Supported.
Chrome (Android) 1.0+ Supported.
Android Browser 4.0+ Degraded. No scroll locking.
IE for Win Phone 8.0+ Degraded. No scroll locking.
Firefox (Android) 23.0+ Supported. (Support may exist for earlier versions but has not been tested)

Known Issues

Currently, form inputs and selects inside of Pinny have issues on iOS7 and under. This is due to not being able to lock scrolling without causing rendering issues as well as iOS attempting to scroll the page when the keyboard opens. Forms work but will cause some visual jumping.

Working with Pinny locally

Requirements

Steps

  1. npm install
  2. grunt serve

Grunt Tasks:

  • grunt or grunt build - builds a distributable release
  • grunt watch - watches for changes and builds when changes are detected.
  • grunt serve - runs the server, building changes and then watching for changes. Use grunt serve to preview the site at http://localhost:3000
  • grunt test - runs Pinny's test suite in your console
  • grunt test:browser - runs a server that allows you to run pinny's test suite in your browser by browsing to http://localhost:8888/tests/runner

The dist directory will be populated with minified versions of the css and javascript files for distribution and use with whatever build system you might use. The src directory has our raw unminified Sass and Javascript files if you prefer to work with those.

Releasing Pinny

To release a new version of Pinny, follow our RELEASE guide.

License

MIT License. Pinny is Copyright © 2014 Mobify. It is free software and may be redistributed under the terms specified in the LICENSE file.