From ea5ce6ca84e594b292283b741b83eaa3a531575f Mon Sep 17 00:00:00 2001 From: Shane Tomlinson <stomlinson@mozilla.com> Date: Mon, 2 Apr 2012 15:11:11 +0100 Subject: [PATCH] A bunch of changes to make the design more responsive to all screen sizes - mobile, desktop and tablet. * Start with a slight refactor of the CSS and DOM. * Vertical center content when content is smaller than the screen size. * Constrain email addresses and show ellipsis if emails overflow box width. Make sure width is updated when browser window size changes or user changes the screen direction of their device. * If form/email address list is taller than available form area, show scroll bar on desktop or expand contents so that entire app must be scrolled on mobile. * If dialog opened on a tablet or large desktop window, add a gradient to the left size of the white area that matches the gradient on the arrow. * On mobile, footer should always be at the very bottom of the screen unless contents overflow screen height, where it will be pushed off the bottom of the screen. Things to test and be aware of: * Dialog should respond to various screen sizes when resizing browser window on desktop. * IE8 does not support media queries so narrowing the browser window will not force the dialog into "mobile" mode. * Contents in the "form area", wait, delay, or error screens should always be vertically centered. * IE6/IE7 should still see the unsupported dialog with the correct layout (untested on checkin due to #1390) * Wide emails should always be constrained to the screen width on mobile or box width on desktop. The layout should respond to changes in device orientation. * The box surrounding the emails should show a scroll bar on desktop/tablet if there are more emails the available screen height. On mobile, the entire screen height should expand so that the entire app must be scrolled. * On mobile, the footer should always be at the very bottom of the screen unless the form area takes up more vertical space than the device height. issue #1101 issue #1317 --- lib/static_resources.js | 1 + resources/static/css/common.css | 21 +- resources/static/css/m.css | 8 + .../static/dialog/controllers/actions.js | 2 + resources/static/dialog/css/m.css | 80 ++--- resources/static/dialog/css/popup.css | 282 ++++++++---------- .../dialog/resources/screen_size_hacks.js | 104 +++++++ resources/static/dialog/views/pick_email.ejs | 6 +- resources/views/dialog.ejs | 17 +- resources/views/dialog_layout.ejs | 30 +- 10 files changed, 312 insertions(+), 239 deletions(-) create mode 100644 resources/static/dialog/resources/screen_size_hacks.js diff --git a/lib/static_resources.js b/lib/static_resources.js index 272ecd7b9..ff7654e26 100644 --- a/lib/static_resources.js +++ b/lib/static_resources.js @@ -84,6 +84,7 @@ var dialog_js = und.flatten([ '/dialog/resources/internal_api.js', '/dialog/resources/helpers.js', '/dialog/resources/state.js', + '/dialog/resources/screen_size_hacks.js', '/dialog/controllers/actions.js', '/dialog/controllers/dialog.js', diff --git a/resources/static/css/common.css b/resources/static/css/common.css index b034067e7..e91bcc39c 100644 --- a/resources/static/css/common.css +++ b/resources/static/css/common.css @@ -46,6 +46,10 @@ header, section, footer { display: block; } +ul, li { + list-style-type: none; +} + .sans { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } @@ -103,9 +107,6 @@ input[type=password] { -moz-box-shadow: 1px 1px 0 rgba(255,255,255,0.5); -o-box-shadow: 1px 1px 0 rgba(255,255,255,0.5); box-shadow: 1px 1px 0 rgba(255,255,255,0.5); - - /* Fix webkit putting an inner box shadow on the input elements. Issue #1313 */ - -webkit-appearance: caret; } input[type=email]:focus, @@ -137,6 +138,7 @@ input[type=password]:disabled { input[type=radio], input[type=checkbox] { cursor: pointer; + margin-left: 2px; /* necessary or chrome cuts off part of the radio button */ } button, @@ -245,8 +247,7 @@ button[disabled], .submit_disabled button, .submit_disabled .button, } -.submit #cancel, -#signIn .submit #cancel { +.submit #cancel { float: right; margin-right: 15px; line-height: 28px; @@ -286,11 +287,9 @@ header a { header a.home { width: 80px; height: 21px; - background-image: url('/i/icon.png'); + background: url("/i/icon.png") 0px 4px no-repeat; + text-indent: -9999px; display: block; - background-position: left 4px; - background-repeat: no-repeat; - text-indent: -999px; } footer { @@ -331,10 +330,6 @@ footer .help { color: #ff0000; } -#error ul, #error li { - list-style-type: none; -} - #wait strong, #error strong, #delay strong { color: #222; font-weight: bold; diff --git a/resources/static/css/m.css b/resources/static/css/m.css index 66274ef26..f9f9b317a 100644 --- a/resources/static/css/m.css +++ b/resources/static/css/m.css @@ -235,4 +235,12 @@ margin: 115px 0 28px 0; /* put a margin-top on so that it does not go under the header */ } + + input[type=email], + input[type=password] { + + /* Fix webkit putting an inner box shadow on the input elements. Issue #1313 */ + -webkit-appearance: caret; + } + } diff --git a/resources/static/dialog/controllers/actions.js b/resources/static/dialog/controllers/actions.js index 315b787cf..2a6ea4850 100644 --- a/resources/static/dialog/controllers/actions.js +++ b/resources/static/dialog/controllers/actions.js @@ -26,6 +26,8 @@ BrowserID.Modules.Actions = (function() { runningService = name; } + bid.resize(); + return module; } diff --git a/resources/static/dialog/css/m.css b/resources/static/dialog/css/m.css index f3e6dc325..6ffd5f551 100644 --- a/resources/static/dialog/css/m.css +++ b/resources/static/dialog/css/m.css @@ -4,18 +4,10 @@ @media screen and (max-width: 640px) { - #wrapper { - min-width: 320px; - width: 100%; - margin: 0 auto; - } - header, footer { + position: static; padding: 5px 20px; - } - - header ul li:nth-child(2) { - display: none; + background-image: url("/i/bg.png"); } button { @@ -28,12 +20,11 @@ font-size: 17px; } - section { - position: static; - overflow: visible; + section, .arrow, .arrowContainer { display: none; } + .inputs > li > label { font-size: 18px; margin-bottom: 10px; @@ -43,8 +34,24 @@ font-size: 20px; } - #signIn, + #selectEmail { + top: auto; + left: auto; + bottom: auto; + right: auto; + overflow-y: inherit; + position: static; + width: auto; + } + + #signIn { + top: 45px; /* 45px is a magic number - the height of the favicon area */ + right: 0; + width: auto; + } + #favicon { + overflow: inherit; display: block; width: 100%; /* Changed this from relative to static to fix issue #1309 - tooltips @@ -57,15 +64,19 @@ padding: 10px; } - #signIn { - padding: 0; - } - #signIn .container { width: 100%; padding: 20px; } + .pickemail #signIn .container { + padding-top: 0; + } + + #signIn .vertical { + position: static; + } + #signIn form { padding: 0; } @@ -102,25 +113,9 @@ display: none; } - .arrow { - display: none; - } - - #checkemail p { - height: 250px; - } - - #signIn .vertical { - padding: 10px; - } - - #signIn .vertical ul li { - margin-top: 20px; - } - #selectEmail > .inputs > li > label { margin: 0; - padding: 15px 1px; + padding: 25px 0; } #signIn .submit { @@ -139,14 +134,23 @@ margin-bottom: 20px; } - .form_section { - margin-top: 20px; + #content { + position: relative; + top: 0; + bottom: 0; } - #content, .form_section, .inputs, .vertical { + .form_section, + .vertical { + position: relative; height: auto; + width: 100%; max-height: none; overflow: visible; + left: 0; + right: 0; + top: 0; + bottom: 0; } .submit #cancel, #signIn .submit #cancel { diff --git a/resources/static/dialog/css/popup.css b/resources/static/dialog/css/popup.css index 64f80ca37..c5f917eb3 100644 --- a/resources/static/dialog/css/popup.css +++ b/resources/static/dialog/css/popup.css @@ -11,35 +11,48 @@ h2 { font-weight: bold; } - -#wrapper { - min-width: 640px; - position: relative; +header, +footer { + position: absolute; + width: 100%; + padding: 20px; + z-index: 2; } +header { + top: 0; + font-weight: bold; + border-bottom: 1px solid #c7c6c1; + background-color: rgba(0,0,0,0.05); + -ms-filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#0c000000,endColorstr=#0c000000); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#0c000000,endColorstr=#0c000000); + zoom: 1; + -webkit-box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; + -moz-box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; + -o-box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; + box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; +} -.vertical { - height: 230px; - overflow-x: hidden; - overflow-y: auto; +footer { + bottom: 0; + font-size: 12px; + color: #62615F; + text-shadow: 1px 1px 0 rgba(255,255,255,0.5); + border-top: 1px solid #c7c6c1; } -.table { - display: table; - width: 100%; +footer a { + color: #549FDC; } -.table .vertical { - display: table-cell; - vertical-align: middle; +label { + display: block; + color: #62615F; + text-shadow: 1px 1px 0 rgba(255,255,255,0.5); } -#content { - position: relative; - height: 250px; - overflow: hidden; - /* Fix for IE6 not displaying the unsupported dialog correctly */ - _width: 100%; +label.selectable { + cursor: pointer; } section { @@ -52,16 +65,51 @@ section { z-index: 0; } +.table { + display: table; + width: 100%; + height: 100%; +} + +.table .vertical { + display: table-cell; + vertical-align: middle; +} + +/* +.contents { + max-height: 210px; + overflow-x: hidden; + overflow-y: auto; +}*/ + + section > .contents { display: table-cell; vertical-align: middle; height: 250px; } +.form_section { + width: 250px; +} + + .contents > strong { display: none; } +#content { + position: absolute; + overflow: hidden; + left: 0; + right: 0; + top: 61px; + bottom: 61px; + /* Fix for IE6 not displaying the unsupported dialog correctly */ + _width: 100%; +} + #wait, #error, #delay { text-align: center; z-index: -1; @@ -156,34 +204,51 @@ section > .contents { position: absolute; left: 0; top: 0; + bottom: 0; + width: 325px; } -#signIn .container { +#signIn .table { /** * Set the width of the container for when the arrow animation happens * otherwise the buttons slide right with the arrow */ - width: 325px; + /*width: 325px;*/ padding: 20px 52px 20px 20px; } -.arrow { +.arrowContainer { width: 40px; - height: 250px; + position: absolute; + right: 0; + top: 0; + bottom: 0; + background-color: #fff; + background-image: -webkit-linear-gradient(left, #c7c7c7, #fff 2px); + background-image: -moz-linear-gradient(left, #c7c7c7, #fff 2px); + background-image: -ms-linear-gradient(left, #c7c7c7, #fff 2px); + background-image: -o-linear-gradient(left, #c7c7c7, #fff 2px); + background-image: linear-gradient(left, #c7c7c7, #fff 2px); +} + +.arrow { display: block; position: absolute; - right: 0px; top: 0; + bottom: 0; + left: 0; + right: 0; background-image: url('/i/arrow.png'); background-repeat: no-repeat; background-position: center; - background-color: #fff; + background-color: transparent; } #favicon { position: absolute; left: 375px; top: 0; + bottom: 0; right: 0; z-index: 10; } @@ -198,8 +263,7 @@ section > .contents { } #favicon .vertical { - display: block; - line-height: 250px; + display: table-cell; text-align: center; overflow: hidden; text-overflow: ellipsis; @@ -211,49 +275,24 @@ div#required_email { font-weight: bold; } + /* #signIn .vertical { position: relative; } +*/ -#signIn .vertical ul { - list-style-type: none; -} - -#signIn .vertical ul li { - margin-top: 10px; -} - -#signIn .vertical ul li:first-child { - margin-top: 0; -} - -#signIn .submit { - line-height: 28px; - margin-top: 10px; - color: #333; - font-size: 11px; - line-height: 1.2; -} - -#signIn .submit > a { - color: #549FDC; - margin-right: 0; - float: none; -} - - /* -#signIn .submit > p { - line-height: 13px; - clear: right; - text-align: center; +#selectEmail { + position: absolute; + top: 20px; + bottom: 20px; + left: 20px; + width: 250px; + overflow-y: auto; } -*/ -#signIn label.half, -.half { - width: 50%; - display: inline-block; - float: left; +#selectEmail.center { + position: static; + overflow-y: visible; } #forgotPassword { @@ -262,21 +301,16 @@ div#required_email { font-size: 11px; } -#signIn button.create { - font-size: 14px; - height: 28px; - padding: 0 10px; - float: right; +.inputs { + line-height: 18px; } -label { - display: block; - color: #62615F; - text-shadow: 1px 1px 0 rgba(255,255,255,0.5); +.inputs > li { + margin-top: 10px; } -label.selectable { - cursor: pointer; +.inputs > li:first-child { + margin-top: 0; } .inputs > li > label { @@ -285,7 +319,7 @@ label.selectable { text-overflow: ellipsis; } -#signIn #selectEmail > .inputs > li { +#selectEmail > .inputs > li { margin: 0; } @@ -294,87 +328,31 @@ label.selectable { white-space: nowrap; } -.inputs > li > label.preselected { +#selectEmail > .inputs > li > label.preselected { font-weight: bold; } -.inputs > li:only-child > label.selectable { +#selectEmail > .inputs > li:only-child > label.selectable { cursor: default; } -.learn { - display: inline-block; - font-size: 12px; - color: #62615F; - text-shadow: 1px 1px 0 rgba(255,255,255,0.5); +#selectEmail > .inputs > li:only-child input[type=radio] { + display: none; } -#signIn .submit > p { - display: block; +.submit { + line-height: 28px; + margin-top: 10px; + color: #333; + font-size: 11px; + line-height: 1.2; } -#signIn .submit > p + p:last-child { +.submit > p + p:last-child { margin-top: 15px; padding-left: 10px; } -#signIn .submit a { - color: #549FDC; -} - -footer .learn a { - color: #549FDC; -} - -.checkregistration p { - color: #62615F; - text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5); -} - -header, -footer { - display: block; - width: 100%; -} - -header { - padding: 20px; - font-weight: bold; - border-bottom: 1px solid #c7c6c1; - background-color: rgba(0,0,0,0.05); - -ms-filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#0c000000,endColorstr=#0c000000); - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#0c000000,endColorstr=#0c000000); - zoom: 1; - -webkit-box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; - -moz-box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; - -o-box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; - box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, 0.1) inset; -} - -header ul { - display: block; - float: left; -} - -header ul li { - margin: 0 10px 0 0; - color: #222; - text-shadow: 1px 1px 0 rgba(255,255,255,0.5); -} - -footer { - border-top: 1px solid #c7c6c1; - padding: 20px; -} - -.inputs { - line-height: 18px; -} - -.inputs > li:only-child input[type=radio] { - display: none; -} - a.emphasize { background-color: #F0EFED; color: #4E4E4E; @@ -384,28 +362,12 @@ a.emphasize { line-height: 18px; } -.submit > button { - margin: 0 0 0 5px; -} - -#newEmail { - -moz-box-flex: 1; -} - .newuser, .forgot, .returning { display: none; } -#create_text_section { - color: #222; -} - -#checkemail { - text-align: center; -} - #your_computer_content { width: 90%; margin: auto; diff --git a/resources/static/dialog/resources/screen_size_hacks.js b/resources/static/dialog/resources/screen_size_hacks.js new file mode 100644 index 000000000..0d48fa3d9 --- /dev/null +++ b/resources/static/dialog/resources/screen_size_hacks.js @@ -0,0 +1,104 @@ +(function() { + /** + * This is a hack to feign fixed headers/footers and dynamic body content + * size. On mobile, it helps keep the footer at the very bottom of the + * screen without the jumpiness that comes with position: fixed in both + * Fennec and Android native browser. On desktop/tablet browsers, resizing + * the #content element causes the contents to be vertically centered. + */ + function onResize() { + var selectEmailEl = $("#selectEmail"), + contentEl = $("#content"); + + selectEmailEl.css("position", "static"); + if($(window).width() >= 640) { + // First, remove the mobile hacks + selectEmailEl.css("width", ""); + contentEl.css("min-height", ""); + + // This is a hack for desktop mode which centers the form vertically in + // the middle of its container. We have to do this hack because we use + // table cell vertical centering when the browserid window is large and + // the number of emails small, but if the screen size is smaller than the + // number of emails, we have to print a scrollbar - but only around the + // emails. + + // set the height to static so that we can get the height without + // constraints. + var height = selectEmailEl.innerHeight(); + // re-introduce constraints + + if(height < $("#signIn .vertical").innerHeight()) { + selectEmailEl.addClass("center"); + } + else { + selectEmailEl.removeClass("center"); + } + } + else { + // First, remove the desktop hacks + selectEmailEl.removeClass("center"); + + // Hack to make sure the email addresses stay within their container. + // We have to do this ghettoness because table-cells (which are used to + // vertically center everything) expand to fully contain their children + // and the ellipsis never show up as expected. + + // First, find the maximum width that emails can be. + selectEmailEl.css("width", "10px").removeClass("center"); + var constrainedWidth = $("#signIn .contents").innerWidth(); + + // Find the real maximum width. + selectEmailEl.css("width", ""); + var maxEmailWidth = selectEmailEl.innerWidth(); + + // If we have a too large an email, constrain the width. + if(maxEmailWidth > constrainedWidth) { + selectEmailEl.css("width", constrainedWidth + "px"); + } + + // Hack to find the min-height of the content area the footer is pushed + // to the bottom if the contents are too small, and expands off the + // bottom if the contents are large. + + // Unconstrain everything so that we can find natural heights of all + // elements. + $("section,#signIn").css("position", "static"); + contentEl.css("min-height", "0"); // required for Chrome to correctly resize the window + + var headerHeight = $("header").outerHeight(); + var footerHeight = $("footer").outerHeight(); + var windowHeight = $(window).height(); + + // Get the amount of space between the header and footer with the + // caveat that we are forcing the footer to be at the bottom of the + // screen if the form's unconstrained height is smaller than the + // content area's height. + var contentHeight = windowHeight - headerHeight - footerHeight; + + // Get the natural height of the form + var formHeight = $("#formWrap").outerHeight(); + + // set the min height of the content area. This serves two purposes. + // First off, for accounts with only one or two emails, it will ensure + // that the footer is at the bottom of the mobile screen with the + // emails (or any other form) vertically centered on the screen. + // Secondly, if an account has many many emails, it will ensure the + // content area expands correctly to keep any email addresses from + // being hidden. This means the footer will be off the screen and the + // mobile user must scroll the entire content area up and down + // - contrast this to the desktop version where users with many email + // addresses only have to scroll the list of emails. + contentHeight = Math.max(100, contentHeight, formHeight); + contentEl.css("min-height", contentHeight + "px"); + + $("section,#signIn").css("position", ""); + } + + selectEmailEl.css("position", ""); + } + + $(window).resize(onResize); + onResize(); + BrowserID.resize = onResize; +}()); diff --git a/resources/static/dialog/views/pick_email.ejs b/resources/static/dialog/views/pick_email.ejs index a9dbdae0a..c4e6d6f30 100644 --- a/resources/static/dialog/views/pick_email.ejs +++ b/resources/static/dialog/views/pick_email.ejs @@ -32,9 +32,13 @@ format(' href="%s" target="_new"', [privacy_url]) ]) %> </p> + <p> <% } %> <button id="signInButton"><%= gettext('sign in') %></button> - <br style="clear: both" /> + <!--br style="clear: both" /--> + <% if (privacy_url && tos_url) { %> + </p> + <% } %> </div> </div> diff --git a/resources/views/dialog.ejs b/resources/views/dialog.ejs index 775879d65..5e9b7ebe6 100644 --- a/resources/views/dialog.ejs +++ b/resources/views/dialog.ejs @@ -4,15 +4,22 @@ <section id="formWrap"> <form novalidate> <div id="favicon"> - <div class="vertical"> - <strong id="sitename"></strong> + <div class="table"> + <div class="vertical"> + <strong id="sitename"></strong> + </div> </div> </div> <div id="signIn"> - <div class="arrow"></div> - <div class="container"> - <div class="vertical contents"></div> + <div class="arrowContainer"> + <div class="arrow"></div> + </div> + <div class="table container"> + <div class="vertical"> + <div class="contents"> + </div> + </div> </div> </div> </form> diff --git a/resources/views/dialog_layout.ejs b/resources/views/dialog_layout.ejs index 24f728902..73a3afd50 100644 --- a/resources/views/dialog_layout.ejs +++ b/resources/views/dialog_layout.ejs @@ -17,33 +17,19 @@ <title><%= gettext('BrowserID') %></title> </head> <body class="waiting"> - <div id="wrapper"> - <header id="header" class="cf"> - <ul> - <li><a class="home" target="_blank" href="/">BrowserID Home</a></li> - </ul> - </header> + <header id="header"> + <h1><a class="home" target="_blank" href="/">BrowserID Home</a></h1> + </header> - <div id="content"> - <%- body %> - </div> - - <footer> - <!--ul class="cf"> - <li>By <a href="http://mozillalabs.com">Mozilla Labs</a></li> - - <li>—</li> - <li><a href="#">Privacy</a></li> - <li><a href="#">TOS</a></li> - </ul--> + <div id="content"> + <%- body %> + </div> - <div class="learn"> + <footer> <%- format(gettext('BrowserID is the fast and secure way to sign in — <a %s>learn more</a>'), [" href='/about' target='_blank'"]) %> - </div> - </footer> + </footer> - </div> <% if (useJavascript !== false) { %> <%- cachify_js(util.format('/production/%s/dialog.js', locale)) %> -- GitLab