Merge remote-tracking branch 'upstream/master'

This commit is contained in:
ViViDboarder 2019-08-20 18:45:30 -07:00
commit 0b466c85de
35 changed files with 2535 additions and 2794 deletions

4
.gitignore vendored
View File

@ -22,3 +22,7 @@ projectFilesBackup
.DS_Store .DS_Store
dist/ dist/
config.json
changelog.md
changelog.md.bk

View File

@ -1,6 +1,11 @@
dist: xenial
language: node_js language: node_js
sudo: false
node_js: node_js:
- "8" - 10
cache: yarn
# Don't run builds for renovate PRs
if: NOT head_branch =~ ^renovate

2
.yarnrc Normal file
View File

@ -0,0 +1,2 @@
version-tag-prefix ""
version-git-message "%s"

View File

@ -1,4 +1,4 @@
Copyright (c) 2013-2018 Ghost Foundation Copyright (c) 2013-2019 Ghost Foundation
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View File

@ -12,7 +12,7 @@ The default theme for [Ghost](http://github.com/tryghost/ghost/). This is the la
Ghost uses a simple templating language called [Handlebars](http://handlebarsjs.com/) for its themes. Ghost uses a simple templating language called [Handlebars](http://handlebarsjs.com/) for its themes.
We've documented our default theme pretty heavily so that it should be fairly easy to work out what's going on just by reading the code and the comments. Once you feel comfortable with how everything works, we also have full [theme API documentation](https://themes.ghost.org) which explains every possible Handlebars helper and template. We've documented our default theme pretty heavily so that it should be fairly easy to work out what's going on just by reading the code and the comments. Once you feel comfortable with how everything works, we also have full [theme API documentation](https://ghost.org/docs/api/handlebars-themes/) which explains every possible Handlebars helper and template.
**The main files are:** **The main files are:**
@ -63,4 +63,4 @@ You can add your own SVG icons in the same manner.
# Copyright & License # Copyright & License
Copyright (c) 2013-2018 Ghost Foundation - Released under the [MIT license](LICENSE). Copyright (c) 2013-2019 Ghost Foundation - Released under the [MIT license](LICENSE).

9
SECURITY.md Normal file
View File

@ -0,0 +1,9 @@
# Reporting Security Vulnerabilities
Potential security vulnerabilities can be reported directly us at `security@ghost.org`. The Ghost Security Team communicates privately and works in a secured, isolated repository for tracking, testing, and resolving security-related issues.
The full, up-to-date details of our security policy and procedure can always be found in our documentation:
https://ghost.org/docs/concepts/security/
Please refer to this before emailing us. Thanks for helping make Ghost safe for everyone 🙏.

View File

@ -1,2 +1,2 @@
a,abbr,acronym,address,applet,article,aside,audio,big,blockquote,body,canvas,caption,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,ul,var,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:"";content:none}img{max-width:100%}html{box-sizing:border-box;font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}*,:after,:before{box-sizing:inherit}a{background-color:transparent}a:active,a:hover{outline:0}b,strong{font-weight:700}dfn,em,i{font-style:italic}h1{margin:.67em 0;font-size:2em}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}mark{background-color:#fdffb6}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;color:inherit;font:inherit}button{overflow:visible;border:none}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input:focus{outline:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}legend{padding:0;border:0}textarea{overflow:auto}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}html{overflow-y:scroll;font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body,html{overflow-x:hidden}body{color:#3c484e;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-size:1.5rem;line-height:1.6em;font-weight:400;font-style:normal;letter-spacing:0;text-rendering:optimizeLegibility;background:#fff;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-moz-font-feature-settings:"liga" on}::-moz-selection{text-shadow:none;background:#cbeafb}::selection{text-shadow:none;background:#cbeafb}hr{position:relative;display:block;width:100%;margin:2.5em 0 3.5em;padding:0;height:1px;border:0;border-top:1px solid #e3e9ed}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{margin:0;padding:0;border:0}textarea{resize:vertical}blockquote,dl,ol,p,ul{margin:0 0 1.5em}ol,ul{padding-left:1.3em;padding-right:1.5em}ol ol,ol ul,ul ol,ul ul{margin:.5em 0 1em}ul{list-style:disc}ol{list-style:decimal}ol,ul{max-width:100%}li{margin:.5em 0;padding-left:.3em;line-height:1.6em}dt{float:left;margin:0 20px 0 0;width:120px;color:#15171a;font-weight:500;text-align:right}dd{margin:0 0 5px;text-align:left}blockquote{margin:1.5em 0;padding:0 1.6em;border-left:.5em solid #e5eff5}blockquote p{margin:.8em 0;font-size:1.2em;font-weight:300}blockquote small{display:inline-block;margin:.8em 0 .8em 1.5em;font-size:.9em;opacity:.8}blockquote small:before{content:"\2014 \00A0"}blockquote cite{font-weight:700}blockquote cite a{font-weight:400}a{color:#26a8ed;text-decoration:none}a:hover{text-decoration:underline}h1,h2,h3,h4,h5,h6{margin-top:0;line-height:1.15;font-weight:700;text-rendering:optimizeLegibility}h1{margin:0 0 .5em;font-size:5rem;font-weight:700}@media (max-width:500px){h1{font-size:2.2rem}}h2{margin:1.5em 0 .5em;font-size:2rem}@media (max-width:500px){h2{font-size:1.8rem}}h3{margin:1.5em 0 .5em;font-size:1.8rem;font-weight:500}@media (max-width:500px){h3{font-size:1.7rem}}h4{margin:1.5em 0 .5em;font-size:1.6rem;font-weight:500}h5,h6{margin:1.5em 0 .5em;font-size:1.4rem;font-weight:500} a,abbr,acronym,address,applet,article,aside,audio,big,blockquote,body,canvas,caption,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,ul,var,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:"";content:none}img{max-width:100%}html{-webkit-box-sizing:border-box;box-sizing:border-box;font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}*,:after,:before{-webkit-box-sizing:inherit;box-sizing:inherit}a{background-color:transparent}a:active,a:hover{outline:0}b,strong{font-weight:700}dfn,em,i{font-style:italic}h1{margin:.67em 0;font-size:2em}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}mark{background-color:#fdffb6}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;color:inherit;font:inherit}button{overflow:visible;border:none}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input:focus{outline:none}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}legend{padding:0;border:0}textarea{overflow:auto}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}html{overflow-y:scroll;font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body,html{overflow-x:hidden}body{color:#3c484e;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-size:1.5rem;line-height:1.6em;font-weight:400;font-style:normal;letter-spacing:0;text-rendering:optimizeLegibility;background:#fff;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-moz-font-feature-settings:"liga" on}::-moz-selection{text-shadow:none;background:#cbeafb}::selection{text-shadow:none;background:#cbeafb}hr{position:relative;display:block;width:100%;margin:2.5em 0 3.5em;padding:0;height:1px;border:0;border-top:1px solid #e3e9ed}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{margin:0;padding:0;border:0}textarea{resize:vertical}blockquote,dl,ol,p,ul{margin:0 0 1.5em}ol,ul{padding-left:1.3em;padding-right:1.5em}ol ol,ol ul,ul ol,ul ul{margin:.5em 0 1em}ul{list-style:disc}ol{list-style:decimal}ol,ul{max-width:100%}li{margin:.5em 0;padding-left:.3em;line-height:1.6em}dt{float:left;margin:0 20px 0 0;width:120px;color:#15171a;font-weight:500;text-align:right}dd{margin:0 0 5px;text-align:left}blockquote{margin:1.5em 0;padding:0 1.6em;border-left:.5em solid #e5eff5}blockquote p{margin:.8em 0;font-size:1.2em;font-weight:300}blockquote small{display:inline-block;margin:.8em 0 .8em 1.5em;font-size:.9em;opacity:.8}blockquote small:before{content:"\2014 \00A0"}blockquote cite{font-weight:700}blockquote cite a{font-weight:400}a{color:#26a8ed;text-decoration:none}a:hover{text-decoration:underline}h1,h2,h3,h4,h5,h6{margin-top:0;line-height:1.15;font-weight:700;text-rendering:optimizeLegibility}h1{margin:0 0 .5em;font-size:5rem;font-weight:700}@media (max-width:500px){h1{font-size:2.2rem}}h2{margin:1.5em 0 .5em;font-size:2rem}@media (max-width:500px){h2{font-size:1.8rem}}h3{margin:1.5em 0 .5em;font-size:1.8rem;font-weight:500}@media (max-width:500px){h3{font-size:1.7rem}}h4{margin:1.5em 0 .5em;font-size:1.6rem;font-weight:500}h5,h6{margin:1.5em 0 .5em;font-size:1.4rem;font-weight:500}
/*# sourceMappingURL=global.css.map */ /*# sourceMappingURL=global.css.map */

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
$(function(t){var o=1,r=window.location.pathname,a=t(document),s=t(".post-feed"),c=300,l=!1,w=!1,d=window.scrollY,v=window.innerHeight,u=a.height();function f(){d=window.scrollY,e()}function g(){v=window.innerHeight,u=a.height(),e()}function e(){l||requestAnimationFrame(n),l=!0}function n(){var e,n;if(n=/(?:page\/)(\d)(?:\/)$/i,(e=(e=r).replace(/#(.*)$/g,"").replace("////g","/")).match(n)&&(o=parseInt(e.match(n)[1]),e=e.replace(n,"")),r=e,!w)if(d+v<=u-c)l=!1;else{if(o>=maxPages)return window.removeEventListener("scroll",f,{passive:!0}),void window.removeEventListener("resize",g);w=!0;var i=r+"page/"+(o+=1)+"/";t.get(i,function(e){var n=document.createRange().createContextualFragment(e).querySelectorAll(".post");n.length&&[].forEach.call(n,function(e){s[0].appendChild(e)})}).fail(function(e){404===e.status&&(window.removeEventListener("scroll",f,{passive:!0}),window.removeEventListener("resize",g))}).always(function(){u=a.height(),l=w=!1})}}window.addEventListener("scroll",f,{passive:!0}),window.addEventListener("resize",g),n()}); !function(n,t){var r=t.querySelector("link[rel=next]");if(r){var i=t.querySelector(".post-feed");if(i){var o=300,s=!1,l=!1,c=n.scrollY,u=n.innerHeight,d=t.documentElement.scrollHeight;n.addEventListener("scroll",a,{passive:!0}),n.addEventListener("resize",m),f()}}function v(){if(404===this.status)return n.removeEventListener("scroll",a),void n.removeEventListener("resize",m);this.response.querySelectorAll(".post-card").forEach(function(e){i.appendChild(e)});var e=this.response.querySelector("link[rel=next]");e?r.href=e.href:(n.removeEventListener("scroll",a),n.removeEventListener("resize",m)),d=t.documentElement.scrollHeight,l=s=!1}function e(){if(!l)if(c+u<=d-o)s=!1;else{l=!0;var e=new n.XMLHttpRequest;e.responseType="document",e.addEventListener("load",v),e.open("GET",r.href),e.send(null)}}function f(){s||n.requestAnimationFrame(e),s=!0}function a(){c=n.scrollY,f()}function m(){u=n.innerHeight,d=t.documentElement.scrollHeight,f()}}(window,document);
//# sourceMappingURL=infinitescroll.js.map //# sourceMappingURL=infinitescroll.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
!function(d){"use strict";d.fn.fitVids=function(t){var i={customSelector:null,ignore:null};if(!document.getElementById("fit-vids-style")){var e=document.head||document.getElementsByTagName("head")[0],r=document.createElement("div");r.innerHTML='<p>x</p><style id="fit-vids-style">.fluid-width-video-container{flex-grow: 1;width:100%;}.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}</style>',e.appendChild(r.childNodes[1])}return t&&d.extend(i,t),this.each(function(){var t=['iframe[src*="player.vimeo.com"]','iframe[src*="youtube.com"]','iframe[src*="youtube-nocookie.com"]','iframe[src*="kickstarter.com"][src*="video.html"]',"object","embed"];i.customSelector&&t.push(i.customSelector);var r=".fitvidsignore";i.ignore&&(r=r+", "+i.ignore);var e=d(this).find(t.join(","));(e=(e=e.not("object object")).not(r)).each(function(){var t=d(this);if(!(0<t.parents(r).length||"embed"===this.tagName.toLowerCase()&&t.parent("object").length||t.parent(".fluid-width-video-wrapper").length)){t.css("height")||t.css("width")||!isNaN(t.attr("height"))&&!isNaN(t.attr("width"))||(t.attr("height",9),t.attr("width",16));var e=("object"===this.tagName.toLowerCase()||t.attr("height")&&!isNaN(parseInt(t.attr("height"),10))?parseInt(t.attr("height"),10):t.height())/(isNaN(parseInt(t.attr("width"),10))?t.width():parseInt(t.attr("width"),10));if(!t.attr("name")){var i="fitvid"+d.fn.fitVids._count;t.attr("name",i),d.fn.fitVids._count++}t.wrap('<div class="fluid-width-video-container"><div class="fluid-width-video-wrapper"></div></div>').parent(".fluid-width-video-wrapper").css("padding-top",100*e+"%"),t.removeAttr("height").removeAttr("width")}})})},d.fn.fitVids._count=0}(window.jQuery||window.Zepto); !function(d){"use strict";d.fn.fitVids=function(t){var i={customSelector:null,ignore:null};if(!document.getElementById("fit-vids-style")){var e=document.head||document.getElementsByTagName("head")[0],r=document.createElement("div");r.innerHTML='<p>x</p><style id="fit-vids-style">.fluid-width-video-container{flex-grow: 1;width:100%;}.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}</style>',e.appendChild(r.childNodes[1])}return t&&d.extend(i,t),this.each(function(){var t=['iframe[src*="player.vimeo.com"]','iframe[src*="youtube.com"]','iframe[src*="youtube-nocookie.com"]','iframe[src*="kickstarter.com"][src*="video.html"]',"object","embed"];i.customSelector&&t.push(i.customSelector);var r=".fitvidsignore";i.ignore&&(r=r+", "+i.ignore);var e=d(this).find(t.join(","));(e=(e=e.not("object object")).not(r)).each(function(){var t=d(this);if(!(0<t.parents(r).length||"embed"===this.tagName.toLowerCase()&&t.parent("object").length||t.parent(".fluid-width-video-wrapper").length)){t.css("height")||t.css("width")||!isNaN(t.attr("height"))&&!isNaN(t.attr("width"))||(t.attr("height",9),t.attr("width",16));var e=("object"===this.tagName.toLowerCase()||t.attr("height")&&!isNaN(parseInt(t.attr("height"),10))?parseInt(t.attr("height"),10):t.height())/(isNaN(parseInt(t.attr("width"),10))?t.width():parseInt(t.attr("width"),10));if(!t.attr("name")){var i="fitvid"+d.fn.fitVids._count;t.attr("name",i),d.fn.fitVids._count++}t.wrap('<div class="fluid-width-video-container"><div class="fluid-width-video-wrapper"></div></div>').parent(".fluid-width-video-wrapper").css("padding-top",100*e+"%"),t.removeAttr("height").removeAttr("width")}})})},d.fn.fitVids._count=0}(window.jQuery||window.Zepto);
//# sourceMappingURL=jquery.fitvids.js.map //# sourceMappingURL=jquery.fitvids.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -142,8 +142,8 @@ body {
background: linear-gradient(rgba(0,0,0,0.1),rgba(0,0,0,0)); background: linear-gradient(rgba(0,0,0,0.1),rgba(0,0,0,0));
} }
.site-header.no-cover:before, .site-header.no-image:before,
.site-header.no-cover:after { .site-header.no-image:after {
display: none; display: none;
} }
@ -411,10 +411,10 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
} }
.post-card-image { .post-card-image {
width: auto; width: 100%;
height: 200px; height: 200px;
background: var(--lightgrey) no-repeat center center; background: var(--lightgrey) no-repeat center center;
background-size: cover; object-fit: cover;
} }
.post-card-content-link { .post-card-content-link {
@ -595,46 +595,48 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
/* Special Styling for home page grid (below): /* Special Styling for home page grid (below):
The first (most recent) post in the list is styled to be bigger than the others and take over the full width of the grid to give it more emphasis. Wrapped in a media query to make sure this only happens on large viewports / desktop-ish devices. The first post in the list is styled to be bigger than the others and take over
the full width of the grid to give it more emphasis. Wrapped in a media query to
make sure this only happens on large viewports / desktop-ish devices.
*/ */
@media (min-width: 795px) { @media (min-width: 795px) {
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) { .post-card-large {
flex: 1 1 100%; flex: 1 1 100%;
flex-direction: row; flex-direction: row;
} }
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) .post-card-image-link { .post-card-large .post-card-image-link {
position: relative; position: relative;
flex: 1 1 auto; flex: 1 1 auto;
border-radius: 5px 0 0 5px; border-radius: 5px 0 0 5px;
} }
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) .post-card-image { .post-card-large .post-card-image {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) .post-card-content { .post-card-large .post-card-content {
flex: 0 1 357px; flex: 0 1 357px;
} }
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) h2 { .post-card-large h2 {
font-size: 2.6rem; font-size: 2.6rem;
} }
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) p { .post-card-large p {
font-size: 1.8rem; font-size: 1.8rem;
line-height: 1.55em; line-height: 1.55em;
} }
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) .post-card-content-link { .post-card-large .post-card-content-link {
padding: 30px 40px 0; padding: 30px 40px 0;
} }
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) .post-card-meta { .post-card-large .post-card-meta {
padding: 0 40px 30px; padding: 0 40px 30px;
} }
} }
@ -709,22 +711,29 @@ The first (most recent) post in the list is styled to be bigger than the others
.post-full-image { .post-full-image {
margin: 0 -10vw -165px; margin: 0 -10vw -165px;
height: 800px;
background: var(--lightgrey) center center; background: var(--lightgrey) center center;
background-size: cover;
border-radius: 5px; border-radius: 5px;
overflow: hidden;
}
.post-full-image img {
width: 100%;
height: 800px;
object-fit: cover;
} }
@media (max-width: 1170px) { @media (max-width: 1170px) {
.post-full-image { .post-full-image {
margin: 0 -4vw -100px; margin: 0 -4vw -100px;
height: 600px;
border-radius: 0; border-radius: 0;
} }
.post-full-image img {
height: 600px;
}
} }
@media (max-width: 800px) { @media (max-width: 800px) {
.post-full-image { .post-full-image img {
height: 400px; height: 400px;
} }
} }
@ -932,6 +941,10 @@ Usage (In Ghost editor):
border-radius: 5px; border-radius: 5px;
} }
.post-full-content pre ::selection {
color: color(var(--midgrey) l(-25%));
}
.post-full-content pre code { .post-full-content pre code {
padding: 0; padding: 0;
font-size: inherit; font-size: inherit;
@ -1878,7 +1891,7 @@ Usage (In Ghost editor):
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
min-width: 100%; width: 100%;
} }
.kg-embed-card .fluid-width-video-wrapper { .kg-embed-card .fluid-width-video-wrapper {
@ -1927,6 +1940,15 @@ Usage (In Ghost editor):
margin: -2.25em 0 3em; margin: -2.25em 0 3em;
} }
/* keep existing <pre> styles for code cards with captions */
.kg-code-card {
min-width: 100%;
}
.kg-code-card pre {
margin: 0;
}
/* 8. Author Template /* 8. Author Template
/* ---------------------------------------------------------- */ /* ---------------------------------------------------------- */

View File

@ -1,19 +1,78 @@
/* global maxPages */ /**
* Infinite Scroll
*/
(function(window, document) {
// next link element
var nextElement = document.querySelector('link[rel=next]');
if (!nextElement) return;
// post feed element
var feedElement = document.querySelector('.post-feed');
if (!feedElement) return;
// 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 = 300; var buffer = 300;
var ticking = false; var ticking = false;
var isLoading = false; var loading = false;
var lastScrollY = window.scrollY; var lastScrollY = window.scrollY;
var lastWindowHeight = window.innerHeight; var lastWindowHeight = window.innerHeight;
var lastDocumentHeight = $document.height(); var lastDocumentHeight = document.documentElement.scrollHeight;
function onPageLoad() {
if (this.status === 404) {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onResize);
return;
}
// append contents
var postElements = this.response.querySelectorAll('.post-card');
postElements.forEach(function (item) {
feedElement.appendChild(item);
});
// set next link
var resNextElement = this.response.querySelector('link[rel=next]');
if (resNextElement) {
nextElement.href = resNextElement.href;
} else {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onResize);
}
// sync status
lastDocumentHeight = document.documentElement.scrollHeight;
ticking = false;
loading = false;
}
function onUpdate() {
// return if already loading
if (loading) return;
// return if not scroll to the bottom
if (lastScrollY + lastWindowHeight <= lastDocumentHeight - buffer) {
ticking = false;
return;
}
loading = true;
var xhr = new window.XMLHttpRequest();
xhr.responseType = 'document';
xhr.addEventListener('load', onPageLoad);
xhr.open('GET', nextElement.href);
xhr.send(null);
}
function requestTick() {
ticking || window.requestAnimationFrame(onUpdate);
ticking = true;
}
function onScroll() { function onScroll() {
lastScrollY = window.scrollY; lastScrollY = window.scrollY;
@ -22,94 +81,12 @@ $(function ($) {
function onResize() { function onResize() {
lastWindowHeight = window.innerHeight; lastWindowHeight = window.innerHeight;
lastDocumentHeight = $document.height(); lastDocumentHeight = document.documentElement.scrollHeight;
requestTick(); requestTick();
} }
function requestTick() { window.addEventListener('scroll', onScroll, { passive: true });
if (!ticking) {
requestAnimationFrame(infiniteScroll);
}
ticking = true;
}
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;
}
// return if not scroll to the bottom
if (lastScrollY + lastWindowHeight <= lastDocumentHeight - buffer) {
ticking = false;
return;
}
/**
* 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 += 1;
// Load more
var nextPage = pathname + 'page/' + currentPage + '/';
$.get(nextPage, function (content) {
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;
ticking = false;
});
}
window.addEventListener('scroll', onScroll, {passive: true});
window.addEventListener('resize', onResize); window.addEventListener('resize', onResize);
infiniteScroll(); requestTick();
}); })(window, document);

View File

@ -3,7 +3,9 @@
{{#author}} {{#author}}
{{!-- Everything inside the #author tags pulls data from the author --}} {{!-- Everything inside the #author tags pulls data from the author --}}
<header class="site-header outer {{#if cover_image}}" style="background-image: url({{cover_image}}){{else}}no-cover{{/if}}">
{{> header background=cover_image}} {{!--Special header.hbs partial to generate the <header> tag--}}
<div class="inner"> <div class="inner">
{{> "site-nav"}} {{> "site-nav"}}
<div class="site-header-content"> <div class="site-header-content">

6
config.example.json Normal file
View File

@ -0,0 +1,6 @@
{
"github": {
"username": "<username>",
"token": "<gh-personal-access-token>"
}
}

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{lang}}"> <html lang="{{@site.lang}}">
<head> <head>
{{!-- Document Settings --}} {{!-- Document Settings --}}
@ -35,11 +35,11 @@
{{!-- The footer at the very bottom of the screen --}} {{!-- The footer at the very bottom of the screen --}}
<footer class="site-footer outer"> <footer class="site-footer outer">
<div class="site-footer-content inner"> <div class="site-footer-content inner">
<section class="copyright"><a href="{{@blog.url}}">{{@blog.title}}</a> &copy; {{date format="YYYY"}}</section> <section class="copyright"><a href="{{@site.url}}">{{@site.title}}</a> &copy; {{date format="YYYY"}}</section>
<nav class="site-footer-nav"> <nav class="site-footer-nav">
<a href="{{@blog.url}}">Latest Posts</a> <a href="{{@site.url}}">Latest Posts</a>
{{#if @blog.facebook}}<a href="{{facebook_url @blog.facebook}}" target="_blank" rel="noopener">Facebook</a>{{/if}} {{#if @site.facebook}}<a href="{{facebook_url @site.facebook}}" target="_blank" rel="noopener">Facebook</a>{{/if}}
{{#if @blog.twitter}}<a href="{{twitter_url @blog.twitter}}" target="_blank" rel="noopener">Twitter</a>{{/if}} {{#if @site.twitter}}<a href="{{twitter_url @site.twitter}}" target="_blank" rel="noopener">Twitter</a>{{/if}}
<a href="https://ghost.org" target="_blank" rel="noopener">Ghost</a> <a href="https://ghost.org" target="_blank" rel="noopener">Ghost</a>
</nav> </nav>
</div> </div>
@ -52,10 +52,10 @@
<div id="subscribe" class="subscribe-overlay"> <div id="subscribe" class="subscribe-overlay">
<a class="subscribe-overlay-close" href="#"></a> <a class="subscribe-overlay-close" href="#"></a>
<div class="subscribe-overlay-content"> <div class="subscribe-overlay-content">
{{#if @blog.logo}} {{#if @site.logo}}
<img class="subscribe-overlay-logo" src="{{@blog.logo}}" alt="{{@blog.title}}" /> <img class="subscribe-overlay-logo" src="{{@site.logo}}" alt="{{@site.title}}" />
{{/if}} {{/if}}
<h1 class="subscribe-overlay-title">Subscribe to {{@blog.title}}</h1> <h1 class="subscribe-overlay-title">Subscribe to {{@site.title}}</h1>
<p class="subscribe-overlay-description">Stay up to date! Get all the latest &amp; greatest posts delivered straight to your inbox</p> <p class="subscribe-overlay-description">Stay up to date! Get all the latest &amp; greatest posts delivered straight to your inbox</p>
{{subscribe_form placeholder="youremail@example.com"}} {{subscribe_form placeholder="youremail@example.com"}}
</div> </div>
@ -83,12 +83,6 @@
<script type="text/javascript" src="{{asset "built/jquery.fitvids.js"}}"></script> <script type="text/javascript" src="{{asset "built/jquery.fitvids.js"}}"></script>
{{#if pagination.pages}} {{#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 "built/infinitescroll.js"}}"></script> <script src="{{asset "built/infinitescroll.js"}}"></script>
{{/if}} {{/if}}

View File

@ -16,13 +16,13 @@ It's a good idea to keep this template as minimal as possible in terms of both f
<body class="error-template"> <body class="error-template">
<div class="site-wrapper"> <div class="site-wrapper">
<header class="site-header outer {{#if feature_image}}" style="background-image: url({{feature_image}}){{else}}no-cover{{/if}}"> <header class="site-header outer {{#if feature_image}}" style="background-image: url({{feature_image}}){{else}}no-image{{/if}}">
<div class="inner"> <div class="inner">
<nav class="site-nav-center"> <nav class="site-nav-center">
{{#if @blog.logo}} {{#if @site.logo}}
<a class="site-nav-logo" href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a> <a class="site-nav-logo" href="{{@site.url}}"><img src="{{img_url @site.logo size="xs"}}" alt="{{@site.title}}" /></a>
{{else}} {{else}}
<a class="site-nav-logo" href="{{@blog.url}}">{{@blog.title}}</a> <a class="site-nav-logo" href="{{@site.url}}">{{@site.title}}</a>
{{/if}} {{/if}}
</nav> </nav>
</div> </div>
@ -34,7 +34,7 @@ It's a good idea to keep this template as minimal as possible in terms of both f
<section class="error-message"> <section class="error-message">
<h1 class="error-code">{{code}}</h1> <h1 class="error-code">{{code}}</h1>
<p class="error-description">{{message}}</p> <p class="error-description">{{message}}</p>
<a class="error-link" href="{{@blog.url}}">Go to the front page →</a> <a class="error-link" href="{{@site.url}}">Go to the front page →</a>
</section> </section>
</div> </div>
</main> </main>

View File

@ -17,13 +17,13 @@ You'll notice that we *don't* use any JavsScript, or ghost_head / ghost_foot in
<body class="error-template"> <body class="error-template">
<div class="site-wrapper"> <div class="site-wrapper">
<header class="site-header outer {{#if feature_image}}" style="background-image: url({{feature_image}}){{else}}no-cover{{/if}}"> <header class="site-header outer {{#if feature_image}}" style="background-image: url({{feature_image}}){{else}}no-image{{/if}}">
<div class="inner"> <div class="inner">
<nav class="site-nav-center"> <nav class="site-nav-center">
{{#if @blog.logo}} {{#if @site.logo}}
<a class="site-nav-logo" href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a> <a class="site-nav-logo" href="{{@site.url}}"><img src="{{img_url @site.logo size="xs"}}" alt="{{@site.title}}" /></a>
{{else}} {{else}}
<a class="site-nav-logo" href="{{@blog.url}}">{{@blog.title}}</a> <a class="site-nav-logo" href="{{@site.url}}">{{@site.title}}</a>
{{/if}} {{/if}}
</nav> </nav>
</div> </div>
@ -35,23 +35,23 @@ You'll notice that we *don't* use any JavsScript, or ghost_head / ghost_foot in
<section class="error-message"> <section class="error-message">
<h1 class="error-code">{{code}}</h1> <h1 class="error-code">{{code}}</h1>
<p class="error-description">{{message}}</p> <p class="error-description">{{message}}</p>
<a class="error-link" href="{{@blog.url}}">Go to the front page →</a> <a class="error-link" href="{{@site.url}}">Go to the front page →</a>
</section> </section>
{{#if errorDetails}} {{#if errorDetails}}
<section class="error-stack"> <section class="error-stack">
<h3>Theme errors</h3> <h3>Theme errors</h3>
<ul class="error-stack-list"> <ul class="error-stack-list">
{{#each errorDetails}} {{#foreach errorDetails}}
<li> <li>
<em class="error-stack-function">{{{rule}}}</em> <em class="error-stack-function">{{{rule}}}</em>
{{#each failures}} {{#foreach failures}}
<p><span class="error-stack-file">Ref: {{ref}}</span></p> <p><span class="error-stack-file">Ref: {{ref}}</span></p>
<p><span class="error-stack-file">Message: {{message}}</span></p> <p><span class="error-stack-file">Message: {{message}}</span></p>
{{/each}} {{/foreach}}
</li> </li>
{{/each}} {{/foreach}}
</ul> </ul>
</section> </section>
{{/if}} {{/if}}

View File

@ -1,87 +1,207 @@
var gulp = require('gulp'); const {series, watch, src, dest, parallel} = require('gulp');
const pump = require('pump');
// gulp plugins and utils // gulp plugins and utils
var gutil = require('gulp-util'); const livereload = require('gulp-livereload');
var livereload = require('gulp-livereload'); const postcss = require('gulp-postcss');
var postcss = require('gulp-postcss'); const zip = require('gulp-zip');
var sourcemaps = require('gulp-sourcemaps'); const uglify = require('gulp-uglify');
var zip = require('gulp-zip'); const beeper = require('beeper');
var uglify = require('gulp-uglify');
var filter = require('gulp-filter');
// postcss plugins // postcss plugins
var autoprefixer = require('autoprefixer'); const autoprefixer = require('autoprefixer');
var colorFunction = require('postcss-color-function'); const colorFunction = require('postcss-color-function');
var cssnano = require('cssnano'); const cssnano = require('cssnano');
var customProperties = require('postcss-custom-properties'); const customProperties = require('postcss-custom-properties');
var easyimport = require('postcss-easy-import'); const easyimport = require('postcss-easy-import');
var swallowError = function swallowError(error) { function serve(done) {
gutil.log(error.toString()); livereload.listen();
gutil.beep(); done();
this.emit('end'); }
const handleError = (done) => {
return function (err) {
if (err) {
beeper();
}
return done(err);
};
}; };
var nodemonServerInit = function () { function hbs(done) {
livereload.listen(1234); pump([
}; src(['*.hbs', 'partials/**/*.hbs', '!node_modules/**/*.hbs']),
livereload()
], handleError(done));
}
gulp.task('build', ['css', 'js'], function (/* cb */) { function css(done) {
return nodemonServerInit(); const processors = [
});
gulp.task('generate', ['css', 'js']);
gulp.task('css', function () {
var processors = [
easyimport, easyimport,
customProperties, customProperties({preserve: false}),
colorFunction(), colorFunction(),
autoprefixer({browsers: ['last 2 versions']}), autoprefixer({browsers: ['last 2 versions']}),
cssnano() cssnano()
]; ];
return gulp.src('assets/css/*.css') pump([
.on('error', swallowError) src('assets/css/*.css', {sourcemaps: true}),
.pipe(sourcemaps.init()) postcss(processors),
.pipe(postcss(processors)) dest('assets/built/', {sourcemaps: '.'}),
.pipe(sourcemaps.write('.')) livereload()
.pipe(gulp.dest('assets/built/')) ], handleError(done));
.pipe(livereload()); }
});
gulp.task('js', function () { function js(done) {
var jsFilter = filter(['**/*.js'], {restore: true}); pump([
src('assets/js/*.js', {sourcemaps: true}),
uglify(),
dest('assets/built/', {sourcemaps: '.'}),
livereload()
], handleError(done));
}
return gulp.src('assets/js/*.js') function zipper(done) {
.on('error', swallowError) const targetDir = 'dist/';
.pipe(sourcemaps.init()) const themeName = require('./package.json').name;
.pipe(jsFilter) const filename = themeName + '.zip';
.pipe(uglify())
.pipe(jsFilter.restore)
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('assets/built/'))
.pipe(livereload());
});
gulp.task('watch', function () { pump([
gulp.watch('assets/css/**', ['css']); src([
}); '**',
'!node_modules', '!node_modules/**',
'!dist', '!dist/**'
]),
zip(filename),
dest(targetDir)
], handleError(done));
}
gulp.task('zip', ['css', 'js'], function () { const cssWatcher = () => watch('assets/css/**', css);
var targetDir = 'dist/'; const hbsWatcher = () => watch(['*.hbs', 'partials/**/*.hbs', '!node_modules/**/*.hbs'], hbs);
var themeName = require('./package.json').name; const watcher = parallel(cssWatcher, hbsWatcher);
var filename = themeName + '.zip'; const build = series(css, js);
const dev = series(build, serve, watcher);
return gulp.src([ exports.build = build;
'**', exports.zip = series(build, zipper);
'!node_modules', '!node_modules/**', exports.default = dev;
'!dist', '!dist/**'
])
.pipe(zip(filename))
.pipe(gulp.dest(targetDir));
});
gulp.task('default', ['build'], function () { // release imports
gulp.start('watch'); const path = require('path');
}); const releaseUtils = require('@tryghost/release-utils');
let config;
try {
config = require('./config');
} catch (err) {
config = null;
}
const REPO = 'TryGhost/Casper';
const USER_AGENT = 'Casper';
const CHANGELOG_PATH = path.join(process.cwd(), '.', 'changelog.md');
const changelog = ({previousVersion}) => {
const changelog = new releaseUtils.Changelog({
changelogPath: CHANGELOG_PATH,
folder: path.join(process.cwd(), '.')
});
changelog
.write({
githubRepoPath: `https://github.com/${REPO}`,
lastVersion: previousVersion
})
.sort()
.clean();
};
const previousRelease = () => {
return releaseUtils
.releases
.get({
userAgent: USER_AGENT,
uri: `https://api.github.com/repos/${REPO}/releases`
})
.then((response) => {
if (!response || !response.length) {
console.log('No releases found. Skipping');
return;
}
console.log(`Previous version ${response[0].name}`);
return response[0].name;
});
};
/**
*
* `yarn ship` will trigger `postship` task.
*
* [optional] For full automation
*
* `GHOST=2.10.1,2.10.0 yarn ship`
* First value: Ships with Ghost
* Second value: Compatible with Ghost/GScan
*
* You can manually run in case the task has thrown an error.
*
* `npm_package_version=0.5.0 gulp release`
*/
const release = () => {
// @NOTE: https://yarnpkg.com/lang/en/docs/cli/version/
const newVersion = process.env.npm_package_version;
let shipsWithGhost = '{version}';
let compatibleWithGhost = '2.10.0';
const ghostEnvValues = process.env.GHOST || null;
if (ghostEnvValues) {
shipsWithGhost = ghostEnvValues.split(',')[0];
compatibleWithGhost = ghostEnvValues.split(',')[1];
if (!compatibleWithGhost) {
compatibleWithGhost = '2.10.0';
}
}
if (!newVersion || newVersion === '') {
console.log('Invalid version.');
return;
}
console.log(`\nDraft release for ${newVersion}.`);
if (!config || !config.github || !config.github.username || !config.github.token) {
console.log('Please copy config.example.json and configure Github token.');
return;
}
return previousRelease()
.then((previousVersion) => {
changelog({previousVersion});
return releaseUtils
.releases
.create({
draft: true,
preRelease: false,
tagName: newVersion,
releaseName: newVersion,
userAgent: USER_AGENT,
uri: `https://api.github.com/repos/${REPO}/releases`,
github: {
username: config.github.username,
token: config.github.token
},
content: [`**Ships with Ghost ${shipsWithGhost} Compatible with Ghost >= ${compatibleWithGhost}**\n\n`],
changelogPath: CHANGELOG_PATH
})
.then((response) => {
console.log(`\nRelease draft generated: ${response.releaseUrl}\n`);
});
});
};
exports.release = release;

View File

@ -2,18 +2,17 @@
{{!-- The tag above means: insert everything in this file {{!-- The tag above means: insert everything in this file
into the {body} of the default.hbs template --}} into the {body} of the default.hbs template --}}
{{!-- The big featured header, it uses blog cover image as a BG if available --}} {{> header background=@site.cover_image}} {{!--Special header.hbs partial to generate the <header> tag--}}
<header class="site-header outer {{#if @blog.cover_image}}" style="background-image: url({{@blog.cover_image}}){{else}}no-cover{{/if}}">
<div class="inner"> <div class="inner">
<div class="site-header-content"> <div class="site-header-content">
<h1 class="site-title"> <h1 class="site-title">
{{#if @blog.logo}} {{#if @site.logo}}
<img class="site-logo" src="{{@blog.logo}}" alt="{{@blog.title}}" /> <img class="site-logo" src="{{img_url @site.logo size="l"}}" alt="{{@site.title}}" />
{{else}} {{else}}
{{@blog.title}} {{@site.title}}
{{/if}} {{/if}}
</h1> </h1>
<h2 class="site-description">{{@blog.description}}</h2> <h2 class="site-description">{{@site.description}}</h2>
</div> </div>
{{> "site-nav"}} {{> "site-nav"}}
</div> </div>

View File

@ -2,9 +2,10 @@
"name": "vivid-casper", "name": "vivid-casper",
"description": "Minor tweaks from the default personal blogging theme for Ghost. Beautiful, minimal and responsive.", "description": "Minor tweaks from the default personal blogging theme for Ghost. Beautiful, minimal and responsive.",
"demo": "https://demo.ghost.io", "demo": "https://demo.ghost.io",
"version": "2.7.0", "version": "2.10.6",
"engines": { "engines": {
"ghost": ">=2.0.0" "ghost": ">=2.0.0",
"ghost-api": "v2"
}, },
"license": "MIT", "license": "MIT",
"screenshots": { "screenshots": {
@ -14,7 +15,11 @@
"scripts": { "scripts": {
"dev": "gulp", "dev": "gulp",
"zip": "gulp zip", "zip": "gulp zip",
"test": "gscan ." "test": "gscan .",
"pretest": "gulp build",
"preship": "yarn test",
"ship": "STATUS=$(git status --porcelain); echo $STATUS; if [ -z \"$STATUS\" ]; then yarn version && git push --follow-tags; else echo \"Uncomitted changes found.\" && exit 1; fi",
"postship": "git fetch && gulp release"
}, },
"author": { "author": {
"name": "ViViDboarder", "name": "ViViDboarder",
@ -40,23 +45,42 @@
"bugs": "https://github.com/ViViDboarder/Casper/issues", "bugs": "https://github.com/ViViDboarder/Casper/issues",
"contributors": "https://github.com/ViViDboarder/Casper/graphs/contributors", "contributors": "https://github.com/ViViDboarder/Casper/graphs/contributors",
"devDependencies": { "devDependencies": {
"autoprefixer": "6.3.6", "@tryghost/release-utils": "0.3.2",
"cssnano": "3.7.1", "autoprefixer": "9.6.1",
"gscan": "^2.0.0", "beeper": "1.1.1",
"gulp": "3.9.1", "cssnano": "4.1.10",
"gulp-livereload": "3.8.1", "gscan": "2.7.0",
"gulp-postcss": "6.1.1", "gulp": "4.0.2",
"gulp-sourcemaps": "1.6.0", "gulp-livereload": "4.0.1",
"gulp-util": "3.0.7", "gulp-postcss": "8.0.0",
"gulp-watch": "4.3.8", "gulp-uglify": "3.0.2",
"gulp-zip": "4.0.0", "gulp-zip": "4.2.0",
"postcss-color-function": "2.0.1", "postcss-color-function": "4.1.0",
"postcss-custom-properties": "5.0.1", "postcss-custom-properties": "8.0.11",
"postcss-easy-import": "1.0.1", "postcss-easy-import": "3.0.0",
"gulp-filter": "5.1.0", "pump": "3.0.0"
"gulp-uglify": "3.0.1"
}, },
"config": { "config": {
"posts_per_page": 25 "posts_per_page": 25,
"image_sizes": {
"xxs": {
"width": 30
},
"xs": {
"width": 100
},
"s": {
"width": 300
},
"m": {
"width": 600
},
"l": {
"width": 1000
},
"xl": {
"width": 2000
}
}
} }
} }

View File

@ -22,7 +22,20 @@ into the {body} of the default.hbs template --}}
</header> </header>
{{#if feature_image}} {{#if feature_image}}
<figure class="post-full-image" style="background-image: url({{feature_image}})"> <figure class="post-full-image">
{{!-- This is a responsive image, it loads different sizes depending on device
https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433 --}}
<img
srcset="{{img_url feature_image size="s"}} 300w,
{{img_url feature_image size="m"}} 600w,
{{img_url feature_image size="l"}} 1000w,
{{img_url feature_image size="xl"}} 2000w"
sizes="(max-width: 800px) 400px,
(max-width: 1170px) 700px,
1400px"
src="{{img_url feature_image size="xl"}}"
alt="{{title}}"
/>
</figure> </figure>
{{/if}} {{/if}}

View File

@ -12,7 +12,7 @@
<div class="author-card"> <div class="author-card">
<div class="basic-info"> <div class="basic-info">
{{#if profile_image}} {{#if profile_image}}
<img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" /> <img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
{{else}} {{else}}
<div class="author-profile-image">{{> "icons/avatar"}}</div> <div class="author-profile-image">{{> "icons/avatar"}}</div>
{{/if}} {{/if}}
@ -29,7 +29,9 @@
</div> </div>
{{#if profile_image}} {{#if profile_image}}
<a href="{{url}}" class="moving-avatar"><img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" /></a> <a href="{{url}}" class="moving-avatar">
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
</a>
{{else}} {{else}}
<a href="{{url}}" class="moving-avatar author-profile-image">{{> "icons/avatar"}}</a> <a href="{{url}}" class="moving-avatar author-profile-image">{{> "icons/avatar"}}</a>
{{/if}} {{/if}}

View File

@ -3,7 +3,7 @@
<section class="author-card"> <section class="author-card">
{{#if profile_image}} {{#if profile_image}}
<img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" /> <img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
{{else}} {{else}}
<span class="avatar-wrapper">{{> "icons/avatar"}}</span> <span class="avatar-wrapper">{{> "icons/avatar"}}</span>
{{/if}} {{/if}}

View File

@ -1,10 +1,10 @@
<div class="floating-header"> <div class="floating-header">
<div class="floating-header-logo"> <div class="floating-header-logo">
<a href="{{@blog.url}}"> <a href="{{@site.url}}">
{{#if @blog.icon}} {{#if @site.icon}}
<img src="{{@blog.icon}}" alt="{{@blog.title}} icon" /> <img src="{{img_url @site.icon size="xxs"}}" alt="{{@site.title}} icon" />
{{/if}} {{/if}}
<span>{{@blog.title}}</span> <span>{{@site.title}}</span>
</a> </a>
</div> </div>
<span class="floating-header-divider">&mdash;</span> <span class="floating-header-divider">&mdash;</span>

53
partials/header.hbs Normal file
View File

@ -0,0 +1,53 @@
{{!--
Wow what the hell is going on in here even?
Ok so, several templates use this big header with a giant BG image. Nice idea, but big images
have a heavy impact on performance, so it's a good idea to make them responsive. Because we
can only get the image dynamically using Handlebars, and we can only set the image to properly
be a background image using CSS, we end up with a handful of inline styles.
If the template in question has a background image, then we render responsive image styles
for it, and apply those styles to the <header> tag. Else, we just output a <header> tag
with a `no-image` class so we can style it accordingly.
--}}
{{#if background}}
<style type="text/css">
.responsive-header-img {
background-image: url({{img_url background size='xl'}});
}
@media(max-width: 1000px) {
.responsive-header-img {
background-image: url({{img_url background size='l'}});
background-image: -webkit-image-set(
url({{img_url background size='l'}}) 1x,
url({{img_url background size='xl'}}) 2x
);
background-image: image-set(
url({{img_url background size='l'}}) 1x,
url({{img_url background size='xl'}}) 2x
);
}
}
@media(max-width: 600px) {
.responsive-header-img {
background-image: url({{img_url background size='m'}});
background-image: -webkit-image-set(
url({{img_url background size='m'}}) 1x,
url({{img_url background size='l'}}) 2x
);
background-image: image-set(
url({{img_url background size='m'}}) 1x,
url({{img_url background size='l'}}) 2x
);
}
}
</style>
<header class="site-header outer responsive-header-img">
{{else}}
<header class="site-header outer no-image">
{{/if}}

View File

@ -1,21 +1,38 @@
<article class="post-card {{post_class}}{{#unless feature_image}} no-image{{/unless}}"> <article class="post-card {{post_class}} {{#unless feature_image}}no-image{{else}}{{#is "home"}}{{#has index="nth:6"}}post-card-large{{/has}}{{/is}}{{/unless}}">
{{#if feature_image}} {{#if feature_image}}
<a class="post-card-image-link" href="{{url}}"> <a class="post-card-image-link" href="{{url}}">
<div class="post-card-image" style="background-image: url({{feature_image}})"></div> {{!-- This is a responsive image, it loads different sizes depending on device
</a> https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433 --}}
<img class="post-card-image"
srcset="{{img_url feature_image size="s"}} 300w,
{{img_url feature_image size="m"}} 600w,
{{img_url feature_image size="l"}} 1000w,
{{img_url feature_image size="xl"}} 2000w"
sizes="(max-width: 1000px) 400px, 700px"
src="{{img_url feature_image size="m"}}"
alt="{{title}}"
/>
</a>
{{/if}} {{/if}}
<div class="post-card-content"> <div class="post-card-content">
<a class="post-card-content-link" href="{{url}}"> <a class="post-card-content-link" href="{{url}}">
<header class="post-card-header"> <header class="post-card-header">
{{#if primary_tag}} {{#if primary_tag}}
<span class="post-card-tags">{{primary_tag.name}}</span> <span class="post-card-tags">{{primary_tag.name}}</span>
{{/if}} {{/if}}
<h2 class="post-card-title">{{title}}</h2> <h2 class="post-card-title">{{title}}</h2>
</header> </header>
<section class="post-card-excerpt"> <section class="post-card-excerpt">
<p>{{excerpt words="33"}}</p> <p>{{excerpt words="33"}}</p>
</section> </section>
</a> </a>
<footer class="post-card-meta"> <footer class="post-card-meta">
<ul class="author-list"> <ul class="author-list">
@ -27,7 +44,9 @@
</div> </div>
{{#if profile_image}} {{#if profile_image}}
<a href="{{url}}" class="static-avatar"><img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" /></a> <a href="{{url}}" class="static-avatar">
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
</a>
{{else}} {{else}}
<a href="{{url}}" class="static-avatar author-profile-image">{{> "icons/avatar"}}</a> <a href="{{url}}" class="static-avatar author-profile-image">{{> "icons/avatar"}}</a>
{{/if}} {{/if}}
@ -38,5 +57,7 @@
<span class="reading-time">{{reading_time}}</span> <span class="reading-time">{{reading_time}}</span>
</footer> </footer>
</div>
</div>{{!--/.post-card-content--}}
</article> </article>

View File

@ -1,29 +1,29 @@
<nav class="site-nav"> <nav class="site-nav">
<div class="site-nav-left"> <div class="site-nav-left">
{{^is "home"}} {{^is "home"}}
{{#if @blog.logo}} {{#if @site.logo}}
<a class="site-nav-logo" href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a> <a class="site-nav-logo" href="{{@site.url}}"><img src="{{@site.logo}}" alt="{{@site.title}}" /></a>
{{else}} {{else}}
<a class="site-nav-logo" href="{{@blog.url}}">{{@blog.title}}</a> <a class="site-nav-logo" href="{{@site.url}}">{{@site.title}}</a>
{{/if}} {{/if}}
{{/is}} {{/is}}
{{#if @blog.navigation}} {{#if @site.navigation}}
{{navigation}} {{navigation}}
{{/if}} {{/if}}
</div> </div>
<div class="site-nav-right"> <div class="site-nav-right">
<div class="social-links"> <div class="social-links">
{{#if @blog.facebook}} {{#if @site.facebook}}
<a class="social-link social-link-fb" href="{{facebook_url @blog.facebook}}" title="Facebook" target="_blank" rel="noopener">{{> "icons/facebook"}}</a> <a class="social-link social-link-fb" href="{{facebook_url @site.facebook}}" title="Facebook" target="_blank" rel="noopener">{{> "icons/facebook"}}</a>
{{/if}} {{/if}}
{{#if @blog.twitter}} {{#if @site.twitter}}
<a class="social-link social-link-tw" href="{{twitter_url @blog.twitter}}" title="Twitter" target="_blank" rel="noopener">{{> "icons/twitter"}}</a> <a class="social-link social-link-tw" href="{{twitter_url @site.twitter}}" title="Twitter" target="_blank" rel="noopener">{{> "icons/twitter"}}</a>
{{/if}} {{/if}}
</div> </div>
{{#if @labs.subscribers}} {{#if @labs.subscribers}}
<a class="subscribe-button" href="#subscribe">Subscribe</a> <a class="subscribe-button" href="#subscribe">Subscribe</a>
{{else}} {{else}}
<a class="rss-button" href="https://feedly.com/i/subscription/feed/{{@blog.url}}/rss/" title="RSS" target="_blank" rel="noopener">{{> "icons/rss"}}</a> <a class="rss-button" href="https://feedly.com/i/subscription/feed/{{@site.url}}/rss/" title="RSS" target="_blank" rel="noopener">{{> "icons/rss"}}</a>
{{/if}} {{/if}}
</div> </div>
</nav> </nav>

View File

@ -28,7 +28,20 @@ into the {body} of the default.hbs template --}}
</header> </header>
{{#if feature_image}} {{#if feature_image}}
<figure class="post-full-image" style="background-image: url({{feature_image}})"> <figure class="post-full-image">
{{!-- This is a responsive image, it loads different sizes depending on device
https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433 --}}
<img
srcset="{{img_url feature_image size="s"}} 300w,
{{img_url feature_image size="m"}} 600w,
{{img_url feature_image size="l"}} 1000w,
{{img_url feature_image size="xl"}} 2000w"
sizes="(max-width: 800px) 400px,
(max-width: 1170px) 700px,
1400px"
src="{{img_url feature_image size="xl"}}"
alt="{{title}}"
/>
</figure> </figure>
{{/if}} {{/if}}
@ -41,7 +54,7 @@ into the {body} of the default.hbs template --}}
{{!-- Email subscribe form at the bottom of the page --}} {{!-- Email subscribe form at the bottom of the page --}}
{{#if @labs.subscribers}} {{#if @labs.subscribers}}
<section class="subscribe-form"> <section class="subscribe-form">
<h3 class="subscribe-form-title">Subscribe to {{@blog.title}}</h3> <h3 class="subscribe-form-title">Subscribe to {{@site.title}}</h3>
<p>Get the latest posts delivered right to your inbox</p> <p>Get the latest posts delivered right to your inbox</p>
{{subscribe_form placeholder="youremail@example.com"}} {{subscribe_form placeholder="youremail@example.com"}}
</section> </section>
@ -82,14 +95,14 @@ into the {body} of the default.hbs template --}}
{{#if related_posts}} {{#if related_posts}}
<article class="read-next-card" <article class="read-next-card"
{{#if ../primary_tag.feature_image}} {{#if ../primary_tag.feature_image}}
style="background-image: url({{../primary_tag.feature_image}})" style="background-image: url({{img_url ../primary_tag.feature_image size="m"}})"
{{else}} {{else}}
{{#if @blog.cover_image}} {{#if @site.cover_image}}
style="background-image: url({{@blog.cover_image}})"{{/if}} style="background-image: url({{img_url @site.cover_image size="m"}})"{{/if}}
{{/if}} {{/if}}
> >
<header class="read-next-card-header"> <header class="read-next-card-header">
<small class="read-next-card-header-sitetitle">&mdash; {{@blog.title}} &mdash;</small> <small class="read-next-card-header-sitetitle">&mdash; {{@site.title}} &mdash;</small>
{{#../primary_tag}} {{#../primary_tag}}
<h3 class="read-next-card-header-title"><a href="{{url}}">{{name}}</a></h3> <h3 class="read-next-card-header-title"><a href="{{url}}">{{name}}</a></h3>
{{/../primary_tag}} {{/../primary_tag}}

9
renovate.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": [
"@tryghost:theme"
],
"travis": { "enabled": true },
"node": {
"supportPolicy": ["lts_latest"]
}
}

View File

@ -1,9 +1,8 @@
{{!< default}} {{!< default}}
{{!-- The tag above means - insert everything in this file into the {body} of the default.hbs template --}} {{!-- The tag above means - insert everything in this file into the {body} of the default.hbs template --}}
{{!-- The big featured header, it uses blog cover image as a BG if available --}}
{{#tag}} {{#tag}}
<header class="site-header outer {{#if feature_image}}" style="background-image: url({{feature_image}}){{else}}no-cover{{/if}}"> {{> header background=feature_image}} {{!--Special header.hbs partial to generate the <header> tag--}}
<div class="inner"> <div class="inner">
{{> "site-nav"}} {{> "site-nav"}}
<div class="site-header-content"> <div class="site-header-content">

4493
yarn.lock

File diff suppressed because it is too large Load Diff