SlideShare a Scribd company logo
Aesthetics-Driven Development:
Cool Features for Ergonomic
Software Design
Boston Ember.js
September 8, 2016
Stephen Vance
1
Motivation
• Using Bootstrap
• Relying on responsive behavior of navbars
• Some of the behavior relies on JavaScript
• Wanted more Ember-y approach
• ember-bootstrap didn’t support it yet
2
The Navbar
Full Rendering
Responsive Rendering
3
Bootstrap Navbar
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class=“navbar-toggle"
data-toggle="collapse" data-target=".navbar-mwpc-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
{{#link-to "index" class="navbar-brand"}}{{siteBrand.brand}}{{/link-to}}
</div>
<div class="collapse navbar-collapse navbar-mwpc-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class=“dropdown-toggle"
data-toggle="dropdown" role="button" aria-expanded="false">
Service Directory<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
{{#each-in serviceCategories.categories as | category info |}}
<li>
{{link-to info.displayName "service" category}}
</li>
{{/each-in}}
</ul>
</li>
<li>
{{#link-to "resources"}}Local Resources{{/link-to}}
</li>
4
Bootstrap Navbar
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class=“navbar-toggle"
data-toggle="collapse" data-target=".navbar-mwpc-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
{{#link-to "index" class="navbar-brand"}}{{siteBrand.brand}}{{/link-to}}
</div>
<div class="collapse navbar-collapse navbar-mwpc-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class=“dropdown-toggle"
data-toggle="dropdown" role="button" aria-expanded="false">
Service Directory<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
{{#each-in serviceCategories.categories as | category info |}}
<li>
{{link-to info.displayName "service" category}}
</li>
{{/each-in}}
</ul>
</li>
<li>
{{#link-to "resources"}}Local Resources{{/link-to}}
</li>
Navbar
Header
Toggle
Content
Nav
Brand
4
Navbar Structure
Navbar
Header
Toggle
Content
5
Navbar Structure
Navbar
Header
Toggle
Content
Toggles
5
Applying DDAU*
Navbar
Header
Toggle
Content
*Data Down, Actions Up
6
Applying DDAU*
Navbar
Header
Toggle
Content
1. Toggles State
*Data Down, Actions Up
6
Applying DDAU*
Navbar
Header
Toggle
Content
1. Toggles State
2. Passes State
*Data Down, Actions Up
6
Design Concerns
• Peer components shouldn’t reference each other
• Navbar state should be within the component
• Outside would require users to define it
• Nearest common parent
• Support multiple navbars in a page
• Strive for clean DSL
• Minimize exposed plumbing
• Principle of Least Astonishment
7
Implementation Concerns
• Component block form
• Nature of problem doesn’t require inline
• Component isolation makes it harder for related
components to cooperate transparently
8
First Cut 😳
{{#bs-navbar}}
{{#bs-navbar-header}}
{{!-- TODO: Create bs-navbar-toggle? --}}
{{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/bs-button}}
{{!-- TODO
{{#bs-navbar-brand}}Brand{{/bs-navbar-brand}}
--}}
<a class="navbar-brand" href="#">Brand</a>
{{/bs-navbar-header}}
{{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}}
{{#bs-collapse collapse=collapsed
class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}}
{{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}}
{{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}}
{{/bs-nav}}
{{/bs-collapse}}
{{/bs-navbar}}
9
First Cut 😳
{{#bs-navbar}}
{{#bs-navbar-header}}
{{!-- TODO: Create bs-navbar-toggle? --}}
{{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/bs-button}}
{{!-- TODO
{{#bs-navbar-brand}}Brand{{/bs-navbar-brand}}
--}}
<a class="navbar-brand" href="#">Brand</a>
{{/bs-navbar-header}}
{{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}}
{{#bs-collapse collapse=collapsed
class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}}
{{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}}
{{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}}
{{/bs-nav}}
{{/bs-collapse}}
{{/bs-navbar}}
Plumbing
Plumbing
Plumbing
Plumbing
9
First Cut 😳
{{#bs-navbar}}
{{#bs-navbar-header}}
{{!-- TODO: Create bs-navbar-toggle? --}}
{{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/bs-button}}
{{!-- TODO
{{#bs-navbar-brand}}Brand{{/bs-navbar-brand}}
--}}
<a class="navbar-brand" href="#">Brand</a>
{{/bs-navbar-header}}
{{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}}
{{#bs-collapse collapse=collapsed
class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}}
{{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}}
{{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}}
{{/bs-nav}}
{{/bs-collapse}}
{{/bs-navbar}}
Plumbing
Plumbing
MysteriousMysterious
Mysterious
Plumbing
Plumbing
9
First Cut 😳
{{#bs-navbar}}
{{#bs-navbar-header}}
{{!-- TODO: Create bs-navbar-toggle? --}}
{{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/bs-button}}
{{!-- TODO
{{#bs-navbar-brand}}Brand{{/bs-navbar-brand}}
--}}
<a class="navbar-brand" href="#">Brand</a>
{{/bs-navbar-header}}
{{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}}
{{#bs-collapse collapse=collapsed
class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}}
{{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}}
{{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}}
{{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}}
{{/bs-nav}}
{{/bs-collapse}}
{{/bs-navbar}}
Plumbing
Plumbing
MysteriousMysterious
Mysterious
Plumbing
Plumbing
Typo
9
Issues
• Didn’t really work
• Explicit class manipulation circumvented
transitions
• Properties weren’t where I thought they were
10
First Really Working Version
{{#bs-navbar as |toggleNavbar navbarCollapsed|}}
{{#bs-navbar-header}}
{{#bs-navbar-toggle action=toggleNavbar}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/bs-navbar-toggle}}
<a class="navbar-brand" href="#">Brand</a>
{{/bs-navbar-header}}
{{#bs-collapse collapsed=navbarCollapsed
class=“navbar-collapse"}}
...
bs-navbar.hbs
{{yield (action 'toggleNavbar') navbarCollapse}}
11
First Really Working Version
{{#bs-navbar as |toggleNavbar navbarCollapsed|}}
{{#bs-navbar-header}}
{{#bs-navbar-toggle action=toggleNavbar}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/bs-navbar-toggle}}
<a class="navbar-brand" href="#">Brand</a>
{{/bs-navbar-header}}
{{#bs-collapse collapsed=navbarCollapsed
class=“navbar-collapse"}}
...
Plumbing
Plumbing
Plumbing
bs-navbar.hbs
{{yield (action 'toggleNavbar') navbarCollapse}}
11
Component Visibility
• A component’s properties are only visible in its
template
• Component block params are visible in the
template that is yielded to
• The same is true for action function references
12
Action Calling
13
Action Calling
{{bs-navbar-toggle action="toggleNavbar"}}
By default it calls the action on the component then passes it to the
controller if unhandled
13
Action Calling
{{bs-navbar-toggle action="toggleNavbar"}}
By default it calls the action on the component then passes it to the
controller if unhandled
{{bs-navbar-toggle action=toggleNavbar}}
Without quotes, it can call an action function passed to it from a higher
level, but …
13
Action Calling
{{bs-navbar-toggle action="toggleNavbar"}}
By default it calls the action on the component then passes it to the
controller if unhandled
{{bs-navbar-toggle action=toggleNavbar}}
Without quotes, it can call an action function passed to it from a higher
level, but …
{{#bs-navbar as | toggleNavbar | }}
It must be exposed as a component block param or …
13
Action Calling
{{bs-navbar-toggle action="toggleNavbar"}}
By default it calls the action on the component then passes it to the
controller if unhandled
{{bs-navbar-toggle action=toggleNavbar}}
Without quotes, it can call an action function passed to it from a higher
level, but …
{{#bs-navbar as | toggleNavbar | }}
It must be exposed as a component block param or …
{{yield toggleNavbar=(action "toggleNavbar")}}
yielded through the template
13
Action Calling
{{bs-navbar-toggle action="toggleNavbar"}}
By default it calls the action on the component then passes it to the
controller if unhandled
{{bs-navbar-toggle action=toggleNavbar}}
Without quotes, it can call an action function passed to it from a higher
level, but …
{{#bs-navbar as | toggleNavbar | }}
It must be exposed as a component block param or …
{{yield toggleNavbar=(action "toggleNavbar")}}
yielded through the template
Prefer Closure Actions!
13
Improving Ergonomics
{{#bs-navbar as |navbar|}}
{{#bs-navbar-header}}
{{#bs-navbar-toggle action=navbar.toggle}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/bs-navbar-toggle}}
<a class="navbar-brand" href="#">Brand</a>
{{/bs-navbar-header}}
{{#bs-collapse collapsed=navbar.collapsed
class="navbar-collapse"}}
...
bs-navbar.hbs{{yield (hash
collapsed=navbarCollapsed
toggle=(action 'toggleNavbar'))}}
14
Can We Do Better?
• Why do these things need to be exposed at all?
• And while we’re at it, is there a better way to show
that the various components are really more closely
related?
15
Enter Contextual Components
{{#bs-navbar as |navbar|}}
{{#navbar.header}}
{{#navbar.toggle}}
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
{{/navbar.toggle}}
{{#navbar.brand}}Brand{{/navbar.brand}}
{{/navbar.header}}
{{#navbar.content}}
{{#navbar.nav}}
...
16
The Template
bs-navbar.hbs{{yield (hash collapsed=collapsed
header=(component 'bs-navbar-header')
toggle=(component ‘bs-navbar-toggle'
action=(action 'toggleNavbar'))
brand=(component 'bs-navbar-brand')
content=(component ‘bs-navbar-content'
collapsed=collapsed)
nav=(component 'bs-navbar-nav')
)
}}
17
Testing Contextual Components
• Integration testing
• Do you test all of the components through the parent?
• Do you just test the presence of the contextual
components? Easily done by referencing them.
• How far should you go to verify the currying is done
properly?
• To what extent should you test the aggregate
behavior?
• Do you need an acceptance test?
18
Victory!
😀
19
Victory!
But at a cost
This is an addon requiring compatibility to 1.13
Contextual components and hash were added in 2.3
😀😞
19
Final Form, But How?
{{#bs-navbar}}

<div class="navbar-header">

{{#bs-navbar-toggle}}

<span class="sr-only">Toggle navigation</span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

{{/bs-navbar-toggle}}

<a class="navbar-brand" href="#">Brand</a>

</div>

{{#bs-navbar-content}}

{{#bs-navbar-nav}}
...
20
Black Magic, Toggle Style
bs-navbar-toggle.jsimport Ember from 'ember';
import BsButtonComponent from 'ember-bootstrap/components/bs-button';
import NavbarComponent from 'ember-bootstrap/components/bs-navbar';
export default BsButtonComponent.extend({
...
targetObject: Ember.computed(function() {
return this.nearestOfType(NavbarComponent);
}),
action: 'toggleNavbar',
actions: {
toggleNavbar() {
this.sendAction();
}
}
});
21
Black Magic, Toggle Style
bs-navbar-toggle.jsimport Ember from 'ember';
import BsButtonComponent from 'ember-bootstrap/components/bs-button';
import NavbarComponent from 'ember-bootstrap/components/bs-navbar';
export default BsButtonComponent.extend({
...
targetObject: Ember.computed(function() {
return this.nearestOfType(NavbarComponent);
}),
action: 'toggleNavbar',
actions: {
toggleNavbar() {
this.sendAction();
}
}
});
21
Private!
Black Magic, Content Style
bs-navbar-content.jsimport Ember from 'ember';
import BsCollapseComponent from 'ember-bootstrap/components/bs-collapse';
import NavbarComponent from 'ember-bootstrap/components/bs-navbar';
export default BsCollapseComponent.extend({
navbar: Ember.computed(function() {
return this.nearestOfType(NavbarComponent);
}),
collapsed: Ember.computed.reads('navbar.collapsed')
});
22
Black Magic, Content Style
bs-navbar-content.jsimport Ember from 'ember';
import BsCollapseComponent from 'ember-bootstrap/components/bs-collapse';
import NavbarComponent from 'ember-bootstrap/components/bs-navbar';
export default BsCollapseComponent.extend({
navbar: Ember.computed(function() {
return this.nearestOfType(NavbarComponent);
}),
collapsed: Ember.computed.reads('navbar.collapsed')
});
22
Private!
Wrapping Up
• Think about how it will be used and how
newcomers will perceive it
• Shoot for elegance, aesthetics, and ergonomics
• Use the latest cool features in the service of design
and ergonomics, not for their own sake
• Remember not everyone’s on the cutting edge
23
Resources
• Ember Guides for Components
• https://guides.emberjs.com/v2.7.0/components/passing-properties-to-a-component/
• https://guides.emberjs.com/v2.7.0/components/wrapping-content-in-a-component/
• https://guides.emberjs.com/v2.7.0/components/block-params/
• Eric Kelly’s (@HeroicEric) Boston Ember.js Talk
• https://speakerdeck.com/heroiceric/contextual-components
• https://youtu.be/Au3rHHuEZNI?t=1h6m42s
• Some Twiddles
• Component Property Scope: https://ember-twiddle.com/
332c58bba5a2d0ac8874dd834e28ac06
• Action Calling: https://ember-twiddle.com/c9ba29e3c2f98937d4d0c8e493261a78
24
Contact Me
Stephen Vance
http://www.vance.com
steve@vance.com
@StephenRVance
srvance on GitHub and LinkedIn
25

More Related Content

PPTX
Getting to Know Bootstrap for Rapid Web Development
PDF
Front-End Dev Tools
PDF
CSS in React
PDF
Responsive Websites
PDF
LESS
PDF
Joomla!Day 2013 Nürnberg; Vortrag von Johannes Hock
PDF
Whirlwind Tour of SVG (plus RaphaelJS)
DOCX
Doctype netscape
Getting to Know Bootstrap for Rapid Web Development
Front-End Dev Tools
CSS in React
Responsive Websites
LESS
Joomla!Day 2013 Nürnberg; Vortrag von Johannes Hock
Whirlwind Tour of SVG (plus RaphaelJS)
Doctype netscape

Viewers also liked (19)

PPTX
AgileChina 2015: Agile Estimation Workshop
PDF
Portafolio interiores
PPTX
Lily Wickens AS Media: Your Finances
PPT
DOCX
Direct sales executive perfomance appraisal 2
PPTX
healthyeating
PPT
Energies albert
DOCX
Reservations manager perfomance appraisal 2
DOCX
Genetically Modified Organisms
PDF
Agentes virais 01 cinomose
PDF
Infographic: All about personal belongings theft in the UK 2015
PPTX
Medicina legal mafer
PPT
Koulutuksen ohjausjärjestelmä ja tietosuoja
PPTX
A cautionary tale of agile project management
PDF
Brochure - SG World USA
PDF
Portafolio hoteles
PPT
Витебщина освобожденная
AgileChina 2015: Agile Estimation Workshop
Portafolio interiores
Lily Wickens AS Media: Your Finances
Direct sales executive perfomance appraisal 2
healthyeating
Energies albert
Reservations manager perfomance appraisal 2
Genetically Modified Organisms
Agentes virais 01 cinomose
Infographic: All about personal belongings theft in the UK 2015
Medicina legal mafer
Koulutuksen ohjausjärjestelmä ja tietosuoja
A cautionary tale of agile project management
Brochure - SG World USA
Portafolio hoteles
Витебщина освобожденная
Ad

Similar to 20160908 Aesthetic-Driven Development (20)

PPTX
Bootstrap [part 2]
PDF
Bootstrap 3 Cheat Sheet PDF Reference
PPTX
Intro to Bootstrap
PDF
The next step is... Bootstrap by Omar Qunsul
PPTX
Lecture-10.pptx
PDF
Pemrograman Web 4 - Bootstrap 3
PDF
Stop reinventing the wheel: Build Responsive Websites Using Bootstrap
PPTX
Bootstrap - Web Development Framework
PDF
Rapid HTML Prototyping with Bootstrap - Chris Griffith
PDF
Web Frontend development: tools and good practices to (re)organize the chaos
PDF
Building Dynamic Navigation in your Rails 4 Layout
PDF
Html5 & CSS overview
PDF
CSMess to OOCSS: Refactoring CSS with Object Oriented Design
PPTX
Bootstrap PPT by Mukesh
PDF
Atomic design con pattern lab
PPTX
Boot strap
PDF
Think Vitamin CSS
PDF
Bootstrap
PDF
Layout with CSS
PDF
Presentation html5 css3 by thibaut
Bootstrap [part 2]
Bootstrap 3 Cheat Sheet PDF Reference
Intro to Bootstrap
The next step is... Bootstrap by Omar Qunsul
Lecture-10.pptx
Pemrograman Web 4 - Bootstrap 3
Stop reinventing the wheel: Build Responsive Websites Using Bootstrap
Bootstrap - Web Development Framework
Rapid HTML Prototyping with Bootstrap - Chris Griffith
Web Frontend development: tools and good practices to (re)organize the chaos
Building Dynamic Navigation in your Rails 4 Layout
Html5 & CSS overview
CSMess to OOCSS: Refactoring CSS with Object Oriented Design
Bootstrap PPT by Mukesh
Atomic design con pattern lab
Boot strap
Think Vitamin CSS
Bootstrap
Layout with CSS
Presentation html5 css3 by thibaut
Ad

More from Stephen Vance (7)

PDF
Ai and mobile - Power in Your Pocket
PDF
Ai and mobile
PDF
Zero Bugs: State of the Practice
PDF
AST Rewriting Using recast and esprima
PDF
Ember and OAuth2
PDF
Cultivating People as the Org Grows
PDF
AgileChina 2015 Keynote: Understand deeply
Ai and mobile - Power in Your Pocket
Ai and mobile
Zero Bugs: State of the Practice
AST Rewriting Using recast and esprima
Ember and OAuth2
Cultivating People as the Org Grows
AgileChina 2015 Keynote: Understand deeply

Recently uploaded (20)

PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PPTX
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PPTX
assetexplorer- product-overview - presentation
PDF
Understanding Forklifts - TECH EHS Solution
PPTX
Transform Your Business with a Software ERP System
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PPTX
L1 - Introduction to python Backend.pptx
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Digital Systems & Binary Numbers (comprehensive )
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
Softaken Excel to vCard Converter Software.pdf
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
How to Migrate SBCGlobal Email to Yahoo Easily
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
Reimagine Home Health with the Power of Agentic AI​
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
assetexplorer- product-overview - presentation
Understanding Forklifts - TECH EHS Solution
Transform Your Business with a Software ERP System
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
How to Choose the Right IT Partner for Your Business in Malaysia
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
L1 - Introduction to python Backend.pptx
VVF-Customer-Presentation2025-Ver1.9.pptx
Wondershare Filmora 15 Crack With Activation Key [2025
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Digital Systems & Binary Numbers (comprehensive )
Navsoft: AI-Powered Business Solutions & Custom Software Development
Softaken Excel to vCard Converter Software.pdf

20160908 Aesthetic-Driven Development

  • 1. Aesthetics-Driven Development: Cool Features for Ergonomic Software Design Boston Ember.js September 8, 2016 Stephen Vance 1
  • 2. Motivation • Using Bootstrap • Relying on responsive behavior of navbars • Some of the behavior relies on JavaScript • Wanted more Ember-y approach • ember-bootstrap didn’t support it yet 2
  • 4. Bootstrap Navbar <nav class="navbar navbar-default" role="navigation"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class=“navbar-toggle" data-toggle="collapse" data-target=".navbar-mwpc-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> {{#link-to "index" class="navbar-brand"}}{{siteBrand.brand}}{{/link-to}} </div> <div class="collapse navbar-collapse navbar-mwpc-collapse"> <ul class="nav navbar-nav"> <li class="dropdown"> <a href="#" class=“dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> Service Directory<span class="caret"></span> </a> <ul class="dropdown-menu" role="menu"> {{#each-in serviceCategories.categories as | category info |}} <li> {{link-to info.displayName "service" category}} </li> {{/each-in}} </ul> </li> <li> {{#link-to "resources"}}Local Resources{{/link-to}} </li> 4
  • 5. Bootstrap Navbar <nav class="navbar navbar-default" role="navigation"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class=“navbar-toggle" data-toggle="collapse" data-target=".navbar-mwpc-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> {{#link-to "index" class="navbar-brand"}}{{siteBrand.brand}}{{/link-to}} </div> <div class="collapse navbar-collapse navbar-mwpc-collapse"> <ul class="nav navbar-nav"> <li class="dropdown"> <a href="#" class=“dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> Service Directory<span class="caret"></span> </a> <ul class="dropdown-menu" role="menu"> {{#each-in serviceCategories.categories as | category info |}} <li> {{link-to info.displayName "service" category}} </li> {{/each-in}} </ul> </li> <li> {{#link-to "resources"}}Local Resources{{/link-to}} </li> Navbar Header Toggle Content Nav Brand 4
  • 10. Applying DDAU* Navbar Header Toggle Content 1. Toggles State 2. Passes State *Data Down, Actions Up 6
  • 11. Design Concerns • Peer components shouldn’t reference each other • Navbar state should be within the component • Outside would require users to define it • Nearest common parent • Support multiple navbars in a page • Strive for clean DSL • Minimize exposed plumbing • Principle of Least Astonishment 7
  • 12. Implementation Concerns • Component block form • Nature of problem doesn’t require inline • Component isolation makes it harder for related components to cooperate transparently 8
  • 13. First Cut 😳 {{#bs-navbar}} {{#bs-navbar-header}} {{!-- TODO: Create bs-navbar-toggle? --}} {{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/bs-button}} {{!-- TODO {{#bs-navbar-brand}}Brand{{/bs-navbar-brand}} --}} <a class="navbar-brand" href="#">Brand</a> {{/bs-navbar-header}} {{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}} {{#bs-collapse collapse=collapsed class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}} {{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}} {{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}} {{/bs-nav}} {{/bs-collapse}} {{/bs-navbar}} 9
  • 14. First Cut 😳 {{#bs-navbar}} {{#bs-navbar-header}} {{!-- TODO: Create bs-navbar-toggle? --}} {{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/bs-button}} {{!-- TODO {{#bs-navbar-brand}}Brand{{/bs-navbar-brand}} --}} <a class="navbar-brand" href="#">Brand</a> {{/bs-navbar-header}} {{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}} {{#bs-collapse collapse=collapsed class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}} {{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}} {{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}} {{/bs-nav}} {{/bs-collapse}} {{/bs-navbar}} Plumbing Plumbing Plumbing Plumbing 9
  • 15. First Cut 😳 {{#bs-navbar}} {{#bs-navbar-header}} {{!-- TODO: Create bs-navbar-toggle? --}} {{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/bs-button}} {{!-- TODO {{#bs-navbar-brand}}Brand{{/bs-navbar-brand}} --}} <a class="navbar-brand" href="#">Brand</a> {{/bs-navbar-header}} {{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}} {{#bs-collapse collapse=collapsed class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}} {{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}} {{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}} {{/bs-nav}} {{/bs-collapse}} {{/bs-navbar}} Plumbing Plumbing MysteriousMysterious Mysterious Plumbing Plumbing 9
  • 16. First Cut 😳 {{#bs-navbar}} {{#bs-navbar-header}} {{!-- TODO: Create bs-navbar-toggle? --}} {{#bs-button toggle=true active=expanded action=toggle class="navbar-toggle collapsed"}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/bs-button}} {{!-- TODO {{#bs-navbar-brand}}Brand{{/bs-navbar-brand}} --}} <a class="navbar-brand" href="#">Brand</a> {{/bs-navbar-header}} {{!-- TODO: Create bs-navbar-content instead of using bs-collapse --}} {{#bs-collapse collapse=collapsed class=(if expanded "collapse navbar-collapse in" "collapse navbar-collapse")}} {{#bs-nav type=type.id justified=justified stacked=stacked navbar=true}} {{#bs-nav-item}}{{#link-to "alert"}}Alert{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "button"}}Buttons{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "dropdown"}}Dropdown{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "forms"}}Forms{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "accordion"}}Accordion{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "collapse"}}Collapse{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "modal"}}Modals{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "progress"}}Progress bars{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navs"}}Navs{{/link-to}}{{/bs-nav-item}} {{#bs-nav-item}}{{#link-to "navbars"}}Navbars{{/link-to}}{{/bs-nav-item}} {{/bs-nav}} {{/bs-collapse}} {{/bs-navbar}} Plumbing Plumbing MysteriousMysterious Mysterious Plumbing Plumbing Typo 9
  • 17. Issues • Didn’t really work • Explicit class manipulation circumvented transitions • Properties weren’t where I thought they were 10
  • 18. First Really Working Version {{#bs-navbar as |toggleNavbar navbarCollapsed|}} {{#bs-navbar-header}} {{#bs-navbar-toggle action=toggleNavbar}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/bs-navbar-toggle}} <a class="navbar-brand" href="#">Brand</a> {{/bs-navbar-header}} {{#bs-collapse collapsed=navbarCollapsed class=“navbar-collapse"}} ... bs-navbar.hbs {{yield (action 'toggleNavbar') navbarCollapse}} 11
  • 19. First Really Working Version {{#bs-navbar as |toggleNavbar navbarCollapsed|}} {{#bs-navbar-header}} {{#bs-navbar-toggle action=toggleNavbar}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/bs-navbar-toggle}} <a class="navbar-brand" href="#">Brand</a> {{/bs-navbar-header}} {{#bs-collapse collapsed=navbarCollapsed class=“navbar-collapse"}} ... Plumbing Plumbing Plumbing bs-navbar.hbs {{yield (action 'toggleNavbar') navbarCollapse}} 11
  • 20. Component Visibility • A component’s properties are only visible in its template • Component block params are visible in the template that is yielded to • The same is true for action function references 12
  • 22. Action Calling {{bs-navbar-toggle action="toggleNavbar"}} By default it calls the action on the component then passes it to the controller if unhandled 13
  • 23. Action Calling {{bs-navbar-toggle action="toggleNavbar"}} By default it calls the action on the component then passes it to the controller if unhandled {{bs-navbar-toggle action=toggleNavbar}} Without quotes, it can call an action function passed to it from a higher level, but … 13
  • 24. Action Calling {{bs-navbar-toggle action="toggleNavbar"}} By default it calls the action on the component then passes it to the controller if unhandled {{bs-navbar-toggle action=toggleNavbar}} Without quotes, it can call an action function passed to it from a higher level, but … {{#bs-navbar as | toggleNavbar | }} It must be exposed as a component block param or … 13
  • 25. Action Calling {{bs-navbar-toggle action="toggleNavbar"}} By default it calls the action on the component then passes it to the controller if unhandled {{bs-navbar-toggle action=toggleNavbar}} Without quotes, it can call an action function passed to it from a higher level, but … {{#bs-navbar as | toggleNavbar | }} It must be exposed as a component block param or … {{yield toggleNavbar=(action "toggleNavbar")}} yielded through the template 13
  • 26. Action Calling {{bs-navbar-toggle action="toggleNavbar"}} By default it calls the action on the component then passes it to the controller if unhandled {{bs-navbar-toggle action=toggleNavbar}} Without quotes, it can call an action function passed to it from a higher level, but … {{#bs-navbar as | toggleNavbar | }} It must be exposed as a component block param or … {{yield toggleNavbar=(action "toggleNavbar")}} yielded through the template Prefer Closure Actions! 13
  • 27. Improving Ergonomics {{#bs-navbar as |navbar|}} {{#bs-navbar-header}} {{#bs-navbar-toggle action=navbar.toggle}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/bs-navbar-toggle}} <a class="navbar-brand" href="#">Brand</a> {{/bs-navbar-header}} {{#bs-collapse collapsed=navbar.collapsed class="navbar-collapse"}} ... bs-navbar.hbs{{yield (hash collapsed=navbarCollapsed toggle=(action 'toggleNavbar'))}} 14
  • 28. Can We Do Better? • Why do these things need to be exposed at all? • And while we’re at it, is there a better way to show that the various components are really more closely related? 15
  • 29. Enter Contextual Components {{#bs-navbar as |navbar|}} {{#navbar.header}} {{#navbar.toggle}} <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> {{/navbar.toggle}} {{#navbar.brand}}Brand{{/navbar.brand}} {{/navbar.header}} {{#navbar.content}} {{#navbar.nav}} ... 16
  • 30. The Template bs-navbar.hbs{{yield (hash collapsed=collapsed header=(component 'bs-navbar-header') toggle=(component ‘bs-navbar-toggle' action=(action 'toggleNavbar')) brand=(component 'bs-navbar-brand') content=(component ‘bs-navbar-content' collapsed=collapsed) nav=(component 'bs-navbar-nav') ) }} 17
  • 31. Testing Contextual Components • Integration testing • Do you test all of the components through the parent? • Do you just test the presence of the contextual components? Easily done by referencing them. • How far should you go to verify the currying is done properly? • To what extent should you test the aggregate behavior? • Do you need an acceptance test? 18
  • 33. Victory! But at a cost This is an addon requiring compatibility to 1.13 Contextual components and hash were added in 2.3 😀😞 19
  • 34. Final Form, But How? {{#bs-navbar}}
 <div class="navbar-header">
 {{#bs-navbar-toggle}}
 <span class="sr-only">Toggle navigation</span>
 <span class="icon-bar"></span>
 <span class="icon-bar"></span>
 <span class="icon-bar"></span>
 {{/bs-navbar-toggle}}
 <a class="navbar-brand" href="#">Brand</a>
 </div>
 {{#bs-navbar-content}}
 {{#bs-navbar-nav}} ... 20
  • 35. Black Magic, Toggle Style bs-navbar-toggle.jsimport Ember from 'ember'; import BsButtonComponent from 'ember-bootstrap/components/bs-button'; import NavbarComponent from 'ember-bootstrap/components/bs-navbar'; export default BsButtonComponent.extend({ ... targetObject: Ember.computed(function() { return this.nearestOfType(NavbarComponent); }), action: 'toggleNavbar', actions: { toggleNavbar() { this.sendAction(); } } }); 21
  • 36. Black Magic, Toggle Style bs-navbar-toggle.jsimport Ember from 'ember'; import BsButtonComponent from 'ember-bootstrap/components/bs-button'; import NavbarComponent from 'ember-bootstrap/components/bs-navbar'; export default BsButtonComponent.extend({ ... targetObject: Ember.computed(function() { return this.nearestOfType(NavbarComponent); }), action: 'toggleNavbar', actions: { toggleNavbar() { this.sendAction(); } } }); 21 Private!
  • 37. Black Magic, Content Style bs-navbar-content.jsimport Ember from 'ember'; import BsCollapseComponent from 'ember-bootstrap/components/bs-collapse'; import NavbarComponent from 'ember-bootstrap/components/bs-navbar'; export default BsCollapseComponent.extend({ navbar: Ember.computed(function() { return this.nearestOfType(NavbarComponent); }), collapsed: Ember.computed.reads('navbar.collapsed') }); 22
  • 38. Black Magic, Content Style bs-navbar-content.jsimport Ember from 'ember'; import BsCollapseComponent from 'ember-bootstrap/components/bs-collapse'; import NavbarComponent from 'ember-bootstrap/components/bs-navbar'; export default BsCollapseComponent.extend({ navbar: Ember.computed(function() { return this.nearestOfType(NavbarComponent); }), collapsed: Ember.computed.reads('navbar.collapsed') }); 22 Private!
  • 39. Wrapping Up • Think about how it will be used and how newcomers will perceive it • Shoot for elegance, aesthetics, and ergonomics • Use the latest cool features in the service of design and ergonomics, not for their own sake • Remember not everyone’s on the cutting edge 23
  • 40. Resources • Ember Guides for Components • https://guides.emberjs.com/v2.7.0/components/passing-properties-to-a-component/ • https://guides.emberjs.com/v2.7.0/components/wrapping-content-in-a-component/ • https://guides.emberjs.com/v2.7.0/components/block-params/ • Eric Kelly’s (@HeroicEric) Boston Ember.js Talk • https://speakerdeck.com/heroiceric/contextual-components • https://youtu.be/Au3rHHuEZNI?t=1h6m42s • Some Twiddles • Component Property Scope: https://ember-twiddle.com/ 332c58bba5a2d0ac8874dd834e28ac06 • Action Calling: https://ember-twiddle.com/c9ba29e3c2f98937d4d0c8e493261a78 24