Compare commits

...

11 Commits

Author SHA1 Message Date
986a954f33 Upgrading Casper to 2.2.0
no issue
2018-04-10 23:27:53 +02:00
636882bce4 Fix mobile display bug on smaller screens 2018-04-10 20:34:35 +02:00
79e113226c Multiple authors in Casper (#448) 2018-04-10 20:19:29 +02:00
8e865b797b Maybe fix it properly this time instead of completely failing 2018-04-10 15:29:09 +02:00
a655b59e69 Fix link target size 2018-04-10 15:27:01 +02:00
b078dcb86e Updated hover animations 2018-04-10 15:21:25 +02:00
d5002f2c51 🐛 Fixed infinitescroll when a paged url is loaded directly (#447)
closes #445
- added an fn to sanitize the pathname, that might include a pagination url
- fixed an issue, where the request would still be made if the current page is bigger than max pages
- added comments
2018-04-10 10:04:43 +01:00
41bcbb7157 💨 Infinite scroll perf improvements
no issue
- swap jQuery HTML parsing and insertion for pure DOM
- remove fade animation
- increase buffer by 100px so next page request happens sooner
2018-04-09 11:03:48 +01:00
452776939c Added yarn.lock and updated README for new yarn scripts
no issue
2018-04-09 16:19:05 +08:00
57c31ddacb Add HTML lang head tag (#429)
Refs #286
2018-04-06 20:49:42 +02:00
21088b1d38 Slack -> Forum
- replacing references to slack with forum
2018-04-02 16:35:21 +01:00
12 changed files with 4648 additions and 55 deletions

View File

@ -1,4 +1,4 @@
Do you need help or have a question? Please come chat in Slack: https://ghost.org/slack 👫.
Do you need help or have a question? Please come chat in our forum: https://forum.ghost.org 👫.
If you're filing a bug 🐛, please include the following information:

View File

@ -35,8 +35,8 @@ One really neat trick is that you can also create custom one-off templates just
Casper styles are compiled using Gulp/PostCSS to polyfill future CSS spec. You'll need Node and Gulp installed globally. After that, from the theme's root directory:
```bash
$ npm install
$ gulp
$ yarn install
$ yarn dev
```
Now you can edit `/assets/css/` files, which will be compiled to `/assets/built/` automatically.
@ -44,7 +44,7 @@ Now you can edit `/assets/css/` files, which will be compiled to `/assets/built/
The `zip` Gulp task packages the theme files into `dist/<theme-name>.zip`, which you can then upload to your site.
```bash
$ gulp zip
$ yarn zip
```
# PostCSS Features Used

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -395,9 +395,9 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
}
.post-card:hover {
box-shadow: 0 0 1px rgba(39,44,49,0.10), 0 3px 16px rgba(39, 44, 49,0.07);
transition: all 0.3s ease;
transform: translate3D(0, -1px, 0);
box-shadow: rgba(39,44,49,0.07) 8px 28px 50px, rgba(39, 44, 49, 0.04) 1px 6px 12px;
transition: all 0.4s ease;
transform: translate3D(0, -1px, 0) scale(1.02);
}
.post-card-image-link {
@ -416,6 +416,7 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
.post-card-content-link {
position: relative;
flex-grow: 1;
display: block;
padding: 25px 25px 0;
color: var(--darkgrey);
@ -452,20 +453,151 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
}
.post-card-meta {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 25px 25px;
}
.profile-image-wrapper,
.avatar-wrapper {
position: absolute;
display: inline-block;
width: 28px;
}
.avatar-wrapper {
display: inline-block;
box-sizing: content-box;
margin-right: 3px;
border: 2px solid #fff;
text-align: center;
background-color: color(var(--lightgrey) l(+10%));
border-radius: 100%;
}
.avatar-wrapper svg {
width: 28px;
height: 28px;
}
.author-profile-image {
box-sizing: content-box;
margin-right: 5px;
width: 25px;
height: 25px;
width: 28px;
height: 28px;
border: 2px solid #fff;
border-radius: 100%;
object-fit: cover;
}
.post-card-author {
font-size: 1.3rem;
.post-card-meta .profile-image-wrapper,
.post-card-meta .avatar-wrapper {
position: relative;
}
.author-list {
display: flex;
margin-bottom: 0;
padding: 0;
max-width: calc(100% - 120px);
list-style-type: none;
}
.author-list-item {
position: relative;
margin: 0;
padding: 0;
width: 25px;
}
.moving-avatar {
transition: all 0.5s cubic-bezier(0.4, 0.01, 0.165, 0.99);
transform: translate(0px);
}
.moving-avatar.right {
transform: translateX(10px);
}
.moving-avatar.left {
transform: translateX(-10px);
}
.author-list-item:nth-child( 1 ) {
z-index: 9;
}
.author-list-item:nth-child( 2 ) {
z-index: 8;
}
.author-list-item:nth-child( 3 ) {
z-index: 7;
}
.author-list-item:nth-child( 4 ) {
z-index: 6;
}
.author-list-item:nth-child( 5 ) {
z-index: 5;
}
.author-list-item:nth-child( 6 ) {
z-index: 4;
}
.author-list-item:nth-child( 7 ) {
z-index: 3;
}
.author-list-item:nth-child( 8 ) {
z-index: 2;
}
.author-list-item:nth-child( 9 ) {
z-index: 1;
}
.z-999 {
z-index: 999;
}
.author-name-tooltip {
position: absolute;
bottom: 36px;
z-index: 999;
display: block;
padding: 2px 8px;
color: white;
font-size: 1.2rem;
letter-spacing: 0.2px;
white-space: nowrap;
background: var(--darkgrey);
border-radius: 3px;
box-shadow: rgba(39,44,49,0.08) 0 12px 26px, rgba(39, 44, 49, 0.03) 1px 3px 8px;
opacity: 0;
transition: all 0.3s cubic-bezier(0.4, 0.01, 0.165, 0.99);
transform: translateY(6px);
pointer-events: none;
}
.author-list-item:hover .author-name-tooltip {
opacity: 1.0;
transform: translateY(0px);
}
@media (max-width: 650px) {
.author-name-tooltip {
display: none;
}
}
.reading-time {
color: var(--midgrey);
font-size: 1.2rem;
font-weight: 500;
letter-spacing: 0.5px;
text-transform: uppercase;
@ -1162,12 +1294,6 @@ Usage (In Ghost editor):
align-items: center;
}
.author-card .author-profile-image {
margin-right: 15px;
width: 60px;
height: 60px;
}
.author-card-name {
margin: 0 0 2px 0;
padding: 0;
@ -1212,6 +1338,155 @@ Usage (In Ghost editor):
text-decoration: none;
}
.post-full-authors {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
padding-top: 40px;
border-top: color(var(--lightgrey) l(+10%)) 1px solid;
}
.post-full-authors-content {
margin-bottom: 20px;
}
.post-full-authors-content p {
margin-bottom: 0;
color: var(--midgrey);
font-size: 1.4rem;
letter-spacing: 0.2px;
text-align: center;
text-transform: uppercase;
}
.post-full-authors-content a {
display: inline-block;
color: color(var(--darkgrey) l(+20%));
font-size: 1.4rem;
font-weight: 600;
text-transform: uppercase;
}
.post-full-footer .author-list-item {
width: 42px;
}
@media (max-width: 650px) {
.post-full-footer .author-list-item {
width: 54px;
height: 54px;
}
}
.post-full-footer .author-list-item > a {
position: absolute;
display: inline-block;
width: 48px;
}
.post-full-footer .author-profile-image,
.post-full-footer .avatar-wrapper {
width: 48px;
height: 48px;
}
.author-card .author-profile-image,
.author-card .avatar-wrapper {
margin-right: 15px;
position: relative;
}
.post-full-footer .avatar-wrapper svg {
width: 46px;
height: 46px;
}
.author-list-item .author-card {
position: absolute;
bottom: 12px;
left: calc(-160px + 28px);
z-index: 999;
display: block;
width: 320px;
font-size: 1.4rem;
letter-spacing: 0.2px;
background: white;
border-radius: 6px;
box-shadow: rgba(39,44,49,0.08) 0 12px 26px, rgba(39, 44, 49, 0.03) 1px 3px 8px;
opacity: 0;
transition: all 0.3s cubic-bezier(0.4, 0.01, 0.165, 0.99);
transform: scale(0.98) translateY(10px);
pointer-events: none;
}
.author-list-item .author-card:before {
content: "";
position: absolute;
top: 100%;
left: calc(160px - 20px + 6px);
display: block;
width: 0;
height: 0;
border-top: 12px solid #fff;
border-right: 12px solid transparent;
border-left: 12px solid transparent;
}
.author-list-item .author-card.hovered {
opacity: 1.0;
transform: scale(1) translateY(0px);
pointer-events: auto;
}
.author-card .basic-info {
display: flex;
flex-direction: column;
align-items: center;
padding: 30px 20px 20px 20px;
color: #fff;
background: var(--darkgrey);
border-radius: 6px 6px 0 0;
}
.author-card .basic-info h2 {
margin: 1em 0 0.5em;
}
.author-card .bio {
padding: 20px 20px 0;
}
@media (max-width: 650px) {
.author-list-item .author-card {
display: none;
}
}
.basic-info .author-profile-image {
margin: 0;
width: 88px;
height: 88px;
border: none;
}
.basic-info .avatar-wrapper {
position: relative;
margin: 0;
width: 88px;
height: 88px;
border: none;
background: rgba(229, 239, 245, 0.1);
}
.basic-info .avatar-wrapper svg {
margin: 0;
width: 88px;
height: 88px;
opacity: 0.15;
}
/* 7.3. Comments
/* ---------------------------------------------------------- */

View File

@ -1,10 +1,12 @@
/* global maxPages */
// Code snippet inspired by https://github.com/douglasrodrigues5/ghost-blog-infinite-scroll
$(function ($) {
var currentPage = 1;
var pathname = window.location.pathname;
var $document = $(document);
var $result = $('.post-feed');
var buffer = 100;
var buffer = 300;
var ticking = false;
var isLoading = false;
@ -13,9 +15,6 @@ $(function ($) {
var lastWindowHeight = window.innerHeight;
var lastDocumentHeight = $document.height();
// remove hash params from pathname
pathname = pathname.replace(/#(.*)$/g, '').replace('/\//g', '/');
function onScroll() {
lastScrollY = window.scrollY;
requestTick();
@ -29,12 +28,34 @@ $(function ($) {
function requestTick() {
if (!ticking) {
requestAnimationFrame(infiniteScroll)
requestAnimationFrame(infiniteScroll);
}
ticking = true;
}
function infiniteScroll () {
function sanitizePathname(path) {
var paginationRegex = /(?:page\/)(\d)(?:\/)$/i;
// remove hash params from path
path = path.replace(/#(.*)$/g, '').replace('////g', '/');
// remove pagination from the path and replace the current pages
// with the actual requested page. E. g. `/page/3/` indicates that
// the user actually requested page 3, so we should request page 4
// next, unless it's the last page already.
if (path.match(paginationRegex)) {
currentPage = parseInt(path.match(paginationRegex)[1]);
path = path.replace(paginationRegex, '');
}
return path;
}
function infiniteScroll() {
// sanitize the pathname from possible pagination or hash params
pathname = sanitizePathname(pathname);
// return if already loading
if (isLoading) {
return;
@ -46,29 +67,40 @@ $(function ($) {
return;
}
// return if currentPage is the last page already
if (currentPage === maxPages) {
/**
* maxPages is defined in default.hbs and is the value
* of the amount of pagination pages.
* If we reached the last page or are past it,
* we return and disable the listeners.
*/
if (currentPage >= maxPages) {
window.removeEventListener('scroll', onScroll, {passive: true});
window.removeEventListener('resize', onResize);
return;
}
isLoading = true;
// next page
currentPage++;
currentPage += 1;
// Load more
var nextPage = pathname + 'page/' + currentPage + '/';
$.get(nextPage, function (content) {
$result.append($(content).find('.post').hide().fadeIn(100));
var parse = document.createRange().createContextualFragment(content);
var posts = parse.querySelectorAll('.post');
if (posts.length) {
[].forEach.call(posts, function (post) {
$result[0].appendChild(post);
});
}
}).fail(function (xhr) {
// 404 indicates we've run out of pages
if (xhr.status === 404) {
window.removeEventListener('scroll', onScroll, {passive: true});
window.removeEventListener('resize', onResize);
}
}).always(function () {
lastDocumentHeight = $document.height();
isLoading = false;

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="{{lang}}">
<head>
{{!-- Document Settings --}}
@ -65,6 +65,9 @@
{{#if pagination.pages}}
<script>
// maxPages is a global variable that is needed to determine
// if we need to load more pages for the infinitescroll, or if
// we reached the last page already.
var maxPages = parseInt('{{pagination.pages}}');
</script>
<script src="{{asset "js/infinitescroll.js"}}"></script>

View File

@ -2,7 +2,7 @@
"name": "casper",
"description": "The default personal blogging theme for Ghost. Beautiful, minimal and responsive.",
"demo": "https://demo.ghost.io",
"version": "2.1.10",
"version": "2.2.0",
"engines": {
"ghost": ">=1.2.0"
},
@ -11,6 +11,10 @@
"desktop": "assets/screenshot-desktop.jpg",
"mobile": "assets/screenshot-mobile.jpg"
},
"scripts": {
"dev": "gulp",
"zip": "gulp zip"
},
"author": {
"name": "Ghost Foundation",
"email": "hello@ghost.org",

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M3.513 18.998C4.749 15.504 8.082 13 12 13s7.251 2.504 8.487 5.998C18.47 21.442 15.417 23 12 23s-6.47-1.558-8.487-4.002zM12 12c2.21 0 4-2.79 4-5s-1.79-4-4-4-4 1.79-4 4 1.79 5 4 5z" fill="#FFF"/></g></svg>

After

Width:  |  Height:  |  Size: 308 B

View File

@ -17,10 +17,26 @@
</section>
</a>
<footer class="post-card-meta">
{{#if author.profile_image}}
<img class="author-profile-image" src="{{author.profile_image}}" alt="{{author.name}}" />
{{/if}}
<span class="post-card-author">{{author}}</span>
<ul class="author-list">
{{#foreach authors}}
<li class="author-list-item">
<div class="author-name-tooltip">
{{name}}
</div>
{{#if profile_image}}
<span class="profile-image-wrapper"><img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" /></span>
{{else}}
<span class="avatar-wrapper">{{> "icons/avatar"}}</span>
{{/if}}
</li>
{{/foreach}}
</ul>
<span class="reading-time">{{reading_time}}</span>
</footer>
</div>
</article>

141
post.hbs
View File

@ -46,27 +46,81 @@ into the {body} of the default.hbs template --}}
{{/if}}
<footer class="post-full-footer">
{{!-- Everything inside the #author tags pulls data from the author --}}
{{#author}}
<section class="author-card">
{{#if profile_image}}
<img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" />
{{/if}}
<section class="author-card-content">
<h4 class="author-card-name"><a href="{{url}}">{{name}}</a></h4>
{{#if bio}}
<p>{{bio}}</p>
{{else}}
<p>Read <a href="{{url}}">more posts</a> by this author.</p>
{{/if}}
{{!-- If there are multiple authors for the post, display all of their avatars in a stack --}}
{{#has author="count:>1"}}
<section class="post-full-authors">
<div class="post-full-authors-content">
<p>This post was a collaboration between</p>
<p>{{authors}}</p>
</div>
<ul class="author-list">
{{#foreach authors}}
<li class="author-list-item">
<div class="author-card">
<div class="basic-info">
{{#if profile_image}}
<img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" />
{{else}}
<span class="avatar-wrapper">{{> "icons/avatar"}}</span>
{{/if}}
<h2>{{name}}</h2>
</div>
<div class="bio">
{{#if bio}}
<p>{{bio}}</p>
<p><a href="{{url}}">More posts</a> by {{name}}.</p>
{{else}}
<p>Read <a href="{{url}}">more posts</a> by this author.</p>
{{/if}}
</div>
</div>
{{#if profile_image}}
<a href="{{url}}" class="moving-avatar"><img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" /></a>
{{else}}
<a href="{{url}}" class="moving-avatar"><span class="avatar-wrapper">{{> "icons/avatar"}}</span></a>
{{/if}}
</li>
{{/foreach}}
</ul>
</section>
</section>
<div class="post-full-footer-right">
<a class="author-card-button" href="{{url}}">Read More</a>
</div>
{{/author}}
{{!-- If there is a single author for the post, display her/his information --}}
{{else}}
{{!-- Everything inside the #author tags pulls data from the author --}}
{{#author}}
<section class="author-card">
{{#if profile_image}}
<img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" />
{{else}}
<span class="avatar-wrapper">{{> "icons/avatar"}}</span>
{{/if}}
<section class="author-card-content">
<h4 class="author-card-name"><a href="{{url}}">{{name}}</a></h4>
{{#if bio}}
<p>{{bio}}</p>
{{else}}
<p>Read <a href="{{url}}">more posts</a> by this author.</p>
{{/if}}
</section>
</section>
<div class="post-full-footer-right">
<a class="author-card-button" href="{{url}}">Read More</a>
</div>
{{/author}}
{{/has}}
</footer>
{{!--
@ -216,6 +270,57 @@ $(document).ready(function () {
window.addEventListener('resize', onResize, false);
update();
});
// Adds delay to author card dropups to disappear. This gives enough
// time for the user to interact with the author card while they move
// the mouse from the avatar to the card. Also makes space for the
// interacted avatar.
$(document).ready(function () {
var hoverTimeout;
var direction = 'left';
$('.author-list-item:first-child').addClass('first-child');
function makeSpaceForAvatar(avatar) {
if (avatar.hasClass('first-child')) {
return;
}
$('.author-list-item').each(function(i, obj) {
if ($(this)[0] != avatar[0]) {
if (Number($(this).css('z-index')) > Number(avatar.css('z-index'))) {
$(this).children('.moving-avatar').addClass('left');
} else {
$(this).children('.moving-avatar').addClass('right');
}
}
});
}
$('.author-list-item').hover(function(){
var $this = $(this);
clearTimeout(hoverTimeout);
$('.author-card').removeClass('hovered');
$(this).children('.author-card').addClass('hovered');
makeSpaceForAvatar($this);
}, function() {
var $this = $(this);
$('.author-list-item').children('.moving-avatar').removeClass('left right');
hoverTimeout = setTimeout(function() {
$this.children('.author-card').removeClass('hovered');
}, 800);
});
});
</script>
{{/contentFor}}

4157
yarn.lock Normal file

File diff suppressed because it is too large Load Diff