Case Study: Stone Fest 21 – PWA

Living in San Diego, we are surrounded by over 140 craft breweries. Once a year, Stone Brewing hosts an annual celebration, where they invite a bunch of their brewery friends to town and showcase their brews. I have been attending for a number of years, and one of the challenges in attending was having to find the beer on Untappd and check-in. So this year I thought I would whip up a quick Progressive Web App (PWA), that would allow me to quickly locate my beer and check in. Here is a link to the working Stone Fest 21 app.

After assembling the data set of the breweries, the beers, their logos (thanks, Untappd!), I generated the actual Ionic application. Since my buddies were on both iOS and Android, I knew the web was my only publishing option. But before I uploaded the code from Ionic to my server, there were some additional steps required to improve the performance of the PWA. Here is a list of the changes I made to project:

Enable gzip

By default, my Dreamhost account that I hosted Stone Fest 21 on did not have this enabled. So, I had to create a .htaccess file and enable it on the server.

< ifmodule mod_deflate.c>
AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript
< /ifmodule>

Improving iOS support

Since PWA support on iOS is not on par with Android was to include some Apple specific meta tags in the head of the index.html file:

< meta name="apple-mobile-web-app-capable" content="yes">
< meta name="apple-mobile-web-app-status-bar-style" content="black">
< meta name="apple-mobile-web-app-title" content="Stone Fest 21">

For more on these tags, see the Apple documentation.

App Icons

Although the manifest.json defines our app icon, not all platform understand this. So as a backup, I add this snippet in the index.html file to assist with that issue:

< link rel="apple-touch-icon" sizes="57x57" href="apple-icon-57x57.png">
< link rel="apple-touch-icon" sizes="60x60" href="apple-icon-60x60.png">
< link rel="apple-touch-icon" sizes="72x72" href="apple-icon-72x72.png">
< link rel="apple-touch-icon" sizes="76x76" href="apple-icon-76x76.png">
< link rel="apple-touch-icon" sizes="114x114" href="apple-icon-114x114.png">
< link rel="apple-touch-icon" sizes="120x120" href="apple-icon-120x120.png">
< link rel="apple-touch-icon" sizes="144x144" href="apple-icon-144x144.png">
< link rel="apple-touch-icon" sizes="152x152" href="apple-icon-152x152.png">
< link rel="apple-touch-icon" sizes="180x180" href="apple-icon-180x180.png">

I used the $ ionic cordova resources command to generate them, then renamed the manually. Since the $ ionic build command will wipe out the www directory, I added them directly to the server.

Updating the BODY tag

One of the items that the Lighthouse test measures is “First Meaningful Paint”. For those who don’t know this term, First Meaningful Paint is the time when page’s primary content appeared on the screen. I added inline CSS to the body tag, so that the browser would render something while the app was starting.

< body style="background-color: #1c1b17; background-image: url('assets/imgs/logo.jpg'); background-position: center; background-repeat: no-repeat;">

Handling the no JavaScript case

Lighthouse also checks what you do if JavaScript was disabled by the user. Remember, this is a Progressive web app. In my case, there was not a lot you can do without JavaScript, but I included this tag to meet the requirement:

< noscript>
< h1>Stone Fest 21 requires JavaScript< /h1>
< /noscript>

Removing Cordova

When you generate an Ionic application, Cordova plugins are automatically integrated, specifically the Splash Screen and Status Bar plugins. Since we are deploying to the web, we can remove Cordova and these plugins from our project. Using $ npm uninstall @ionic-native/splash-screen and $ npm uninstall @ionic-native/status-bar

Edit the app.module.ts to remove the import statements for these two plugins and remove them from the providers array.

In the app.component.ts file also remove the imports. Also, you will remove the injected imports from the arguments in the constructor, as well as the two references to the plugins in the platform.ready check.

If you generated your Ionic application with Cordova integration, there are other plugins and modules that you should remove as well. If you open your package.json file you will several references to cordova-* items. Go ahead and use npm to uninstall them:

  • cordova-plugin-console
  • cordova-plugin-device
  • cordova-plugin-splashscreen
  • cordova-plugin-statusbar
  • cordova-plugin-whitelist
  • and ionic-plugin-keyboard

Depending on what platforms you may have installed, you might also have these modules:

  • cordova-ios
  • cordova-android

Remove them as well.

Finally, we can also remove @ionic-native/core, as we have now scrubbed our Ionic application of any Ionic Native code.

Image Paths

As I checked my Lighthouse score, I noticed that it was flagging how my brewery logos were being called, as well as an accessibility issue around them. To quickly solve this, I adjusted the img tag to reference the full path to the logo and added the alt attribute

< img src="https://aj-software.com/apps/stone/assets/breweries/{{brewery.logo}}" alt="Company Logo">

Enabling the Serviceworker.js code

By default, this code block is commented out in the index.html. Uncomment this block of code and you have a nicely configured service worker ready to go.

if ('serviceWorker' in navigator) {
 .then(() => console.log('service worker installed'))
 .catch(err => console.error('Error', err));

Updating the manifest.json file

The last tweak is updating some of the items in the default manifest.json file.

  1. Update the name and short name. Note: the short name should not be more than 12 characters in length.
  2. Ensure there is an icon available in the proper directory and of the proper size.
  3. Update the theme and background colors to something that matched our app.
  4. Add an orientation value of portrait
 "name": "Stone Fest 21",
 "short_name": "Stone Fest",
 "start_url": "index.html",
 "display": "standalone",
 "icons": [{
 "src": "assets/imgs/logo.jpg",
 "sizes": "512x512",
 "type": "image/jpg"
 "background_color": "#1c1b17",
 "theme_color": "#1c1b17",
 "orientation": "portrait"


After implementing these steps, the Stone Fest 21 app received  the following Lighthouse score:


Hopefully, these guidelines can help you create better PWA using Ionic. Until stencil.js is ready, this is about a good as I can make an Ionic-Angular’s performance. I will be porting this application to stencil shortly and will update this post with the results. The source code is available on my GitHub account.

Farewell to the Edge…

Adobe Edge Preview

Adobe announced today that it was ending their Edge Tools and Services experiment. I use the term experiment, as these products were built as part of a transition period for Adobe, shifting away from the Flash Platform toward a web platform stack. I was fortunate enough to be a part of the early development of many of these tools (as an Adobe Community Professional), but their usefulness as stand-alone elements was always troublesome.

For example, there was never a simple workflow to take your Edge Reflow work into a larger web project. Nor was it easy to fully interact with Edge Inspect from your system and devices.

But these experiments did lead to some tangible enhancements in Adobe’s main product lines. The responsive media query tool in Dreamweaver CC is a direct result of the groundwork laid by Reflow. The device preview features in PhotoShop and Illustrator are based in part on Edge Inspect.

But the mother of the Edge Tools and Services was Edge Animate (for those who remember, it was just Edge at first). EA was a interesting tool, focusing on animating the DOM rather than just painting to the HTML canvas. The timeline explored new ideas for animation control. Part of its appeal was that it was not Flash, which carried a dreaded (and partially unwarranted) stigma for many. But like all technologies, the web and its tools evolve. Although you could create some incredibly rich and interactive animations, EA never seemed to fit in. That Flash Professional app kept evolving as well, supporting more formats as publishing options (Canvas, WebGL, OAM). Take a look at last year’s Adobe MAX conference, and you can see that Flash’s animation roots were still strong and growing (judging by the number of sessions).

I have given several web animation talks, and could never quite explain how both tools fit under the Adobe umbrella. With today’s announcement, I will no longer be faced with that issue. Edge Animate’s development has been ended. It is still available as part of your CC subscription (heck, Fireworks is still there!), so you can continue to use it on your projects. However, as the web evolves with new browsers, standards, and libraries, EA will not be updated.

In its place, we can turn to Animate, the renamed Flash Professional. Like a phoenix rising from the ashes, Flash has really returned to its roots as a first class animation tool and is now reborn.

Now, designers can use a tool that they are all probably comfortable with, without worrying about backlash from managers or clients who didn’t understand that the tool was more than just a SWF creator.

I am looking forward to exploring Flash’s Animate’s new future as part of my design toolkit. To all those at Adobe who worked on these products, thanks for the efforts and experiments.


Centering Gallery Elements

Recently, I was rebuilding one of my websites to be responsive. It is important to take advantage of the capabilities of modern browsers and be responsive, to support the growing use of the site on mobile devices. One portion of the design was a set of image gallery pages. These pages are pretty straight-forward, up to 5 columns of photos per row centered within the width of the page. As the window width reduces, the number of columns reduces. Again, nothing out of the ordinary with this design.


Block version of the design


I began by centering the containing div by setting the CSS properties of margin-left and the margin-right values to “auto”. That positioned the containing div correctly and it responded as expected as the browser width was changed. However, as the width of the window reduced, the thumbnails would begin to start to flow down to the next row, but they would display centered within the containing div. Not exactly the design I was looking for.

Centered elements

Centered elements

I applied a float:left to the thumbnails, and my centering last row was fixed! However, it introduced a new problem–the thumbnails no longer appeared to be centered as a group within the page. Adding the float to the thumbnails introduced extra spacing between the last column and the right boundary of the containing div.

Highlighting the margin right issue

Highlighting the margin right issue

I knew that had to be solved, so I began looking at many of my favorite tutorial sites, and found nothing. Lots of great samples of gallery pages, but all using the full width of browser in their samples or the thumbnail sizes would adapt to their widths. Nothing seemed to fit my design.

Next, I decided to look at using Flexbox. I had made the choice to only support “modern” browsers, so I knew that I did not need to worry too much about compatibility issues. But the same issues remained as I experimented with the various flex layout options; the last row items would be centered, or the gap on the right existed. Back to the drawing board!

Then I got to thinking about how the layout was computed. Each thumbnail would take the same fixed amount of space. Each would position itself to the left due to setting the float property to left. If the thumbnail did not have enough space within the width of the container, it would move to the next row. Float 101, right? But how did it know the space it had? I only defined the containing div to have a max-width and to center itself, and the width would vary based on the width of the window. It then struck me that if the width of the containing div matched the number of allowable columns, then there would not be extra space along the right.


If I can set the containing div to match my column widths, that container will still be properly centered. So I calculated the computed widths for 2,3,4, and 5 columns of thumbnails. So, when the browser’s width reaches specific widths, the containing div’s width is fixed to match the allowable number of columns. With a fixed width instead of a dynamic width, it can be properly centered on the page. Design problem solved!
The resulting CSS looks like this

 #photoMenu {
   margin-left: auto;
   margin-right: auto;
   height: auto;
   min-height: 220px;
 .thumbnail {
   position: relative;
   border: 5px solid #ddd;
   float: left;
   margin: 20px;
   width: 160px;
 .thumbnail img {
  max-width: 100%;
 .thumbnail h3 {
   position: absolute;
   bottom: 0;
   left: 0;
   width: 100%;
   margin: 0;
   text-align: center;
   color: white;
   font: bold 1em/1.5em Verdana, Sans-Serif;
   background: rgb(0, 0, 0);
 @media (min-width: 315px) {
   #photoMenu {
     width: 210px;
 @media (min-width: 525px) {
   #photoMenu {
     background-color: darkseagreen;
     width: 420px;
 @media (min-width: 735px) {
   #photoMenu {
     background-color: rebeccapurple;
     width: 630px;
 @media (min-width: 945px) {
   #photoMenu {
     background-color: bisque;
     width: 840px;
 @media (min-width: 1155px) {
   #photoMenu {
     background-color: aqua;
     width: 1050px;

For each of the media query breakpoints, I added a background color just to highlight it. Here is what the HTML looks like:

<div id="photoMenu">
  <div class="thumbnail">
    <img src="thumbnail.png" alt="">
  <div class="thumbnail">
    <img src="thumbnail.png" alt="">
  <div class="thumbnail">
    <img src="thumbnail.png" alt="">
  <div class="thumbnail">
    <img src="thumbnail.png" alt="">
  <div class="thumbnail">
    <img src="thumbnail.png" alt="">
  <div class="thumbnail">
    <img src="thumbnail.png" alt="">
  <div class="thumbnail">
    <img src="thumbnail.png" alt="">

You can see the completed sample here.

Adobe Edge Animate 1.5 released

animate-logoThere has been an update to Edge Animate! This release adds some new CSS capabilities and enhanced text support and improved gradient support. But best of all, it’s still available for free through any subscription tier of Creative Cloud.

Here is a recap of all the new features and capabilities to Edge Animate 1.5:

Edge Web Fonts

You can now browse the Edge Web Fonts library within Edge Animate and apply collection fonts to your compositions. Fonts can be browsed by style and added to the font library at the document level.

CSS Filter Support

The cutting edge of CSS capabilities is making an appearance in the Edge Animate. CSS filter support for:

  • Blur
  • Greyscale
  • Sepia
  • Drop-shadow
  • Brightness
  • Contract
  • Hue-rotate
  • Invert and,
  • Saturate

Are available in the properties panel as animatable effects. Please note: CSS filters are a cutting edge spec and support is only available in Chrome 18 and later, Safari 6, iOS Safari 6 and Blackberry 10.


Apply both radial and linear gradient styling to your elements through the new gradient tool. The new gradients feature allows you to:

  • Add multiple gradient stops to your elements
  • Create animation tweens for gradient colors and positioning
  • Repeat gradients infinitely based on defined color stops
  • Gradient transitions are available in either linear or radial (linear and radial gradient transitions cannot be mixed)
  • Specify CSS properties for radial gradients such as ellipse-farthest-corner, circle-farthest-corner, etc.
  • Control gradient positioning in rotation degrees for linear and x/y percentages for radial.

New Color Picker

The color picker has gotten a refresh to allow for easier color editing.

New Color Modes

Colors can now be defined for both gradients and color using RGBa, HSLa and HEX.

Color Sliders

Color can be easily controlled and applied using new color sliders available in both the color and gradient panels.

Reusable swatches

Swatches can be saved and reused for both gradients and color at the document level.

HiDPI iconography

High resolution icons have been implemented when using Edge Animate on high resolution displays.

CDN Runtime Hosting

Users now have the option of hosting Edge Animate runtime files on the Adobe CDN, allowing caching for faster composition loading. Runtime files with this option selected will not be included on publish export.

Iframe Friendly Preloaders

Preloading is now optimized to work within iframes for coordinated resource loading. Animate compositions contained within iframes can wait for the document to load before signaling the composition to execute, allowing polite preloading for advertisements.

Symbol editing mode preservation on Preview

No longer does your Animate composition return to the stage when previewing in browser. Animate will preserve your current editing state on each preview.

For more information, visit http://html.adobe.com/edge/animate/

Come learn about the Edge Tools and Services!

I am presenting to Orange County Multimedia Association on Tuesday, January 15. I will be demonstrating the entire Edge Tools and Services suite. I will cover everything from Edge Animate, Edge Code, Edge Inspect, to PhoneGap Build, and even a sneak of Edge Reflow. Hope to see you there!

Time: Food and networking 6:00p.m.; Meeting: 6:30-9:00 p.m.
Location: Digital Media Center @ 1300 S. Bristol St. Santa Ana, CA 92704


Update:  My slides are available here. Hopefully, the recording turned out. Once I have the link I will post it here.

StageWebView Loading Improved

Recently, someone asked about trying to improve hiding the white rectangle of the StageWebView. I got to thinking about this and tried a little experiment. Did I have to set the viewPort during the normal configuration, or could I wait?

So, I decided to try it. I moved the setting of the StageWebView’s viewPort until the page had been loaded. We know this via an Event.COMPLETE event. Here is the sample code:

import flash.media.StageWebView;
import flash.events.Event;

var webView:StageWebView = new StageWebView();
webView.stage = this.stage;
//webView.viewPort = new Rectangle(0, 0, 480, 200);


function handleLoad(evt:Event):void
webView.viewPort = new Rectangle(0, 0, 480, 300);


Demo File

Create The Web Event Livestream

The keynote for Adobe’s #CreateTheWeb HTML/Web event Monday will be streamed live online: https://t.co/EGtJFz7Q

 Kevin Lynch (Chief Technology Officer, Adobe)

Danny Winokur (Vice President and General Manager, Interactive Development, Adobe)

Jeffrey Veen (Vice President, Products for Creative Cloud, Adobe)

Adobe is committed to moving the web forward. Through work with the community to advance the graphical capabilities of the web, contributions to community projects, and essential tools and services, Adobe is pushing the expressive capabilities of the web forward and making it easier for web designers and developers to express themselves and create the next generation of web experiences.

In the opening keynote, Adobe will layout its commitment to the web as the primary platform of the future, and will announce and demonstrate new tools and technologies that enable web designers and developers to create the experiences that push the boundaries of what is possible on the web.