feat(onboarding): improve onboarding flow

Summary:
Now with more CSS

Also fixed flow for when you're just adding an account

Fixes T3805

Test Plan: manual :(

Reviewers: drew, bengotow

Reviewed By: bengotow

Maniphest Tasks: T3805

Differential Revision: https://phab.nylas.com/D2071
This commit is contained in:
Evan Morikawa 2015-09-25 20:43:36 -04:00
parent aa019360b9
commit e74981502e
21 changed files with 452 additions and 166 deletions

View file

@ -115,9 +115,8 @@ class AccountSwitcher extends React.Component
@setState(showing: false)
_onAddAccount: =>
ipc = require('ipc')
ipc.send('command', 'application:add-account')
@setState(showing: false)
require('remote').getGlobal('application').windowManager.newOnboardingWindow(addingAccount: true)
@setState showing: false
_getStateFromStores: =>
accounts: AccountStore.items()

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -25,16 +25,15 @@ class AccountChoosePage extends React.Component
@_usub?()
render: =>
closeType = if @props.pageData.addingAccount then "close" else "quit"
<div className="page account-choose">
<div className="quit" onClick={ => atom.close() }>
<div className="quit" onClick={ => atom[closeType]() }>
<RetinaImg name="onboarding-close.png" mode={RetinaImg.Mode.ContentPreserve}/>
</div>
<div className="logo-container">
<RetinaImg name="onboarding-logo.png" mode={RetinaImg.Mode.ContentPreserve} className="logo"/>
</div>
<RetinaImg url="nylas://onboarding/assets/nylas-pictograph@2x.png" mode={RetinaImg.Mode.ContentIsMask} style={zoom: 0.29} className="logo"/>
<div className="caption" style={marginBottom:20}>Select your email provider</div>
<div className="caption" style={marginTop: 15, marginBottom:20}>Select your email provider</div>
{@_renderProviders()}
@ -47,7 +46,7 @@ class AccountChoosePage extends React.Component
<div className="icon-container">
<RetinaImg name={provider.icon} mode={RetinaImg.Mode.ContentPreserve} className="icon"/>
</div>
{provider.displayName}
<span className="provider-name">{provider.displayName}</span>
</div>
_renderError: ->

View file

@ -158,15 +158,15 @@ class AccountSettingsPage extends React.Component
_renderButton: =>
pages = @state.provider.pages || []
if pages.length > @state.pageNumber + 1
<button className="btn btn-large btn-gradient" type="button" onClick={@_onNextButton}>Next</button>
if pages.length > @state.pageNumber+1
<button className="btn btn-large btn-gradient" type="button" onClick={@_onNextButton}>Continue</button>
else if @state.provider.name isnt 'gmail'
if @state.tryingToAuthenticate
<button className="btn btn-large btn-gradient btn-setup-spinning" type="button">
<RetinaImg name="sending-spinner.gif" width={15} height={15} mode={RetinaImg.Mode.ContentPreserve} /> Setting up&hellip;
<RetinaImg name="sending-spinner.gif" width={15} height={15} mode={RetinaImg.Mode.ContentPreserve} /> Adding account&hellip;
</button>
else
<button className="btn btn-large btn-gradient" type="button" onClick={@_onSubmit}>Set up account</button>
<button className="btn btn-large btn-gradient" type="button" onClick={@_onSubmit}>Add account</button>
_onNextButton: (event) =>
@setState(pageNumber: @state.pageNumber + 1)

View file

@ -2,6 +2,13 @@
Providers = [
{
name: 'gmail'
displayName: 'Gmail'
icon: 'ic-settings-account-gmail.png'
header_icon: 'setup-icon-provider-gmail.png'
color: '#e99999'
settings: []
}, {
name: 'exchange'
displayName: 'Microsoft Exchange / Live'
icon: 'ic-settings-account-eas.png'
@ -34,38 +41,6 @@ Providers = [
}
]
}, {
# name: 'outlook'
# displayName: 'Outlook / Hotmail'
# icon: 'ic-settings-account-eas.png'
# header_icon: 'setup-icon-provider-hotmail.png'
# color: '#308acd'
# fields: [
# {
# name: 'name'
# type: 'text'
# placeholder: 'Ashton Letterman'
# label: 'Name'
# }, {
# name: 'email'
# type: 'text'
# placeholder: 'you@hotmail.com'
# label: 'Email'
# }
# ]
# settings: [
# {
# name: 'username'
# type: 'text'
# placeholder: 'MYCORP\\bob (if known)'
# label: 'Username (optional)'
# },{
# name: 'password'
# type: 'password'
# placeholder: 'Password'
# label: 'Password'
# }
# ]
# }, {
name: 'icloud'
displayName: 'iCloud'
icon: 'ic-settings-account-icloud.png'
@ -90,13 +65,6 @@ Providers = [
placeholder: 'Password'
label: 'Password'
}]
}, {
name: 'gmail'
displayName: 'Gmail'
icon: 'ic-settings-account-gmail.png'
header_icon: 'setup-icon-provider-gmail.png'
color: '#e99999'
settings: []
}, {
name: 'yahoo'
displayName: 'Yahoo'
@ -246,4 +214,4 @@ Providers = [
}
]
module.exports = Providers
module.exports = Providers

View file

@ -61,7 +61,11 @@ class InitialPackagesPage extends React.Component
@displayName: "InitialPackagesPage"
render: =>
closeType = if @props.pageData.addingAccount then "close" else "quit"
<div className="page no-top opaque" style={width:900, height:650}>
<div className="quit" onClick={ => atom[closeType]() }>
<RetinaImg name="onboarding-close.png" mode={RetinaImg.Mode.ContentPreserve}/>
</div>
<div className="back" onClick={@_onPrevPage}>
<RetinaImg name="onboarding-back.png" mode={RetinaImg.Mode.ContentPreserve}/>
</div>

View file

@ -80,7 +80,11 @@ class InitialPreferencesPage extends React.Component
@displayName: "InitialPreferencesPage"
render: =>
closeType = if @props.pageData.addingAccount then "close" else "quit"
<div className="page no-top opaque" style={width:900, height:620}>
<div className="quit" onClick={ => atom[closeType]() }>
<RetinaImg name="onboarding-close.png" mode={RetinaImg.Mode.ContentPreserve}/>
</div>
<h1 style={paddingTop: 100}>Welcome to N1</h1>
<h4 style={marginBottom: 70}>Let's set things up to your liking.</h4>
<ConfigPropContainer>

View file

@ -64,11 +64,11 @@ class PageRouter extends React.Component
gradient = @state.pageData?.provider?.color
if gradient
background = "linear-gradient(to top, #f6f7f8, #{gradient})"
height = 200
else
background = "linear-gradient(to top, #f6f7f8 0%, rgba(255,255,255,0) 100%),
linear-gradient(to right, #e1e58f 0%, #a8d29e 50%, #8bc9c9 100%)"
<div className="page-gradient" key={"#{@state.page}-gradient"} style={background: background}/>
background = "linear-gradient(to top, #f6f7f8 0%, rgba(255,255,255,0) 100%), linear-gradient(to right, #e1e58f 0%, #a8d29e 50%, #8bc9c9 100%)"
height = 330
<div className="page-gradient" key={"#{@state.page}-gradient"} style={background: background, height: height}/>
_renderCurrentPage: =>
Component = {
@ -86,9 +86,9 @@ class PageRouter extends React.Component
_renderDragRegion: ->
styles =
top:0
left:40
left: 26
right:0
height: 20
height: 27
zIndex:100
position: 'absolute'
"WebkitAppRegion": "drag"

View file

@ -1,4 +1,6 @@
React = require 'react'
shell = require 'shell'
classnames = require 'classnames'
{RetinaImg, TimeoutTransitionGroup} = require 'nylas-component-kit'
OnboardingActions = require './onboarding-actions'
@ -8,44 +10,107 @@ class WelcomePage extends React.Component
constructor: (@props) ->
@state =
step: 0
lastStep: 0
render: =>
buttons = []
if @state.step > 0
buttons.push <button key="back" className="btn btn-large" style={marginRight: 10} onClick={@_onBack}>Back</button>
buttons.push <button key="next" className="btn btn-large" onClick={@_onContinue}>Continue</button>
<div className="page no-top opaque" style={width: 667, display: "inline-block"}>
<div className="quit" onClick={ => atom.close() }>
render: ->
closeType = if @props.pageData.addingAccount then "close" else "quit"
<div className="welcome-page page no-top opaque">
<div className="quit" onClick={ => atom[closeType]() }>
<RetinaImg name="onboarding-close.png" mode={RetinaImg.Mode.ContentPreserve}/>
</div>
<TimeoutTransitionGroup leaveTimeout={300}
enterTimeout={300}
className="welcome-image-container"
transitionName="welcome-image">
{@_renderStep()}
</TimeoutTransitionGroup>
<div style={textAlign:'center', paddingTop:30, paddingBottom:30}>
{buttons}
</div>
<div className="steps-container">{@_renderSteps()}</div>
<div className="footer">{@_renderButtons()}</div>
</div>
_renderStep: =>
if @state.step is 0
<div className="welcome-image" key="step-0">
<RetinaImg name="welcome1bg.png" mode={RetinaImg.Mode.ContentPreserve} />
<RetinaImg name="welcome1icon.png" mode={RetinaImg.Mode.ContentPreserve} style={position:'absolute', top:'50%', left:'50%', transform:'translate(-50%, -50%)'}/>
</div>
else if @state.step is 1
<div className="welcome-image" key="step-1">
<RetinaImg name="welcome2bg.png" mode={RetinaImg.Mode.ContentPreserve} />
</div>
else if @state.step is 2
<div className="welcome-image" key="step-2">
<RetinaImg name="welcome3bg.png" mode={RetinaImg.Mode.ContentPreserve} />
_renderButtons: ->
buttons = []
# if @state.step > 0
# buttons.push <span key="back" className="btn-back" onClick={@_onBack}>Back</span>
btnText = if @state.step is 2 then "Get Started" else "Continue"
buttons.push <button key="next" className="btn btn-large btn-continue" onClick={@_onContinue}>{btnText}</button>
return buttons
_renderSteps: -> [
@_renderStep0()
@_renderStep1()
@_renderStep2()
]
_stepClass: (n) ->
obj =
"step-wrap": true
"active": @state.step is n
obj["step-#{n}-wrap"] = true
className = classnames(obj)
return className
_renderStep0: ->
<div className={@_stepClass(0)} key="step-0">
<p className="hero-text" style={marginTop: 25}>N1 is a new email app that is fast,<br/>friendly, and easy to use.</p>
<RetinaImg className="logo" style={zoom: 0.35, marginTop: 13} url="nylas://onboarding/assets/nylas-pictograph@2x.png" mode={RetinaImg.Mode.ContentIsMask} />
<RetinaImg className="icons" style={position: "absolute", left: -45, top: 130} url="nylas://onboarding/assets/shapes-left@2x.png" mode={RetinaImg.Mode.ContentIsMask} />
<RetinaImg className="icons" style={position: "absolute", right: -40, top: 130} url="nylas://onboarding/assets/shapes-right@2x.png" mode={RetinaImg.Mode.ContentIsMask} />
<p className="sub-text" style={marginTop: 17}>It is the foundation for a highly extensible email experience</p>
{@_renderNavBubble(0)}
</div>
_renderStep1: ->
<div className={@_stepClass(1)} key="step-1">
<p className="hero-text" style={marginTop: 40}>Under the hood, N1 is built for developers.</p>
<div className="gear-outer-container"><div className="gear-container">
{@_gears()}
</div></div>
<RetinaImg className="gear-small" mode={RetinaImg.Mode.ContentPreserve}
url="nylas://onboarding/assets/gear-small@2x.png" />
<RetinaImg className="wrench" mode={RetinaImg.Mode.ContentPreserve}
url="nylas://onboarding/assets/wrench@2x.png" />
<p className="sub-text">You can extend it using packages and build your own components.</p>
{@_renderNavBubble(1)}
</div>
_gears: ->
gears = []
for i in [0..3]
gears.push <RetinaImg className="gear-large gear-large-#{i}"
mode={RetinaImg.Mode.ContentPreserve}
url="nylas://onboarding/assets/gear-large@2x.png" />
return gears
_renderStep2: ->
<div className={@_stepClass(2)} key="step-2">
<p className="hero-text" style={marginTop: 40}>N1 is secured and enhanced by the Nylas Sync Engine</p>
<div className="cell-wrap">
<div className="cell" style={float: "left"}>
<RetinaImg mode={RetinaImg.Mode.ContentPreserve}
url="nylas://onboarding/assets/lock@2x.png" />
<p>Secured using<br/>bank-grade encryption</p>
<a onClick={=> @_open("https://nylas.com/security/")}>more info</a>
</div>
<div className="cell" style={float: "right"}>
<RetinaImg mode={RetinaImg.Mode.ContentPreserve}
style={paddingTop: 4, paddingBottom: 4}
url="nylas://onboarding/assets/cloud@2x.png" />
<p>Synced by Nylas to be<br/>faster and more extensible</p>
<a onClick={=> @_open("https://github.com/nylas/sync-engine")}>more info</a>
</div>
</div>
{@_renderNavBubble(2)}
</div>
_open: (link) ->
shell.openExternal(link)
return
_renderNavBubble: (step=0) ->
bubbles = [0..2].map (n) =>
active = if n is step then "active" else ""
<div className="nav-bubble #{active}"
onClick={ => @setState step: n }></div>
<div className="nav-bubbles">
{bubbles}
</div>
_onBack: =>
@setState(step: @state.step - 1)

View file

@ -109,10 +109,6 @@
padding: 0 15px;
width: 330px
}
&.account-choose {
width: 330px;
padding-bottom:10px;
}
&.opaque {
background-color: @gray-lighter;
}
@ -139,8 +135,9 @@
.quit {
position: absolute;
top:5px;
left:5px;
z-index: 100;
top: 1px;
left: 6px;
}
.back,
@ -167,30 +164,6 @@
opacity: 0.01;
}
.welcome-image-container {
height:391px;
display:block;
}
.welcome-image {
position:absolute;
}
.welcome-image-enter {
opacity: 0;
transition: opacity .3s linear;
}
.welcome-image-enter.welcome-image-enter-active {
opacity: 1;
}
.welcome-image-leave {
opacity: 1;
transition: opacity .3s linear;
}
.welcome-image-leave.welcome-image-leave-active {
opacity: 0;
}
.check {
width: @checkSize;
height: @checkSize;
@ -242,25 +215,6 @@
}
}
.provider {
text-align: left;
border-top: 1px solid rgba(0,0,0,0.05);
cursor: default;
img.icon {
}
.icon-container {
width: 50px;
height: 50px;
display: inline-block;
box-sizing: content-box;
padding: 5px 15px;
}
}
.provider:hover{
background: rgba(255,255,255,0.7);
}
.errormsg {
color: #A33;
margin-bottom:5px;
@ -338,6 +292,44 @@
}
}
.page.account-choose {
width: 388px;
height: 602px;
img.logo.content-mask {
background-color: rgba(255,255,255,0.4);
}
.caption {
font-size: 17px;
color: rgba(0,0,0,0.56);
}
.provider-name {
font-size: 20px;
font-weight: 300;
color: rgba(0,0,0,0.7);
}
.provider {
text-align: left;
border-top: 1px solid rgba(0,0,0,0.05);
cursor: default;
img.icon {
}
.icon-container {
width: 50px;
height: 50px;
display: inline-block;
box-sizing: content-box;
padding: 10px 25px;
}
}
.provider:hover{
background: rgba(255,255,255,0.7);
}
}
.initial-package {
display:block;
margin:auto;
@ -361,3 +353,266 @@
max-width:500px;
}
}
.welcome-page {
width: 675px;
height: 480px;
display: flex;
flex-direction: column;
@-webkit-keyframes slideIn {
from {
transform: translate3d(20,0,0);
opacity: 0;
}
to {
transform: translate3d(0,0,0);
opacity: 1;
}
}
a {
color: rgba(255,255,255,0.7);
border-bottom: 1px solid rgba(255,255,255,0.7);
&:hover {
cursor: default;
color: white;
border-bottom: 1px solid white;
}
}
.steps-container {
position: relative;
flex: 1;
}
.step-wrap {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: all 0.3s;
z-index: 1;
opacity: 0;
&.active {
opacity: 1;
z-index: 3;
}
}
.footer {
text-align: center;
background: #ececec;
border-top: 1px solid #d4d4d4;
.btn-continue {
font-size: 18px;
font-weight: 300;
margin: 20px 0;
padding: 12px 0;
width: 296px;
}
.btn-back {
color: rgba(0,0,0,0.4);
&:hover {
color: rgba(0,0,0,0.7);
cursor: default;
}
position: absolute;
left: 20px;
bottom: 30px;
}
}
p.hero-text {
font-weight: 200;
-webkit-font-smoothing: subpixel-antialiased;
}
p.sub-text {
font-size: 17px;
font-weight: 300;
}
.nylas-wash-light-bg {
background-image: linear-gradient(to bottom, rgba(236,236,236,0) 0%, rgba(236,236,236,1) 88%), linear-gradient(to right, rgba(213,224,0,0.47) 0%,rgba(88,182,63,0.47) 50%,rgba(12,162,163,0.47) 100%);
color: white;
}
.nylas-wash-bg {
background: rgba(101, 191, 191, 0.44);
background-image: linear-gradient(to right, rgba(213,224,0,0.35) 0%,rgba(88,182,63,0.35) 50%,rgba(12,162,163,0.35) 100%);
color: white;
}
.nylas-static-wash-bg {
background-image: linear-gradient(to right, rgba(162,210,128,1) 0%,rgba(113,193,154,1) 50%,rgba(89,187,187,1) 100%);
color: white;
}
.nylas-dark-bg {
background: rgba(58, 68, 74, 1);
color: rgba(255,255,255,0.7);
.sub-text {
color: rgba(255,255,255,0.5);
}
}
.nylas-blue-wash-bg {
background-image: linear-gradient(to right, rgba(79,186,151,1) 0%,rgba(113,193,154,1) 50%,rgba(34,172,187,1) 100%);
color: white;
}
.step-0-wrap {
.nylas-static-wash-bg;
.hero-text {
font-size: 36px;
line-height: 41px;
}
img.logo.content-mask {
background-color: rgba(255,255,255,0.4);
}
img.icons.content-mask {
background-color: rgba(255,255,255,0.7);
}
}
.step-1-wrap {
.nylas-dark-bg;
.hero-text {
font-size: 29px;
}
.gear-outer-container {
background: #69767f;
border: 1px solid #161f25;
width: 210px;
height: 210px;
margin: 20px auto;
border-radius: 105px;
}
.gear-container {
position: relative;
background: #182025;
border: 4px solid #161f25;
width: 204px;
height: 204px;
margin: 3px auto;
border-radius: 102px;
overflow: hidden;
// http://stackoverflow.com/questions/5736503/how-to-make-css3-rounded-corners-hide-overflow-in-chrome-opera
-webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
}
&.active {
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(720deg); }
}
@keyframes rotate-2 {
from { transform: rotate(7deg); }
to { transform: rotate(727deg); }
}
.gear-large {
animation: rotate 20s ease-in-out;
animation-iteration-count: infinite;
}
.gear-large-0{
animation-name: rotate-2;
animation-direction: reverse;
}
}
.gear-large {
position: absolute;
}
.gear-large-0 {
left: 50%;
top: 50%;
margin-left: -150px;
margin-top: -150px;
transform: rotate(7deg);
}
.gear-large-1 {
left: -147px;
top: -147px;
}
.gear-large-2 {
right: -147px;
top: -147px;
}
.gear-large-3 {
left: 50%;
margin-left: -148px;
bottom: -226px;
}
.gear-small {
position: absolute;
top: 470px;
left: 390px;
}
.wrench {
position: absolute;
right: 310px;
top: 370px;
}
}
.step-2-wrap {
.nylas-blue-wash-bg;
.cell-wrap {
width: 514px;
margin: 30px auto 0 auto;
}
.cell {
width: 242px;
background: rgba(247, 251, 251, 0.25);
border-radius: 10px;
box-shadow: 0px 1px 1px rgba(0,0,0,0.09), inset 0px 0px 1px rgba(255,255,255,0.15);
padding: 30px 0;
p {
margin-top: 1em;
color: rgba(255,255,255,0.7);
font-size: 17px;
line-height: 24px;
}
a {
margin-top: 1em;
}
}
.hero-text {
font-size: 27px;
}
}
.nav-bubbles {
position: absolute;
bottom: 14px;
left: 50%;
margin-left: -24px;
display: flex;
width: 48px;
}
.nav-bubble {
margin: 4px;
width: 8px;
height: 8px;
border-radius: 4px;
background-color: rgba(255,255,255,0.2);
&:hover {
background-color: rgba(255,255,255,0.4);
}
&.active {
background-color: rgba(255,255,255,0.7);
}
}
}

View file

@ -401,6 +401,9 @@ class Atom extends Model
close: ->
@getCurrentWindow().close()
quit: ->
remote.require('app').quit()
# Essential: Get the size of current window.
#
# Returns an {Object} in the format `{width: 1000, height: 700}`
@ -631,7 +634,10 @@ class Atom extends Model
CommandInstaller.installApmCommand resourcePath, false, (error) ->
console.warn error.message if error?
@commands.add 'atom-workspace',
'atom-workspace:add-account': @addAccount
'atom-workspace:add-account': @onAddAccount
onAddAccount: =>
require('remote').getGlobal('application').windowManager.newOnboardingWindow(addingAccount: true)
# Call this method when establishing a secondary application window
# displaying a specific set of packages.
@ -766,17 +772,6 @@ class Atom extends Model
executeJavaScriptInDevTools: (code) ->
ipc.send('call-window-method', 'executeJavaScriptInDevTools', code)
addAccount: =>
@newWindow
title: 'Add an Account'
width: 340
height: 550
toolbar: false
resizable: false
windowType: 'onboarding'
windowProps:
page: 'add-account'
###
Section: Private
###

View file

@ -159,7 +159,7 @@ class Application
@windowManager.showMainWindow(loadingMessage)
@windowManager.ensureWorkWindow()
else
@windowManager.newOnboardingWindow({welcome: true})
@windowManager.newOnboardingWindow()
# The onboarding window automatically shows when it's ready
_resetConfigAndRelaunch: =>
@ -169,7 +169,7 @@ class Application
@config.set('nylas', null)
@config.set('edgehill', null)
@setDatabasePhase('setup')
@windowManager.newOnboardingWindow({welcome: true})
@windowManager.newOnboardingWindow()
_deleteDatabase: (callback) ->
@deleteFileWithRetry path.join(configDirPath,'edgehill.db'), callback

View file

@ -132,23 +132,20 @@ class WindowManager
# Returns a new onboarding window
#
newOnboardingWindow: ({welcome} = {}) ->
options =
newOnboardingWindow: ({addingAccount}={}) ->
page = if addingAccount then "account-choose" else "welcome"
title = if addingAccount then "Add an Account" else "Welcome to N1"
win = @newWindow
title: title
toolbar: false
resizable: false
hidden: true
title: 'Add an Account'
hidden: true # The `PageRouter` will center and show on load
windowType: 'onboarding'
windowProps:
page: 'account-choose'
page: page
pageData: {addingAccount}
uniqueId: 'onboarding'
if welcome
options.title = "Welcome to N1"
options.windowProps.page = "welcome"
@newWindow(options)
# Makes a new window appear of a certain `windowType`.
#
# In almost all cases, instead of booting up a new window from scratch,

View file

@ -108,7 +108,7 @@ class RetinaImg extends React.Component
style = @props.style ? {}
style.WebkitUserDrag = 'none'
style.zoom = if pathIsRetina then 0.5 else 1
style.zoom ?= if pathIsRetina then 0.5 else 1
style.width = style.width / style.zoom if style.width
style.height = style.height / style.zoom if style.height