mirror of
https://github.com/GenZmeY/casper-i18n.git
synced 2025-07-12 16:56:06 +00:00
Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
475c015fa2 | |||
79ebbd50a3 | |||
a22dda9694 | |||
3c2347c7f9 | |||
f695d69aeb | |||
d53b25bf5d | |||
bef56a2294 | |||
5ad6e1ed1f | |||
36971db512 | |||
bfcab7172f | |||
fce92d6cf1 | |||
d48178fde9 | |||
6f4fe840b3 | |||
8a9efb5ef1 | |||
04854967a8 | |||
fa18115aa6 | |||
ae192a9fff | |||
78993720fd | |||
676e17bd13 | |||
67e595741c | |||
7a88c88ea2 | |||
a0e42e0599 | |||
dc3bb316c7 | |||
7380ed0291 | |||
ecaf3d921f | |||
89ea6c5872 | |||
9d803067a4 | |||
0ea375643b | |||
f31957bd0d | |||
36841d4624 | |||
d41fcbbc8a | |||
5d318225f5 | |||
9b56779ca6 | |||
1bf2cdb52a | |||
f67caaba5c | |||
25d1e368d6 | |||
5c6cce1519 | |||
019842155a | |||
1860a8305b | |||
6417b4cf2a | |||
1c61bc5f16 | |||
7a7f04c716 | |||
fd56128f9b | |||
cd3b470c18 | |||
f3b44a7411 | |||
b83917060f | |||
4aaedf69d2 | |||
03ac0d91b8 | |||
b55d5e2b32 | |||
4fe2eac739 | |||
6e3399366d | |||
98e9c12018 |
6
.travis.yml
Normal file
6
.travis.yml
Normal file
@ -0,0 +1,6 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "8"
|
||||
|
||||
|
@ -32,7 +32,7 @@ One really neat trick is that you can also create custom one-off templates just
|
||||
|
||||
# Development
|
||||
|
||||
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:
|
||||
Casper styles are compiled using Gulp/PostCSS to polyfill future CSS spec. You'll need [Node](https://nodejs.org/), [Yarn](https://yarnpkg.com/) and [Gulp](https://gulpjs.com) installed globally. After that, from the theme's root directory:
|
||||
|
||||
```bash
|
||||
$ yarn install
|
||||
|
2
assets/built/infinitescroll.js
Normal file
2
assets/built/infinitescroll.js
Normal file
@ -0,0 +1,2 @@
|
||||
!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
|
1
assets/built/infinitescroll.js.map
Normal file
1
assets/built/infinitescroll.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["infinitescroll.js"],"names":["window","document","nextElement","querySelector","feedElement","buffer","ticking","loading","lastScrollY","scrollY","lastWindowHeight","innerHeight","lastDocumentHeight","documentElement","scrollHeight","addEventListener","onScroll","passive","onResize","requestTick","onPageLoad","this","status","removeEventListener","response","querySelectorAll","forEach","item","appendChild","resNextElement","href","onUpdate","xhr","XMLHttpRequest","responseType","open","send","requestAnimationFrame"],"mappings":"CAIA,SAAUA,EAAQC,GAEd,IAAIC,EAAcD,EAASE,cAAc,kBACzC,GAAKD,EAAL,CAGA,IAAIE,EAAcH,EAASE,cAAc,cACzC,GAAKC,EAAL,CAEA,IAAIC,EAAS,IAETC,GAAU,EACVC,GAAU,EAEVC,EAAcR,EAAOS,QACrBC,EAAmBV,EAAOW,YAC1BC,EAAqBX,EAASY,gBAAgBC,aAmElDd,EAAOe,iBAAiB,SAAUC,EAAU,CAAEC,SAAS,IACvDjB,EAAOe,iBAAiB,SAAUG,GAElCC,KApEA,SAASC,IACL,GAAoB,MAAhBC,KAAKC,OAGL,OAFAtB,EAAOuB,oBAAoB,SAAUP,QACrChB,EAAOuB,oBAAoB,SAAUL,GAKtBG,KAAKG,SAASC,iBAAiB,cACrCC,QAAQ,SAAUC,GAC3BvB,EAAYwB,YAAYD,KAI5B,IAAIE,EAAiBR,KAAKG,SAASrB,cAAc,kBAC7C0B,EACA3B,EAAY4B,KAAOD,EAAeC,MAElC9B,EAAOuB,oBAAoB,SAAUP,GACrChB,EAAOuB,oBAAoB,SAAUL,IAIzCN,EAAqBX,EAASY,gBAAgBC,aAE9CP,EADAD,GAAU,EAId,SAASyB,IAEL,IAAIxB,EAGJ,GAAIC,EAAcE,GAAoBE,EAAqBP,EACvDC,GAAU,MADd,CAKAC,GAAU,EAEV,IAAIyB,EAAM,IAAIhC,EAAOiC,eACrBD,EAAIE,aAAe,WAEnBF,EAAIjB,iBAAiB,OAAQK,GAE7BY,EAAIG,KAAK,MAAOjC,EAAY4B,MAC5BE,EAAII,KAAK,OAGb,SAASjB,IACLb,GAAWN,EAAOqC,sBAAsBN,GACxCzB,GAAU,EAGd,SAASU,IACLR,EAAcR,EAAOS,QACrBU,IAGJ,SAASD,IACLR,EAAmBV,EAAOW,YAC1BC,EAAqBX,EAASY,gBAAgBC,aAC9CK,KAhFR,CAuFGnB,OAAQC","file":"infinitescroll.js","sourcesContent":["/**\n * Infinite Scroll\n */\n\n(function(window, document) {\n // next link element\n var nextElement = document.querySelector('link[rel=next]');\n if (!nextElement) return;\n\n // post feed element\n var feedElement = document.querySelector('.post-feed');\n if (!feedElement) return;\n\n var buffer = 300;\n\n var ticking = false;\n var loading = false;\n\n var lastScrollY = window.scrollY;\n var lastWindowHeight = window.innerHeight;\n var lastDocumentHeight = document.documentElement.scrollHeight;\n\n function onPageLoad() {\n if (this.status === 404) {\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('resize', onResize);\n return;\n }\n\n // append contents\n var postElements = this.response.querySelectorAll('.post-card');\n postElements.forEach(function (item) {\n feedElement.appendChild(item);\n });\n\n // set next link\n var resNextElement = this.response.querySelector('link[rel=next]');\n if (resNextElement) {\n nextElement.href = resNextElement.href;\n } else {\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('resize', onResize);\n }\n\n // sync status\n lastDocumentHeight = document.documentElement.scrollHeight;\n ticking = false;\n loading = false;\n }\n\n function onUpdate() {\n // return if already loading\n if (loading) return;\n\n // return if not scroll to the bottom\n if (lastScrollY + lastWindowHeight <= lastDocumentHeight - buffer) {\n ticking = false;\n return;\n }\n\n loading = true;\n\n var xhr = new window.XMLHttpRequest();\n xhr.responseType = 'document';\n\n xhr.addEventListener('load', onPageLoad);\n\n xhr.open('GET', nextElement.href);\n xhr.send(null);\n }\n\n function requestTick() {\n ticking || window.requestAnimationFrame(onUpdate);\n ticking = true;\n }\n\n function onScroll() {\n lastScrollY = window.scrollY;\n requestTick();\n }\n\n function onResize() {\n lastWindowHeight = window.innerHeight;\n lastDocumentHeight = document.documentElement.scrollHeight;\n requestTick();\n }\n\n window.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('resize', onResize);\n\n requestTick();\n})(window, document);\n"],"sourceRoot":"/source/"}
|
2
assets/built/jquery.fitvids.js
Normal file
2
assets/built/jquery.fitvids.js
Normal file
@ -0,0 +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);
|
||||
//# sourceMappingURL=jquery.fitvids.js.map
|
1
assets/built/jquery.fitvids.js.map
Normal file
1
assets/built/jquery.fitvids.js.map
Normal file
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
@ -13,9 +13,12 @@ production stylesheet in assets/built/screen.css
|
||||
7. Single Post
|
||||
7.1. Subscribe Form
|
||||
7.2. Post Footer
|
||||
7.2.1 Single Author Byline
|
||||
7.2.2 Multiple Author Byline
|
||||
7.3. Comments
|
||||
7.4. Related Posts
|
||||
7.5. Floating Header
|
||||
7.6. Koenig Styles
|
||||
8. Author Template
|
||||
9. Error Template
|
||||
10. Subscribe Overlay
|
||||
@ -408,10 +411,10 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
|
||||
}
|
||||
|
||||
.post-card-image {
|
||||
width: auto;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: var(--lightgrey) no-repeat center center;
|
||||
background-size: cover;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.post-card-content-link {
|
||||
@ -455,38 +458,16 @@ 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;
|
||||
align-items: flex-end;
|
||||
padding: 0 25px 25px;
|
||||
}
|
||||
|
||||
.profile-image-wrapper,
|
||||
.author-profile-image,
|
||||
.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: 28px;
|
||||
height: 28px;
|
||||
border: 2px solid #fff;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: color(var(--lightgrey) l(+10%));
|
||||
border-radius: 100%;
|
||||
|
||||
object-fit: cover;
|
||||
@ -499,75 +480,81 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
|
||||
|
||||
.author-list {
|
||||
display: flex;
|
||||
margin-bottom: 0;
|
||||
flex-wrap: wrap-reverse;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-width: calc(100% - 120px);
|
||||
list-style-type: none;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.author-list-item {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
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);
|
||||
.author-list-item:nth-child(1) {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.moving-avatar.right {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
|
||||
.moving-avatar.left {
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 1 ) {
|
||||
.author-list-item:nth-child(2) {
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 2 ) {
|
||||
.author-list-item:nth-child(3) {
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 3 ) {
|
||||
.author-list-item:nth-child(4) {
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 4 ) {
|
||||
.author-list-item:nth-child(5) {
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 5 ) {
|
||||
.author-list-item:nth-child(6) {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 6 ) {
|
||||
.author-list-item:nth-child(7) {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 7 ) {
|
||||
.author-list-item:nth-child(8) {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 8 ) {
|
||||
.author-list-item:nth-child(9) {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.author-list-item:nth-child( 9 ) {
|
||||
.author-list-item:nth-child(10) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.z-999 {
|
||||
z-index: 999;
|
||||
.static-avatar {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 0 -5px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: #fff 2px solid;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.moving-avatar {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 0 -6px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: #fff 2px solid;
|
||||
border-radius: 100%;
|
||||
transition: all 0.5s cubic-bezier(0.4, 0.01, 0.165, 0.99) 0.7s;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
.author-list:hover .moving-avatar {
|
||||
margin: 0;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0.01, 0.165, 0.99);
|
||||
}
|
||||
}
|
||||
|
||||
.author-name-tooltip {
|
||||
position: absolute;
|
||||
bottom: 36px;
|
||||
bottom: 105%;
|
||||
z-index: 999;
|
||||
display: block;
|
||||
padding: 2px 8px;
|
||||
@ -596,8 +583,11 @@ The knock-on effect of this is ugly browser-scroll bars at the bottom, so 80px o
|
||||
}
|
||||
|
||||
.reading-time {
|
||||
flex-shrink: 0;
|
||||
margin-left: 20px;
|
||||
color: var(--midgrey);
|
||||
font-size: 1.2rem;
|
||||
line-height: 33px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
@ -605,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):
|
||||
|
||||
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) {
|
||||
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) {
|
||||
.post-card-large {
|
||||
flex: 1 1 100%;
|
||||
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;
|
||||
flex: 1 1 auto;
|
||||
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;
|
||||
width: 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;
|
||||
}
|
||||
|
||||
.home-template .post-feed .post-card:nth-child(6n+1):not(.no-image) h2 {
|
||||
.post-card-large h2 {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
@ -719,22 +711,29 @@ The first (most recent) post in the list is styled to be bigger than the others
|
||||
|
||||
.post-full-image {
|
||||
margin: 0 -10vw -165px;
|
||||
height: 800px;
|
||||
background: var(--lightgrey) center center;
|
||||
background-size: cover;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.post-full-image img {
|
||||
width: 100%;
|
||||
height: 800px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
@media (max-width: 1170px) {
|
||||
.post-full-image {
|
||||
margin: 0 -4vw -100px;
|
||||
height: 600px;
|
||||
border-radius: 0;
|
||||
}
|
||||
.post-full-image img {
|
||||
height: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.post-full-image {
|
||||
.post-full-image img {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
@ -798,13 +797,6 @@ The first (most recent) post in the list is styled to be bigger than the others
|
||||
display: none;
|
||||
}
|
||||
|
||||
.kg-card-markdown {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 920px;
|
||||
}
|
||||
|
||||
.post-full-content h1,
|
||||
.post-full-content h2,
|
||||
.post-full-content h3,
|
||||
@ -830,13 +822,9 @@ The first (most recent) post in the list is styled to be bigger than the others
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.post-template .kg-card-markdown > p:first-child {
|
||||
font-size: 1.25em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.post-full-content a {
|
||||
color: #000;
|
||||
word-break: break-word;
|
||||
box-shadow: var(--blue) 0 -1px 0 inset;
|
||||
}
|
||||
|
||||
@ -864,6 +852,7 @@ The first (most recent) post in the list is styled to be bigger than the others
|
||||
display: block;
|
||||
margin: 1.5em auto;
|
||||
max-width: 1040px;
|
||||
height: auto;
|
||||
}
|
||||
@media (max-width: 1040px) {
|
||||
.post-full-content img,
|
||||
@ -899,11 +888,13 @@ Usage (In Ghost editor):
|
||||
display: block;
|
||||
margin-top: -3em;
|
||||
margin-bottom: 1.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/* Override third party iframe styles */
|
||||
.post-full-content iframe {
|
||||
margin: 0 auto;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.post-full-content blockquote {
|
||||
@ -933,6 +924,10 @@ Usage (In Ghost editor):
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.post-full-content p code {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.post-full-content pre {
|
||||
overflow-x: auto;
|
||||
margin: 1.5em 0 3em;
|
||||
@ -953,7 +948,7 @@ Usage (In Ghost editor):
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.post-full-content pre code * {
|
||||
.post-full-content pre code :not(span) {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@ -1289,13 +1284,22 @@ Usage (In Ghost editor):
|
||||
max-width: 840px;
|
||||
}
|
||||
|
||||
/* 7.2.1 Single Author Byline
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
.author-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.author-card .author-profile-image,
|
||||
.author-card .avatar-wrapper {
|
||||
margin-right: 15px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.author-card-name {
|
||||
margin: 0 0 2px 0;
|
||||
margin: 8px 0 2px 0;
|
||||
padding: 0;
|
||||
font-size: 2rem;
|
||||
}
|
||||
@ -1338,6 +1342,9 @@ Usage (In Ghost editor):
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* 7.2.2 Multiple Author Byline
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
.post-full-authors {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
@ -1369,46 +1376,24 @@ Usage (In Ghost editor):
|
||||
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;
|
||||
.post-full-footer .author-list {
|
||||
justify-content: center;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.author-card .author-profile-image,
|
||||
.author-card .avatar-wrapper {
|
||||
margin-right: 15px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.post-full-footer .avatar-wrapper svg {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.author-list-item .author-card {
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
left: calc(-160px + 28px);
|
||||
z-index: 999;
|
||||
bottom: 130%;
|
||||
left: 50%;
|
||||
z-index: 300;
|
||||
display: block;
|
||||
margin-left: -160px;
|
||||
width: 320px;
|
||||
font-size: 1.4rem;
|
||||
letter-spacing: 0.2px;
|
||||
@ -1417,7 +1402,7 @@ Usage (In Ghost editor):
|
||||
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);
|
||||
transform: scale(0.98) translateY(15px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -1425,8 +1410,9 @@ Usage (In Ghost editor):
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: calc(160px - 20px + 6px);
|
||||
left: 50%;
|
||||
display: block;
|
||||
margin-left: -12px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 12px solid #fff;
|
||||
@ -1846,6 +1832,110 @@ Usage (In Ghost editor):
|
||||
}
|
||||
|
||||
|
||||
/* 7.6. Koenig Styles
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
.post-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 920px;
|
||||
}
|
||||
|
||||
.post-template .post-content > p:first-child {
|
||||
font-size: 1.25em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.post-full-content .kg-image {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Preventing full-width image overlap with post image. */
|
||||
.post-full-image + .post-full-content .kg-content *:first-child .kg-image {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.post-full-content .kg-width-wide .kg-image {
|
||||
max-width: 1040px;
|
||||
}
|
||||
|
||||
.post-full-content .kg-width-full .kg-image {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.post-full-content figure {
|
||||
margin: 1.5em 0 3em;
|
||||
}
|
||||
|
||||
.post-full-content figure img {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.post-full-content figcaption {
|
||||
margin: 1.0em 0 0;
|
||||
font-size: 80%;
|
||||
line-height: 1.6em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.kg-width-full figcaption {
|
||||
padding: 0 1.5em;
|
||||
}
|
||||
|
||||
.kg-embed-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.kg-embed-card .fluid-width-video-wrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 1040px) {
|
||||
.post-full-content .kg-width-full .kg-image {
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
.kg-gallery-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1040px;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.kg-gallery-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.kg-gallery-image img {
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.kg-gallery-row:not(:first-of-type) {
|
||||
margin: 0.75em 0 0 0;
|
||||
}
|
||||
|
||||
.kg-gallery-image:not(:first-of-type) {
|
||||
margin: 0 0 0 0.75em;
|
||||
}
|
||||
|
||||
.kg-gallery-card + .kg-image-card.kg-width-wide,
|
||||
.kg-gallery-card + .kg-gallery-card,
|
||||
.kg-image-card.kg-width-wide + .kg-gallery-card,
|
||||
.kg-image-card.kg-width-wide + .kg-image-card.kg-width-wide {
|
||||
margin: -2.25em 0 3em;
|
||||
}
|
||||
|
||||
/* 8. Author Template
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
|
@ -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 ticking = false;
|
||||
var isLoading = false;
|
||||
var loading = false;
|
||||
|
||||
var lastScrollY = window.scrollY;
|
||||
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() {
|
||||
lastScrollY = window.scrollY;
|
||||
@ -22,94 +81,12 @@ $(function ($) {
|
||||
|
||||
function onResize() {
|
||||
lastWindowHeight = window.innerHeight;
|
||||
lastDocumentHeight = $document.height();
|
||||
lastDocumentHeight = document.documentElement.scrollHeight;
|
||||
requestTick();
|
||||
}
|
||||
|
||||
function requestTick() {
|
||||
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('scroll', onScroll, { passive: true });
|
||||
window.addEventListener('resize', onResize);
|
||||
|
||||
infiniteScroll();
|
||||
});
|
||||
requestTick();
|
||||
})(window, document);
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
{{#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=feature_image}} {{!--Special header.hbs partial to generate the <header> tag--}}
|
||||
<div class="inner">
|
||||
{{> "site-nav"}}
|
||||
<div class="site-header-content">
|
||||
@ -38,7 +40,7 @@
|
||||
{{/author}}
|
||||
|
||||
{{!-- The main content area --}}
|
||||
<main id="site-main" class="site-main outer" role="main">
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
|
||||
<div class="post-feed">
|
||||
|
22
default.hbs
22
default.hbs
@ -55,22 +55,28 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<script>
|
||||
var images = document.querySelectorAll('.kg-gallery-image img');
|
||||
images.forEach(function (image) {
|
||||
var container = image.closest('.kg-gallery-image');
|
||||
var width = image.attributes.width.value;
|
||||
var height = image.attributes.height.value;
|
||||
var ratio = width / height;
|
||||
container.style.flex = ratio + ' 1 0%';
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
{{!-- jQuery + Fitvids, which makes all video embeds responsive --}}
|
||||
<script
|
||||
src="https://code.jquery.com/jquery-3.2.1.min.js"
|
||||
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
<script type="text/javascript" src="{{asset "js/jquery.fitvids.js"}}"></script>
|
||||
<script type="text/javascript" src="{{asset "built/jquery.fitvids.js"}}"></script>
|
||||
|
||||
{{#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>
|
||||
<script src="{{asset "built/infinitescroll.js"}}"></script>
|
||||
{{/if}}
|
||||
|
||||
{{!-- The #block helper will pull in data from the #contentFor other template files. In this case, there's some JavaScript which we only want to use in post.hbs, but it needs to be included down here, after jQuery has already loaded. --}}
|
||||
|
@ -20,7 +20,7 @@ It's a good idea to keep this template as minimal as possible in terms of both f
|
||||
<div class="inner">
|
||||
<nav class="site-nav-center">
|
||||
{{#if @blog.logo}}
|
||||
<a class="site-nav-logo" href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a>
|
||||
<a class="site-nav-logo" href="{{@blog.url}}"><img src="{{img_url @blog.logo size="xs"}}" alt="{{@blog.title}}" /></a>
|
||||
{{else}}
|
||||
<a class="site-nav-logo" href="{{@blog.url}}">{{@blog.title}}</a>
|
||||
{{/if}}
|
||||
@ -28,7 +28,7 @@ It's a good idea to keep this template as minimal as possible in terms of both f
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="site-main" class="site-main outer" role="main">
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
|
||||
<section class="error-message">
|
||||
|
@ -21,7 +21,7 @@ You'll notice that we *don't* use any JavsScript, or ghost_head / ghost_foot in
|
||||
<div class="inner">
|
||||
<nav class="site-nav-center">
|
||||
{{#if @blog.logo}}
|
||||
<a class="site-nav-logo" href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a>
|
||||
<a class="site-nav-logo" href="{{@blog.url}}"><img src="{{img_url @blog.logo size="xs"}}" alt="{{@blog.title}}" /></a>
|
||||
{{else}}
|
||||
<a class="site-nav-logo" href="{{@blog.url}}">{{@blog.title}}</a>
|
||||
{{/if}}
|
||||
@ -29,7 +29,7 @@ You'll notice that we *don't* use any JavsScript, or ghost_head / ghost_foot in
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="site-main" class="site-main outer" role="main">
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
|
||||
<section class="error-message">
|
||||
|
22
gulpfile.js
22
gulpfile.js
@ -6,6 +6,8 @@ var livereload = require('gulp-livereload');
|
||||
var postcss = require('gulp-postcss');
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var zip = require('gulp-zip');
|
||||
var uglify = require('gulp-uglify');
|
||||
var filter = require('gulp-filter');
|
||||
|
||||
// postcss plugins
|
||||
var autoprefixer = require('autoprefixer');
|
||||
@ -24,10 +26,12 @@ var nodemonServerInit = function () {
|
||||
livereload.listen(1234);
|
||||
};
|
||||
|
||||
gulp.task('build', ['css'], function (/* cb */) {
|
||||
gulp.task('build', ['css', 'js'], function (/* cb */) {
|
||||
return nodemonServerInit();
|
||||
});
|
||||
|
||||
gulp.task('generate', ['css', 'js']);
|
||||
|
||||
gulp.task('css', function () {
|
||||
var processors = [
|
||||
easyimport,
|
||||
@ -46,11 +50,25 @@ gulp.task('css', function () {
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
gulp.task('js', function () {
|
||||
var jsFilter = filter(['**/*.js'], {restore: true});
|
||||
|
||||
return gulp.src('assets/js/*.js')
|
||||
.on('error', swallowError)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(jsFilter)
|
||||
.pipe(uglify())
|
||||
.pipe(jsFilter.restore)
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest('assets/built/'))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
gulp.task('watch', function () {
|
||||
gulp.watch('assets/css/**', ['css']);
|
||||
});
|
||||
|
||||
gulp.task('zip', ['css'], function () {
|
||||
gulp.task('zip', ['css', 'js'], function () {
|
||||
var targetDir = 'dist/';
|
||||
var themeName = require('./package.json').name;
|
||||
var filename = themeName + '.zip';
|
||||
|
@ -2,13 +2,12 @@
|
||||
{{!-- 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 --}}
|
||||
<header class="site-header outer {{#if @blog.cover_image}}" style="background-image: url({{@blog.cover_image}}){{else}}no-cover{{/if}}">
|
||||
{{> header background=@blog.cover_image}} {{!--Special header.hbs partial to generate the <header> tag--}}
|
||||
<div class="inner">
|
||||
<div class="site-header-content">
|
||||
<h1 class="site-title">
|
||||
{{#if @blog.logo}}
|
||||
<img class="site-logo" src="{{@blog.logo}}" alt="{{@blog.title}}" />
|
||||
<img class="site-logo" src="{{img_url @blog.logo size="s"}}" alt="{{@blog.title}}" />
|
||||
{{else}}
|
||||
{{@blog.title}}
|
||||
{{/if}}
|
||||
@ -20,7 +19,7 @@ into the {body} of the default.hbs template --}}
|
||||
</header>
|
||||
|
||||
{{!-- The main content area --}}
|
||||
<main id="site-main" class="site-main outer" role="main">
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
|
||||
<div class="post-feed">
|
||||
|
39
package.json
39
package.json
@ -2,9 +2,9 @@
|
||||
"name": "casper",
|
||||
"description": "The default personal blogging theme for Ghost. Beautiful, minimal and responsive.",
|
||||
"demo": "https://demo.ghost.io",
|
||||
"version": "2.2.0",
|
||||
"version": "2.8.0",
|
||||
"engines": {
|
||||
"ghost": ">=1.2.0"
|
||||
"ghost": ">=2.0.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"screenshots": {
|
||||
@ -12,8 +12,9 @@
|
||||
"mobile": "assets/screenshot-mobile.jpg"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "gulp",
|
||||
"zip": "gulp zip"
|
||||
"dev": "gulp",
|
||||
"zip": "gulp zip",
|
||||
"test": "gscan ."
|
||||
},
|
||||
"author": {
|
||||
"name": "Ghost Foundation",
|
||||
@ -29,7 +30,8 @@
|
||||
},
|
||||
"keywords": [
|
||||
"ghost",
|
||||
"theme"
|
||||
"theme",
|
||||
"ghost-theme"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -40,6 +42,7 @@
|
||||
"devDependencies": {
|
||||
"autoprefixer": "6.3.6",
|
||||
"cssnano": "3.7.1",
|
||||
"gscan": "^2.0.0",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-livereload": "3.8.1",
|
||||
"gulp-postcss": "6.1.1",
|
||||
@ -49,9 +52,31 @@
|
||||
"gulp-zip": "4.0.0",
|
||||
"postcss-color-function": "2.0.1",
|
||||
"postcss-custom-properties": "5.0.1",
|
||||
"postcss-easy-import": "1.0.1"
|
||||
"postcss-easy-import": "1.0.1",
|
||||
"gulp-filter": "5.1.0",
|
||||
"gulp-uglify": "3.0.1"
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
page.hbs
21
page.hbs
@ -12,7 +12,7 @@ into the {body} of the default.hbs template --}}
|
||||
{{!-- Everything inside the #post tags pulls data from the post --}}
|
||||
{{#post}}
|
||||
|
||||
<main id="site-main" class="site-main outer" role="main">
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
|
||||
<article class="post-full {{post_class}} {{#unless feature_image}}no-image{{/unless}}">
|
||||
@ -22,12 +22,27 @@ into the {body} of the default.hbs template --}}
|
||||
</header>
|
||||
|
||||
{{#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>
|
||||
{{/if}}
|
||||
|
||||
<section class="post-full-content">
|
||||
{{content}}
|
||||
<div class="post-content">
|
||||
{{content}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
|
74
partials/byline-multiple.hbs
Normal file
74
partials/byline-multiple.hbs
Normal file
@ -0,0 +1,74 @@
|
||||
<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="{{img_url profile_image size="xs"}}" alt="{{name}}" />
|
||||
{{else}}
|
||||
<div class="author-profile-image">{{> "icons/avatar"}}</div>
|
||||
{{/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="{{img_url profile_image size="xs"}}" alt="{{name}}" />
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{url}}" class="moving-avatar author-profile-image">{{> "icons/avatar"}}</a>
|
||||
{{/if}}
|
||||
|
||||
</li>
|
||||
{{/foreach}}
|
||||
|
||||
</ul>
|
||||
|
||||
</section>
|
||||
|
||||
{{#contentFor "scripts"}}
|
||||
<script>
|
||||
// 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;
|
||||
|
||||
$('.author-list-item').hover(function(){
|
||||
var $this = $(this);
|
||||
|
||||
clearTimeout(hoverTimeout);
|
||||
|
||||
$('.author-card').removeClass('hovered');
|
||||
$(this).children('.author-card').addClass('hovered');
|
||||
|
||||
}, function() {
|
||||
var $this = $(this);
|
||||
|
||||
hoverTimeout = setTimeout(function() {
|
||||
$this.children('.author-card').removeClass('hovered');
|
||||
}, 800);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{{/contentFor}}
|
23
partials/byline-single.hbs
Normal file
23
partials/byline-single.hbs
Normal file
@ -0,0 +1,23 @@
|
||||
{{!-- Everything inside the #author tags pulls data from the author --}}
|
||||
{{#primary_author}}
|
||||
|
||||
<section class="author-card">
|
||||
{{#if profile_image}}
|
||||
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" 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>
|
||||
|
||||
{{/primary_author}}
|
@ -2,7 +2,7 @@
|
||||
<div class="floating-header-logo">
|
||||
<a href="{{@blog.url}}">
|
||||
{{#if @blog.icon}}
|
||||
<img src="{{@blog.icon}}" alt="{{@blog.title}} icon" />
|
||||
<img src="{{img_url @blog.icon size="xxs"}}" alt="{{@blog.title}} icon" />
|
||||
{{/if}}
|
||||
<span>{{@blog.title}}</span>
|
||||
</a>
|
||||
@ -20,7 +20,7 @@
|
||||
{{> "icons/facebook"}}
|
||||
</a>
|
||||
</div>
|
||||
<progress class="progress" value="0">
|
||||
<progress id="reading-progress" class="progress" value="0">
|
||||
<div class="progress-container">
|
||||
<span class="progress-bar"></span>
|
||||
</div>
|
||||
|
51
partials/header.hbs
Normal file
51
partials/header.hbs
Normal file
@ -0,0 +1,51 @@
|
||||
{{!--
|
||||
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}}
|
@ -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}}
|
||||
<a class="post-card-image-link" href="{{url}}">
|
||||
<div class="post-card-image" style="background-image: url({{feature_image}})"></div>
|
||||
</a>
|
||||
<a class="post-card-image-link" href="{{url}}">
|
||||
{{!-- 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 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}}
|
||||
|
||||
<div class="post-card-content">
|
||||
|
||||
<a class="post-card-content-link" href="{{url}}">
|
||||
|
||||
<header class="post-card-header">
|
||||
{{#if primary_tag}}
|
||||
<span class="post-card-tags">{{primary_tag.name}}</span>
|
||||
{{/if}}
|
||||
<h2 class="post-card-title">{{title}}</h2>
|
||||
</header>
|
||||
|
||||
<section class="post-card-excerpt">
|
||||
<p>{{excerpt words="33"}}</p>
|
||||
</section>
|
||||
|
||||
</a>
|
||||
|
||||
<footer class="post-card-meta">
|
||||
|
||||
<ul class="author-list">
|
||||
@ -27,9 +44,11 @@
|
||||
</div>
|
||||
|
||||
{{#if profile_image}}
|
||||
<span class="profile-image-wrapper"><img class="author-profile-image" src="{{profile_image}}" alt="{{name}}" /></span>
|
||||
<a href="{{url}}" class="static-avatar">
|
||||
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
|
||||
</a>
|
||||
{{else}}
|
||||
<span class="avatar-wrapper">{{> "icons/avatar"}}</span>
|
||||
<a href="{{url}}" class="static-avatar author-profile-image">{{> "icons/avatar"}}</a>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/foreach}}
|
||||
@ -38,5 +57,7 @@
|
||||
<span class="reading-time">{{reading_time}}</span>
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
</div>{{!--/.post-card-content--}}
|
||||
|
||||
</article>
|
||||
|
@ -14,16 +14,16 @@
|
||||
<div class="site-nav-right">
|
||||
<div class="social-links">
|
||||
{{#if @blog.facebook}}
|
||||
<a class="social-link social-link-fb" href="{{facebook_url @blog.facebook}}" target="_blank" rel="noopener">{{> "icons/facebook"}}</a>
|
||||
<a class="social-link social-link-fb" href="{{facebook_url @blog.facebook}}" title="Facebook" target="_blank" rel="noopener">{{> "icons/facebook"}}</a>
|
||||
{{/if}}
|
||||
{{#if @blog.twitter}}
|
||||
<a class="social-link social-link-tw" href="{{twitter_url @blog.twitter}}" target="_blank" rel="noopener">{{> "icons/twitter"}}</a>
|
||||
<a class="social-link social-link-tw" href="{{twitter_url @blog.twitter}}" title="Twitter" target="_blank" rel="noopener">{{> "icons/twitter"}}</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if @labs.subscribers}}
|
||||
<a class="subscribe-button" href="#subscribe">Subscribe</a>
|
||||
{{else}}
|
||||
<a class="rss-button" href="https://feedly.com/i/subscription/feed/{{@blog.url}}/rss/" target="_blank" rel="noopener">{{> "icons/rss"}}</a>
|
||||
<a class="rss-button" href="https://feedly.com/i/subscription/feed/{{@blog.url}}/rss/" title="RSS" target="_blank" rel="noopener">{{> "icons/rss"}}</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</nav>
|
||||
|
173
post.hbs
173
post.hbs
@ -12,7 +12,7 @@ into the {body} of the default.hbs template --}}
|
||||
{{!-- Everything inside the #post tags pulls data from the post --}}
|
||||
{{#post}}
|
||||
|
||||
<main id="site-main" class="site-main outer" role="main">
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
|
||||
<article class="post-full {{post_class}} {{#unless feature_image}}no-image{{/unless}}">
|
||||
@ -28,12 +28,27 @@ into the {body} of the default.hbs template --}}
|
||||
</header>
|
||||
|
||||
{{#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>
|
||||
{{/if}}
|
||||
|
||||
<section class="post-full-content">
|
||||
{{content}}
|
||||
<div class="post-content">
|
||||
{{content}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{!-- Email subscribe form at the bottom of the page --}}
|
||||
@ -47,102 +62,22 @@ into the {body} of the default.hbs template --}}
|
||||
|
||||
<footer class="post-full-footer">
|
||||
|
||||
{{!-- If there are multiple authors for the post, display all of their avatars in a stack --}}
|
||||
{{!-- There are two options for how we display the byline/author-info.
|
||||
If the post has more than one author, we load a specific template
|
||||
from includes/byline-multiple.hbs, otherwise, we just use the
|
||||
default byline. --}}
|
||||
|
||||
{{#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>
|
||||
|
||||
{{!-- If there is a single author for the post, display her/his information --}}
|
||||
{{> "byline-multiple"}}
|
||||
{{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}}
|
||||
{{> "byline-single"}}
|
||||
{{/has}}
|
||||
|
||||
</footer>
|
||||
|
||||
{{!--
|
||||
|
||||
If you use Disqus comments, just uncomment this block.
|
||||
The only thing you need to change is "test-apkdzgmqhj" - which
|
||||
should be replaced with your own Disqus site-id.
|
||||
|
||||
<section class="post-full-comments">
|
||||
<div id="disqus_thread"></div>
|
||||
<script>
|
||||
var disqus_config = function () {
|
||||
this.page.url = '{{url absolute="true"}}';
|
||||
this.page.identifier = 'ghost-{{comment_id}}';
|
||||
};
|
||||
(function() {
|
||||
var d = document, s = d.createElement('script');
|
||||
s.src = 'https://test-apkdzgmqhj.disqus.com/embed.js';
|
||||
s.setAttribute('data-timestamp', +new Date());
|
||||
(d.head || d.body).appendChild(s);
|
||||
})();
|
||||
</script>
|
||||
If you want to embed comments, this is a good place to do it!
|
||||
</section>
|
||||
--}}
|
||||
|
||||
@ -160,10 +95,10 @@ into the {body} of the default.hbs template --}}
|
||||
{{#if related_posts}}
|
||||
<article class="read-next-card"
|
||||
{{#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}}
|
||||
{{#if @blog.cover_image}}
|
||||
style="background-image: url({{@blog.cover_image}})"{{/if}}
|
||||
style="background-image: url({{img_url @blog.cover_image size="m"}})"{{/if}}
|
||||
{{/if}}
|
||||
>
|
||||
<header class="read-next-card-header">
|
||||
@ -221,7 +156,7 @@ $(document).ready(function () {
|
||||
$postContent.fitVids();
|
||||
// End fitVids
|
||||
|
||||
var progressBar = document.querySelector('progress');
|
||||
var progressBar = document.querySelector('#reading-progress');
|
||||
var header = document.querySelector('.floating-header');
|
||||
var title = document.querySelector('.post-full-title');
|
||||
|
||||
@ -271,56 +206,6 @@ $(document).ready(function () {
|
||||
|
||||
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}}
|
||||
|
5
tag.hbs
5
tag.hbs
@ -1,9 +1,8 @@
|
||||
{{!< default}}
|
||||
{{!-- 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}}
|
||||
<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">
|
||||
{{> "site-nav"}}
|
||||
<div class="site-header-content">
|
||||
@ -21,7 +20,7 @@
|
||||
{{/tag}}
|
||||
|
||||
{{!-- The main content area --}}
|
||||
<main id="site-main" class="site-main outer" role="main">
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
<div class="post-feed">
|
||||
{{#foreach posts}}
|
||||
|
Reference in New Issue
Block a user