Adaptive ADF/WebCenter template for the iPad
- by Maiko Rocha
One of my WebCenter Portal customers was asking about adaptive design with ADF/WebCenter Portal and how they could go about creating an adaptive iPad template for their WebCenter Portal application. They were looking not only for the out-of-the-box support for mobile Safari which is certified against PS5+ (11.1.1.6) for ADF/WebCenter - but also to create a specific template to streamline their workflow on the iPad. Seems like they wanted something in the lines of Yahoo! Mail provides for the iPad - so the example I will use is shamelessly inspired by Y! Mail's iPad UI.
But first, let's quickly understand how can we bake in some adaptive goodness into ADF Faces. First thing we need to understand is, yes, there are a couple of constraints that we will need to work around, namely, the use or layout managers and skins. Please also keep in mind that I'm not and I don't pretend to be a web designer, much less an UX specialist, so feel free to leave your thoughts on the matter in the comments section. Now, back to the limitations.
Layout Managers
ADF Faces layout managers create an abstraction on top of the generated HTML code for a page so a developer doesn't need to be worried about how to size and dimension the UI layout (eg, af:panelStretchLayout). Although layout managers are very helpful, in this specific situation we will need to know a little bit more of how the final HTML is being rendered so we can apply the CSS class accordingly and create transition containers where the media queries will be applied - now, if you're using 11gR2 (11.1.2.2.3) there's the new component af:panelGridLayout (here and here) that will greatly improve creating responsive templates and pages because it is based on the grid/fluid systems and will generate straight out to DIVs on your final page. For now, I'm limited to PS5 and the af:panelStretchLayout component as a starting point because that's the release my customer is on.
Skins
You won't be able to use media queries, or use anything with "@" notation on the skin CSS file - the skin pre-processor will remove all extraneous "@" from the CSS file. The solution is to split your CSS in two separate files: a skin CSS file and plain CSS where you will add the media queries. The issue here is that you won't be able to use media queries for any faces components. We can, though, still apply the media queries for the components like af:panelGroupLayout and af:panelBorderLayout through their styleClass property to enable these components to be responsive to to the iPad orientation, by changing its dimensions, font sizes, hide/show areas, etc.
Difference between responsive and adaptive design
The best definition of adaptive vs responsive web design I could find is this:
“Responsive web design,” as coined by Ethan Marcotte, means “fluid grids, fluid images/media & media queries.” “Adaptive web design,” as I use it, is about creating interfaces that adapt to the user’s capabilities (in terms of both form and function). To me, “adaptive web design” is just another term for “progressive enhancement” of which responsive web design can (an often should) be an integral part, but is a more holistic approach to web design in that it also takes into account varying levels of markup, CSS, JavaScript and assistive technology support.
Responsive/adapative web design is much more than slapping an HTML template with CSS around your content or application. The content and application themselves are part of your web design - in other words, a responsive template is just an afterthought if it is not originating from a responsive design the involves the whole web application/s.
Tips on responsive / adapative design with ADF/WebCenter
Some of the tips listed below were already mentioned in multiple blog posts about ADF layout and skinning, but it is still worth remembering:
a simple guideline for ADF/WebCenter apps would be to first create a high-level group of devices, for example: smartphones, tablets, and desktop. For each of these large groups, create the basic structure to provide responsiveness: a page template, a skin, and an external CSS:
pagetemplate_smartphone.jspx, smartphone_skin.css, smartphone-responsive.css
pagetemplate_tablet.jspx, tablet_skin.css, tablet-responsive.css
pagetemplate_desktop.jspx, desktop_skin.css, desktop-responsive.css
These three assets can be changed on the fly through an user-agent check on the server side, delivering the right UI to the right device. Within each of the assets, you can make fine adjustments for each subgroup of devices with media queries - for example, smart phones with different screen dimensions and pixel density. Having these three groups and the corresponding assets per group seem to be a good compromise between trying to put everything on a single set of assets - specially considering the constraints above - and going to the other side of the spectrum to create assets per discrete device (iPhone4, iPhone5, Nexus, S3, etc.). Keep in mind that these are my rules and are not in any shape or form a best practice - this is how it fits best for the scenarios I've been working with.
If you need to use HTML tags on your page, surround them with af:group to protect the DOM structure
For stretchable/fluid layouts:
Use non-stretching containers: panelGroupLayout, panelBorderLayout, …
panelBorderLayout can be used to approximate HTML table component
To avoid multiple scroll bars, do not nest scrolling PanelGroupLayout components. Consider layout="vertical"
For stretchable/fluid layouts:
Most stretchable ADF components also work in flowing context with dimensionsFrom="auto"
To stretch a component horizontally, use styleClass="AFStretchWidth" instead of "width:100%"
Skinning
Don't use CSS3 @media, @import, animations, etc. on skin css files. They will be removed. CSS3 properties within a class (box-shadow, transition, etc.) work just fine.
Consider resetting some skin classes to better control their rendering:
body {color: inherit;font: inherit;}
af|document {-tr-inhibit: all;}
af|commandLink {-tr-inhibit: all;}
af|goLink {-tr-inhibit: all;}
af|inputText::content {font: inherit;}
Specific meta tags and CSS properties:
Use <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"/> to avoid zooming (if you want)
Use -webkit-overflow-scrolling: touch to enable native momentum scrolling within overflown areas (here)
Use text-rendering: optmizeLegibility to improve readability. (here)
User text-overflow: ellipsis to gracefully crop overflown text. (here)
The meta-tags are included in each and every page in the metaContainer facet of af:document tag. You can also use a javascript to inject the meta-tags from the template. For the purpose of the example, I wanted to use as few workarounds as possible.
The iPad template and sample application
This sample application has been built as a WebCenter Portal application, but you will also be able to reuse the template and techniques on your vanilla ADF application. Keep in mind that I'm neither a designer nor a CSS specialist, so please don't bash me too much on the messy CSS file you'll find on the application.
I've extended the provided PreferencesBean class that comes with WebCenter Portal and added code to dinamically change the template and skin on the fly.
This is the sample application in landscape orientation:
This is the sample application in portrait orientation - the left side menu hides automatically based on a CSS media query:
Another screenshot with a skinned popup opened:
This is a sample application for you to play with - ideally you shouldn't use it as a starting point. On the left side bar you will find links rendered from a WebCenter Portal navigation model - the link triggers a full request through an af:goLink, while the light blue PPR button triggers a PPR navigation. The dark blue toolbar buttons at the top don't have any function,while the Approve and Reject buttons show a skinned popup. The search box of course doesn't have any behavior attahed to it either.
There's a known issue right now with some PPR calls that are randomly generating a 403 error redirecting to the login page - I didn't have time to investigate if this is iOS6 specific or not - if you have any insights please let me know your findings. You can download the sample here.