Use mobile-first approach and sticky positioning

This commit is contained in:
zo 2017-08-06 10:46:13 +02:00 committed by Mikaël Bouillot
parent b6e26e75c8
commit a0790a5bee
4 changed files with 291 additions and 305 deletions

View file

@ -1,17 +1,6 @@
.wrapper {
margin-top: 200px;
min-height: calc(100vh - 200px);
}
.header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 200px;
width: auto;
background: #6D7DD2;
background-image: url('/img/2penguins.svg');
background-repeat: no-repeat;
background-size: 300px;

View file

@ -1,50 +1,3 @@
function throttle(fn, threshhold) {
threshhold || (threshhold = 150)
var last, deferTimer
return function () {
var now = +new Date
if (last && now < last + threshhold) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fn()
}, threshhold)
} else {
last = now
fn()
}
};
}
/* Fixed topbar and tag menu when header is hidden by scroll */
// Cross browser scrollTop position
var getScrollTop
if (typeof window.pageYOffset != 'undefined') {
getScrollTop = function () { return window.pageYOffset }
} else {
if (document.documentElement.clientHeight)
getScrollTop = function () { return document.documentElement.scrollTop }
else getScrollTop = function () { return document.body.scrollTop }
}
var bodyClassName = ''
function checkScroll () {
var isHiddenHeader = getScrollTop() >= 200 ? 'hidden-header' : ''
if (isHiddenHeader !== bodyClassName) {
document.body.className = bodyClassName = isHiddenHeader
}
}
// If header is present
if (document.querySelector('.header')) {
window.onscroll = throttle(checkScroll, 10)
checkScroll()
} else {
document.body.className = 'hidden-header'
}
/* Tags menu filter */
var versions = document.querySelector('.versions')
@ -121,6 +74,18 @@ input.oninput = function () {
}
}
// prevent chrome auto-scrolling to element
document.querySelectorAll('input').forEach(function(el) {
el.onkeydown = function (e) {
var before = wrapper.scrollTop
function reset() {
wrapper.scrollTop = before
}
window.requestAnimationFrame(reset)
setTimeout(reset, 0)
}
})
/* Tags menu tree */
@ -154,7 +119,6 @@ openMenu.onclick = tag.onclick = function (e) {
wrapper.classList.toggle('show-menu')
}
sidebar.onclick = function (e) {
console.log(e.target.className)
if (e.target === this || e.target.classList.contains('close-menu')) {
wrapper.classList.remove('show-menu')
}
@ -162,44 +126,35 @@ sidebar.onclick = function (e) {
/* Linenumbers navigation */
document.querySelector('.go-top').onclick = function() {
wrapper.scrollTop = 0
wrapper.scrollLeft = 0
}
// When using linenumbers's anchor
// it jump the line a the top of the page
// and it's hidden under the fixed topbar element.
// To prevent this let's make the jump at half the height
// of the screen
var height
var middle
// cross browser height calculation
function getHeight () {
height = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight
middle = height / 5
}
window.onresize = getHeight
getHeight()
// To prevent this let's jump to a few lines behind the top
// This will capture hash changes while on the page
function offsetAnchor() {
function offsetAnchor(e) {
if (e && e.preventDefault) e.preventDefault()
if (location.hash.length !== 0) {
var rect = document.querySelector(location.hash).getBoundingClientRect()
var elTop = rect.top + window.scrollY
window.scrollTo(window.scrollX, elTop - middle)
var el = document.querySelector(location.hash)
if (el) {
var offsetTop = el.offsetTop
wrapper.scrollTop = offsetTop < 100 ? 200 : offsetTop + 100
}
}
}
window.onhashchange = offsetAnchor
// This is here so that when you enter the page with a hash,
// it can provide the offset in that case too. Having a timeout
// seems necessary to allow the browser to jump to the anchor first.
window.setTimeout(offsetAnchor, 1)
// it can provide the offset in that case too.
window.requestAnimationFrame(offsetAnchor)
// recalculate scroll when page is fully loaded
// in case of slow rendering very long pages.
window.onload = function () {
getHeight()
offsetAnchor()
window.requestAnimationFrame(offsetAnchor)
}

View file

@ -20,8 +20,8 @@ html {
}
*::-webkit-scrollbar {
width: 0.6rem;
height: 0.6rem;
width: 9px;
height: 9px;
}
.sidebar nav::-webkit-scrollbar-corner {
@ -44,12 +44,19 @@ body::-webkit-scrollbar-thumb {
background: #ccc;
}
.wrapper::-webkit-scrollbar-corner {
background: #666;
}
.wrapper::-webkit-scrollbar-track {
background: #666;
}
.wrapper::-webkit-scrollbar-thumb {
background: #ccc;
}
body {
background: #fff;
font-family: 'Ubuntu', sans-serif;
overflow-y: scroll;
overflow-x: hidden;
overflow-x: auto;
}
pre {
@ -108,6 +115,7 @@ h2 {
/* IE 9 */
.browserupgrade {
position: absolute;
top: 0em;
@ -129,64 +137,14 @@ h2 {
}
/* main */
.wrapper {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: stretch;
-ms-flex-align: stretch;
align-items: stretch;
}
main {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
-webkit-box-ordinal-group: 2;
-ms-flex-order: 1;
order: 1;
margin-left: 235px;
min-height: 100vh;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: stretch;
-ms-flex-align: stretch;
align-items: stretch;
}
main > div {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
}
.scrollable {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
margin-top: 2.66em;
}
.scrollable > div {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1
}
/* sub-header */
.sub-header {
position: absolute;
top: 156px;
left: 0;
right: 0;
height: 2.7em;
margin-top: -2.7em;
text-align: center;
}
.select-projects {
padding: 0.5rem 0.6rem;
@ -208,9 +166,6 @@ main > div {
background-position: bottom 0.45em right 0.3em;
background-size: 9%;
overflow: visible;
text-overflow: auto;
opacity: 0.7;
}
.select-projects:-moz-focusring {
@ -240,12 +195,6 @@ main > div {
.topbar {
background: #c0c0c0;
position: absolute;
top: 0;
left: 235px;
right: 0;
width: auto;
height: auto;
z-index: 20;
}
@ -279,7 +228,7 @@ main > div {
font-size: 0.9em;
width: 100%;
border: 0 none;
padding: 0.9rem 1rem;
padding: 0.45em;
margin: 0;
}
.filter input:focus,
@ -301,8 +250,8 @@ main > div {
.search {
position: relative;
float: right;
width: 40%;
padding: 0.5em;
padding-top: 0;
}
.search button:focus,
.search button:hover {
@ -318,6 +267,10 @@ main > div {
background: #eee;
}
.filter {
padding: 0.5em;
background: #222;
}
.filter input {
color: #fff;
background: #333;
@ -337,10 +290,7 @@ main > div {
/* footer */
.footer {
position: fixed;
bottom: 0;
left: 235px;
right: 0;
height: 25px;
padding: 0.3rem 2.5rem;
padding-left: 1.5rem;
font-size: 0.8em;
@ -405,11 +355,6 @@ main > div {
/* sidebar */
.sidebar {
position: absolute;
top: 0;
bottom: 0;
width: 235px;
min-height: 100vh;
height: auto;
z-index: 10;
}
@ -452,7 +397,6 @@ main > div {
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
max-height: 100vh;
}
.js .sidebar nav {
will-change: transform;
@ -550,6 +494,11 @@ main > div {
display: block;
}
.no-js .sidebar:target {
left: 0;
height: 100vh;
overflow: auto;
}
.no-js .sidebar nav ul span {
display: none;
}
@ -565,14 +514,6 @@ main > div {
}
/* fixed topbar and sidebar when header isn't in viewport */
.hidden-header .topbar,
.hidden-header .sidebar {
position: fixed;
}
/* filter results */
.filter-results a {
@ -690,9 +631,6 @@ main > div {
.lxrcode {
position: relative;
}
.lxrcode table {
height: 100%;
}
.lxrcode pre {
margin: 0;
padding-top: 1em;
@ -700,9 +638,11 @@ main > div {
min-height: 100%;
}
.lxrcode,
.highlighttable,
.linenodiv,
.highlight {
min-height: 100%;
height: 100%;
}
.linenodiv {
@ -740,7 +680,8 @@ main > div {
color: inherit;
font-weight: 700;
background-color: #f4f6ff;
box-shadow: 0 0 0 1px #f4f6ff;
-webkit-box-shadow: 0 0 0 1px #f4f6ff;
box-shadow: 0 0 0 1px #f4f6ff;
border-radius: 0.2em;
}
.highlight a:hover {
@ -911,18 +852,45 @@ main > div {
.highlight .il { color: #0000DD } /* Literal.Number.Integer.Long */
/* layout */
main {
min-height: calc(100vh - 45px - 25px);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
position: relative;
}
.js .wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: auto;
height: auto;
overflow-x: auto;
overflow-y: scroll;
}
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
}
/* responsive */
.open-menu {
display: none;
margin-right: 1.5rem;
margin-right: 1.2rem;
margin-left: -0.5rem;
}
.open-menu::before {
font-size: 1.5rem;
line-height: 0;
position: absolute;
top: 1.45rem;
left: 0.6rem;
vertical-align: -0.31rem;
}
.close-menu {
@ -934,98 +902,172 @@ main > div {
font-size: 1.7rem;
width: 2.8rem;
height: 2.8rem;
margin-left: 0em;
}
.close-menu {
margin-left: 0.3em;
margin-top: -0.3em;
}
@media screen and (max-width: 768px) {
/* responsive menu */
.wrapper .sidebar {
position: fixed;
z-index: 30;
top: 0;
bottom: 0;
left: -100%;
width: 100%;
height: auto;
background: rgba(3, 8, 35, 0);
-webkit-transition: left 0.2s ease 0.2s, background 0.2s ease;
transition: left 0.2s ease 0.2s, background 0.2s ease;
}
.sidebar .filter,
.sidebar nav {
width: 60%;
max-width: 330px;
}
.show-menu .sidebar {
background: rgba(3, 8, 35, 0.5);
left: 0;
-webkit-transition: left 0.2s ease, background 0.2s ease 0.2s;
transition: left 0.2s ease, background 0.2s ease 0.2s;
}
main {
margin-left: 0;
}
.topbar {
left: 0;
}
.hidden-header .topbar {
left: 0;
}
.footer {
left: 0;
}
.open-menu {
display: inline;
}
.sidebar {
position: fixed;
z-index: 30;
top: 0;
bottom: 0;
left: -100%;
width: 100%;
height: auto;
background: rgba(3, 8, 35, 0);
-webkit-transition: left 0.2s ease 0.2s, background 0.2s ease;
transition: left 0.2s ease 0.2s, background 0.2s ease;
}
.sidebar .filter,
.sidebar nav {
width: 60%;
max-width: 330px;
}
.show-menu .sidebar {
background: rgba(3, 8, 35, 0.5);
left: 0;
-webkit-transition: left 0.2s ease, background 0.2s ease 0.2s;
transition: left 0.2s ease, background 0.2s ease 0.2s;
}
@media screen and (max-width: 600px) {
.sidebar .filter,
.sidebar nav {
width: 85%;
width: calc(100% - 2.8rem);
/* tablet */
@media screen and (min-width: 600px) {
.filter input,
.search input {
padding: 0.9rem 1rem;
}
.lxrtree td a {
padding: 0.15em 0.5em;
}
.search {
float: none;
width: 100%;
padding: 0.5em;
padding-top: 0;
}
.breadcrumb {
padding: 1em 1.2em;
}
.scrollable {
margin-top: 4.5em;
margin-top: 4.9em;
}
.close-menu {
margin-left: 0.3em;
margin-top: -0.3em;
.search,
.filter {
padding: 0;
}
.filter {
padding: 0.5em;
background: #222;
}
.filter button {
height: 2.2em;
right: 0.4em;
.close-menu {
margin-left: 0;
margin-top: 0;
}
.filter input {
padding: 0.45em;
.topbar {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.search input {
padding: 0.45em;
.breadcrumb {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
}
.search {
width: 40%;
}
.sub-header {
text-align: center;
text-align: left;
}
}
/* desktop */
@media screen and (min-width: 769px) {
.open-menu,
.close-menu {
display: none;
}
.sidebar {
position: absolute;
top: -45px;
bottom: -25px;
left: 0;
-webkit-box-ordinal-group: 1;
-ms-flex-order: 0;
order: 0;
width: 235px;
-webkit-box-flex: 0;
-ms-flex: 0 0 235px;
flex: 0 0 235px;
}
.sidebar .filter, .sidebar nav {
width: 100%;
}
.workspace {
-webkit-box-ordinal-group: 2;
-ms-flex-order: 1;
order: 1;
padding-left: 235px;
}
.topbar {
padding-left: 235px;
}
.footer {
padding-left: calc(2.5rem + 235px);
}
.linenos {
left: 235px;
}
.no-js .sidebar {
left: auto;
height: auto;
overflow: visible;
}
}
/* if browser support position: sticky */
@supports ((position: -webkit-sticky) or (position: sticky)) {
.js .header {
position: -webkit-sticky;
position: sticky;
left: 0;
}
.js .footer {
position: -webkit-sticky;
position: sticky;
right: unset;
}
.js .topbar {
position: -webkit-sticky;
position: sticky;
top: 0;
left: 0;
z-index: 2;
}
.js .sub-header {
position: -webkit-sticky;
position: sticky;
left: 0;
}
.js .linenos {
position: -webkit-sticky;
position: sticky;
left: 0;
z-index: 1;
}
.js main {
position: unset;
}
/* desktop */
@media screen and (min-width: 769px) {
.js .linenos {
left: 235px;
}
.js .workspace {
padding-left: unset;
}
.js .sidebar {
position: -webkit-sticky;
position: sticky;
top: 0;
left: 0;
height: 100vh;
margin-top: -45px;
margin-bottom: -25px;
/* screen height minus potential horizontal scrollbar height */
height: calc(100vh - 18px);
/* little hack to hide potential scrollbar gap */
-webkit-box-shadow: 0 18px 0 0 #111;
box-shadow: 0 18px 0 0 #111;
}
}
}

View file

@ -6,7 +6,7 @@
<title>{{title}}</title>
<meta name="description" content="Elixir Cross Referencer">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1" />
<link rel="stylesheet" href="/style.css?v=2">
<link rel="stylesheet" href="/style.css?v=3">
<base href="{{baseurl}}"/>
<script>document.documentElement.className = 'js'</script>
</head>
@ -14,52 +14,52 @@
<!--[if lte IE 9]>
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
<![endif]-->
{% include 'header.html' %}
<header class="sub-header">
<select class="select-projects" onchange="window.location.href=this.value">
{%- for p in projects %}
<option{% if p == project %} selected{% endif %} value="/{{p}}/latest/source">{{p}}</option>
{%- endfor %}
</select>
</header>
<div class="wrapper">
{% include 'header.html' %}
<header class="sub-header">
<select class="select-projects" onchange="window.location.href=this.value">
{%- for p in projects %}
<option{% if p == project %} selected{% endif %} value="/{{p}}/latest/source">{{p}}</option>
{%- endfor %}
</select>
</header>
<header class="topbar">
<div class="breadcrumb">
<a href="{{version}}/{{url}}#menu" class="open-menu icon-menu screenreader" title="Open Menu">Open Menu</a>
{{breadcrumb}}
</div>
<div class="search">
<form method="post" action="{{version}}/ident">
<input placeholder="Search Identifier" type="text" name="i" value="{{ident}}"/>
<button class="icon-search"><span class="screenreader">Go get it</span></button>
</form>
</div>
</header>
<main>
<header class="topbar">
<div class="breadcrumb">
<a href="{{version}}/{{url}}#menu" class="open-menu icon-menu screenreader" title="Open Menu">Open Menu</a>
{{breadcrumb}}
</div>
<div class="search">
<form method="post" action="{{version}}/ident">
<input placeholder="Search Identifier" type="text" name="i" value="{{ident}}"/>
<button class="icon-search"><span class="screenreader">Go get it</span></button>
</form>
</div>
</header>
<div class="scrollable">
{{main}}
</div>
<footer class="footer">
<span class="version"><a href="{{version}}/source">{{project}}</a> <em class="icon-tag">{{tag}}</em></span>
<a title="Go to top of the page" class="go-top icon-up screenreader" href="{{version}}/{{url}}#">Top</a>
<span class="poweredby">powered by <a target="_blank" href="https://github.com/free-electrons/elixir">Elixir 0.2</a></span>
</footer>
<div class="workspace">
{{main}}
</div>
<aside class="sidebar" id="menu">
<nav>
<h3 class="screenreader">Projects</h3>
<ul class="projects">
{%- for p in projects %}
<li{% if p == project %} class="active"{% endif %}><a href="/{{p}}/latest/source">{{p}}</a></li>
{%- endfor %}
</ul>
<h3 class="screenreader">Versions</h3>
<ul class="versions">
{{versions}}
</ul>
</nav>
</aside>
</main>
<aside class="sidebar" id="menu">
<nav>
<h3 class="screenreader">Projects</h3>
<ul class="projects">
{%- for p in projects %}
<li{% if p == project %} class="active"{% endif %}><a href="/{{p}}/latest/source">{{p}}</a></li>
{%- endfor %}
</ul>
<h3 class="screenreader">Versions</h3>
<ul class="versions">
{{versions}}
</ul>
</nav>
</aside>
<footer class="footer">
<span class="version"><a href="{{version}}/source">{{project}}</a> <em class="icon-tag">{{tag}}</em></span>
<a title="Go to top of the page" class="go-top icon-up screenreader" href="{{version}}/{{url}}#">Top</a>
<span class="poweredby">powered by <a target="_blank" href="https://github.com/free-electrons/elixir">Elixir 0.2</a></span>
</footer>
</div>
<script src="/script.js?v=2"></script>
<script src="/script.js?v=3"></script>
</body>
</html>