first commit

This commit is contained in:
Helldragon67 2024-10-17 16:11:58 +08:00
commit b0e800da06
74 changed files with 5251 additions and 0 deletions

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2013-2023 Ghost Foundation
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

65
README.md Normal file
View File

@ -0,0 +1,65 @@
# Source
The default theme for [Ghost](http://github.com/tryghost/ghost/). This is the latest development version of Source! If you're just looking to download the latest release, head over to the [releases](https://github.com/TryGhost/Source/releases) page.
 
# First time using a Ghost theme?
Ghost uses a simple templating language called [Handlebars](http://handlebarsjs.com/) for its themes.
This theme has lots of code comments to help explain what's going on just by reading the code. Once you feel comfortable with how everything works, we also have full [theme API documentation](https://ghost.org/docs/themes/) which explains every possible Handlebars helper and template.
**The main files are:**
- `default.hbs` - The parent template file, which includes your global header/footer
- `home.hbs` - The homepage
- `index.hbs` - The main template to generate a list of posts
- `post.hbs` - The template used to render individual posts
- `page.hbs` - Used for individual pages
- `tag.hbs` - Used for tag archives, eg. "all posts tagged with `news`"
- `author.hbs` - Used for author archives, eg. "all posts written by Jamie"
One neat trick is that you can also create custom one-off templates by adding the slug of a page to a template file. For example:
- `page-about.hbs` - Custom template for an `/about/` page
- `tag-news.hbs` - Custom template for `/tag/news/` archive
- `author-ali.hbs` - Custom template for `/author/ali/` archive
# Development
Source 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
# install dependencies
yarn install
# run development server
yarn dev
```
Now you can edit `/assets/css/` files, which will be compiled to `/assets/built/` automatically.
The `zip` Gulp task packages the theme files into `dist/<theme-name>.zip`, which you can then upload to your site.
```bash
# create .zip file
yarn zip
```
# PostCSS Features Used
- Autoprefixer - Don't worry about writing browser prefixes of any kind, it's all done automatically with support for the latest 2 major versions of every browser.
# SVG Icons
Source uses inline SVG icons, included via Handlebars partials. You can find all icons inside `/partials/icons`. To use an icon just include the name of the relevant file, eg. To include the SVG icon in `/partials/icons/rss.hbs` - use `{{> "icons/rss"}}`.
You can add your own SVG icons in the same manner.
# Copyright & License
Copyright (c) 2013-2023 Ghost Foundation - Released under the [MIT license](LICENSE).

2
assets/built/screen.css Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
assets/built/source.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

63
assets/css/contact.css Normal file
View File

@ -0,0 +1,63 @@
.contact-form {
background: linear-gradient(to bottom right, #F9B208, #E72E77); /* Gradient background */
padding: 30px;
border-radius: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 100%;
text-align: left;
}
.form-row {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.form-field {
flex: 1;
}
.form-field label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: black;
}
.form-field input,
.form-field textarea {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 15px;
background-color: white; /* Field background */
color: black; /* Text color */
transition: border-color 0.3s;
}
.form-field input:focus,
.form-field textarea:focus {
outline: none;
border-color: #E72E77; /* Pink focus */
}
.form-field textarea {
height: 120px;
}
.submit-button {
display: block;
width: 100%;
padding: 12px;
background-color: #28a745; /* Green button color */
color: white;
border: none;
border-radius: 8px; /* Rounded corners */
font-size: 16px;
font-weight: bold; /* Bold text */
text-transform: uppercase; /* Full caps */
cursor: pointer;
transition: background-color 0.3s;
}
.submit-button:disabled {
background-color: #ddd;
cursor: not-allowed;
}
.submit-button:hover:not(:disabled) {
background-color: #218838; /* Darker green on hover */
}

74
assets/css/hero.css Normal file
View File

@ -0,0 +1,74 @@
.hero {
position: relative;
width: 100%;
margin: 0 auto; /* Centering the hero section */
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 80px;
padding-bottom: 80px;
padding-left: 40px;
padding-right: 40px;
background: linear-gradient(to bottom right, #F9B208, #E72E77);
}
.hero-inner {
display: flex;
justify-content: space-between;
width: 100%;
}
.hero-content {
max-width: 50%;
text-align: center;
}
.hero-logo {
width: 150px;
margin: 0 auto 20px;
}
.hero-title {
font-size: 2.5rem;
color: white;
margin-bottom: 20px;
}
.hero-button {
background-color: white;
color: #E72E77;
padding: 15px 30px;
border: none;
border-radius: 5px;
font-size: 1.4rem;
font-weight: bold;
cursor: pointer;
transition: background-color 0.3s;
text-transform: uppercase;
}
.hero-button:hover {
background-color: #F9B208;
}
.hero-screenshots {
display: flex;
gap: 20px;
}
.screenshot {
width: 150px;
height: 300px;
background-color: white;
border: 2px solid white;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
border-radius: 15px;
overflow: hidden;
}
.screenshot img {
width: 100%;
height: 100%;
object-fit: cover;
}

3335
assets/css/screen.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,3 @@
[ZoneTransfer]
ZoneId=3
HostUrl=https://drive.usercontent.google.com/download?id=1ZnC3HIninR6-smY1OryZrDWobuMoP2bo&export=download&authuser=0&confirm=t&uuid=fcc30c47-4e3c-44fa-b599-b2f0f36e606a&at=AN_67v3g121eQ3UuST_8Rf4eieJM:1728460853316

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,3 @@
[ZoneTransfer]
ZoneId=3
HostUrl=https://drive.usercontent.google.com/download?id=1me856DELB6JerhC5BnctMM6xKFgRGMpU&export=download&authuser=0&confirm=t&uuid=b8cd3847-558d-42ea-962f-8d1d49891ee1&at=AN_67v2B4Q15WVcuCijrjHCQNQj2:1728460828982

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

View File

@ -0,0 +1 @@
<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/images/preloader.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

93
assets/js/dropdown.js Normal file
View File

@ -0,0 +1,93 @@
function dropdown() {
const mediaQuery = window.matchMedia('(max-width: 767px)');
const head = document.querySelector('.gh-navigation');
const menu = head.querySelector('.gh-navigation-menu');
const nav = menu?.querySelector('.nav');
if (!nav) return;
const logo = document.querySelector('.gh-navigation-logo');
const navHTML = nav.innerHTML;
if (mediaQuery.matches) {
const items = nav.querySelectorAll('li');
items.forEach(function (item, index) {
item.style.transitionDelay = `${0.03 * (index + 1)}s`;
});
}
const makeDropdown = function () {
if (mediaQuery.matches) return;
const submenuItems = [];
while ((nav.offsetWidth + 64) > menu.offsetWidth) {
if (nav.lastElementChild) {
submenuItems.unshift(nav.lastElementChild);
nav.lastElementChild.remove();
} else {
break;
}
}
if (!submenuItems.length) {
head.classList.add('is-dropdown-loaded');
return;
}
const toggle = document.createElement('button');
toggle.setAttribute('class', 'gh-more-toggle gh-icon-button');
toggle.setAttribute('aria-label', 'More');
toggle.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="currentColor"><path d="M21.333 16c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0c0 1.473-1.194 2.667-2.667 2.667v0c-1.473 0-2.667-1.194-2.667-2.667v0zM13.333 16c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0c0 1.473-1.194 2.667-2.667 2.667v0c-1.473 0-2.667-1.194-2.667-2.667v0zM5.333 16c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0c0 1.473-1.194 2.667-2.667 2.667v0c-1.473 0-2.667-1.194-2.667-2.667v0z"></path></svg>';
const wrapper = document.createElement('div');
wrapper.setAttribute('class', 'gh-dropdown');
if (submenuItems.length >= 10) {
head.classList.add('is-dropdown-mega');
wrapper.style.gridTemplateRows = `repeat(${Math.ceil(submenuItems.length / 2)}, 1fr)`;
} else {
head.classList.remove('is-dropdown-mega');
}
submenuItems.forEach(function (child) {
wrapper.appendChild(child);
});
toggle.appendChild(wrapper);
nav.appendChild(toggle);
const toggleRect = toggle.getBoundingClientRect();
const documentCenter = window.innerWidth / 2;
if (toggleRect.left < documentCenter) {
wrapper.classList.add('is-left');
}
head.classList.add('is-dropdown-loaded');
window.addEventListener('click', function (e) {
if (head.classList.contains('is-dropdown-open')) {
head.classList.remove('is-dropdown-open');
} else if (toggle.contains(e.target)) {
head.classList.add('is-dropdown-open');
}
});
}
imagesLoaded(logo, function () {
makeDropdown();
});
window.addEventListener('load', function () {
if (!logo) {
makeDropdown();
}
});
window.addEventListener('resize', function () {
setTimeout(() => {
nav.innerHTML = navHTML;
makeDropdown();
}, 1);
});
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
assets/js/lib/photoswipe.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
assets/js/lib/reframe.min.js vendored Normal file
View File

@ -0,0 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).reframe=t()}(this,function(){"use strict";function t(){for(var e=0,t=0,n=arguments.length;t<n;t++)e+=arguments[t].length;for(var i=Array(e),o=0,t=0;t<n;t++)for(var r=arguments[t],f=0,d=r.length;f<d;f++,o++)i[o]=r[f];return i}return function(e,s){return void 0===s&&(s="js-reframe"),("string"==typeof e?t(document.querySelectorAll(e)):"length"in e?t(e):[e]).forEach(function(e){var t,n,i,o,r,f,d,l;-1!==e.className.split(" ").indexOf(s)||-1<e.style.width.indexOf("%")||(i=e.getAttribute("height")||e.offsetHeight,o=e.getAttribute("width")||e.offsetWidth,r=("string"==typeof i?parseInt(i):i)/("string"==typeof o?parseInt(o):o)*100,(f=document.createElement("div")).className=s,(d=f.style).position="relative",d.width="100%",d.paddingTop=r+"%",(l=e.style).position="absolute",l.width="100%",l.height="100%",l.left="0",l.top="0",null!==(t=e.parentNode)&&void 0!==t&&t.insertBefore(f,e),null!==(n=e.parentNode)&&void 0!==n&&n.removeChild(e),f.appendChild(e))})}});

103
assets/js/lightbox.js Normal file
View File

@ -0,0 +1,103 @@
function lightbox(trigger) {
var onThumbnailsClick = function (e) {
e.preventDefault();
var items = [];
var index = 0;
var prevSibling = e.target.closest('.kg-card').previousElementSibling;
while (prevSibling && (prevSibling.classList.contains('kg-image-card') || prevSibling.classList.contains('kg-gallery-card'))) {
var prevItems = [];
prevSibling.querySelectorAll('img').forEach(function (item) {
prevItems.push({
src: item.getAttribute('src'),
msrc: item.getAttribute('src'),
w: item.getAttribute('width'),
h: item.getAttribute('height'),
el: item,
})
index += 1;
});
prevSibling = prevSibling.previousElementSibling;
items = prevItems.concat(items);
}
if (e.target.classList.contains('kg-image')) {
items.push({
src: e.target.getAttribute('src'),
msrc: e.target.getAttribute('src'),
w: e.target.getAttribute('width'),
h: e.target.getAttribute('height'),
el: e.target,
});
} else {
var reachedCurrentItem = false;
e.target.closest('.kg-gallery-card').querySelectorAll('img').forEach(function (item) {
items.push({
src: item.getAttribute('src'),
msrc: item.getAttribute('src'),
w: item.getAttribute('width'),
h: item.getAttribute('height'),
el: item,
});
if (!reachedCurrentItem && item !== e.target) {
index += 1;
} else {
reachedCurrentItem = true;
}
});
}
var nextSibling = e.target.closest('.kg-card').nextElementSibling;
while (nextSibling && (nextSibling.classList.contains('kg-image-card') || nextSibling.classList.contains('kg-gallery-card'))) {
nextSibling.querySelectorAll('img').forEach(function (item) {
items.push({
src: item.getAttribute('src'),
msrc: item.getAttribute('src'),
w: item.getAttribute('width'),
h: item.getAttribute('height'),
el: item,
})
});
nextSibling = nextSibling.nextElementSibling;
}
var pswpElement = document.querySelectorAll('.pswp')[0];
var options = {
bgOpacity: 0.9,
closeOnScroll: true,
fullscreenEl: false,
history: false,
index: index,
shareEl: false,
zoomEl: false,
getThumbBoundsFn: function(index) {
var thumbnail = items[index].el,
pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
rect = thumbnail.getBoundingClientRect();
return {x:rect.left, y:rect.top + pageYScroll, w:rect.width};
}
}
var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
return false;
};
var triggers = document.querySelectorAll(trigger);
triggers.forEach(function (trig) {
trig.addEventListener('click', function (e) {
onThumbnailsClick(e);
});
});
}

60
assets/js/main.js Normal file
View File

@ -0,0 +1,60 @@
/* Mobile menu burger toggle */
(function () {
const navigation = document.querySelector('.gh-navigation');
const burger = navigation.querySelector('.gh-burger');
if (!burger) return;
burger.addEventListener('click', function () {
if (!navigation.classList.contains('is-open')) {
navigation.classList.add('is-open');
document.documentElement.style.overflowY = 'hidden';
} else {
navigation.classList.remove('is-open');
document.documentElement.style.overflowY = null;
}
});
})();
/* Add lightbox to gallery images */
(function () {
lightbox(
'.kg-image-card > .kg-image[width][height], .kg-gallery-image > img'
);
})();
/* Responsive video in post content */
(function () {
const sources = [
'.gh-content iframe[src*="youtube.com"]',
'.gh-content iframe[src*="youtube-nocookie.com"]',
'.gh-content iframe[src*="player.vimeo.com"]',
'.gh-content iframe[src*="kickstarter.com"][src*="video.html"]',
'.gh-content object',
'.gh-content embed',
];
reframe(document.querySelectorAll(sources.join(',')));
})();
/* Turn the main nav into dropdown menu when there are more than 5 menu items */
(function () {
dropdown();
})();
/* Infinite scroll pagination */
(function () {
if (!document.body.classList.contains('home-template') && !document.body.classList.contains('post-template')) {
pagination();
}
})();
/* Responsive HTML table */
(function () {
const tables = document.querySelectorAll('.gh-content > table:not(.gist table)');
tables.forEach(function (table) {
const wrapper = document.createElement('div');
wrapper.className = 'gh-table';
table.parentNode.insertBefore(wrapper, table);
wrapper.appendChild(table);
});
})();

95
assets/js/pagination.js Normal file
View File

@ -0,0 +1,95 @@
function pagination(isInfinite = true, done, isMasonry = false) {
const feedElement = document.querySelector('.gh-feed');
if (!feedElement) return;
let loading = false;
const target = document.querySelector('.gh-footer');
const buttonElement = document.querySelector('.gh-loadmore');
if (!document.querySelector('link[rel=next]') && buttonElement) {
buttonElement.remove();
}
const loadNextPage = async function () {
const nextElement = document.querySelector('link[rel=next]');
if (!nextElement) return;
try {
const res = await fetch(nextElement.href);
const html = await res.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const postElements = doc.querySelectorAll('.gh-feed:not(.gh-featured):not(.gh-related) > *');
const fragment = document.createDocumentFragment();
const elems = [];
postElements.forEach(function (post) {
var clonedItem = document.importNode(post, true);
if (isMasonry) {
clonedItem.style.visibility = 'hidden';
}
fragment.appendChild(clonedItem);
elems.push(clonedItem);
});
feedElement.appendChild(fragment);
if (done) {
done(elems, loadNextWithCheck);
}
const resNextElement = doc.querySelector('link[rel=next]');
if (resNextElement && resNextElement.href) {
nextElement.href = resNextElement.href;
} else {
nextElement.remove();
if (buttonElement) {
buttonElement.remove();
}
}
} catch (e) {
nextElement.remove();
throw e;
}
};
const loadNextWithCheck = async function () {
if (target.getBoundingClientRect().top <= window.innerHeight && document.querySelector('link[rel=next]')) {
await loadNextPage();
}
}
const callback = async function (entries) {
if (loading) return;
loading = true;
if (entries[0].isIntersecting) {
// keep loading next page until target is out of the viewport or we've loaded the last page
if (!isMasonry) {
while (target.getBoundingClientRect().top <= window.innerHeight && document.querySelector('link[rel=next]')) {
await loadNextPage();
}
} else {
await loadNextPage();
}
}
loading = false;
if (!document.querySelector('link[rel=next]')) {
observer.disconnect();
}
};
const observer = new IntersectionObserver(callback);
if (isInfinite) {
observer.observe(target);
} else {
buttonElement.addEventListener('click', loadNextPage);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 KiB

View File

@ -0,0 +1,3 @@
[ZoneTransfer]
ZoneId=3
HostUrl=https://drive.usercontent.google.com/download?id=1rETn5Be27W69BwP9dj8ML-3WFfvLBuw-&export=download&authuser=0&confirm=t&uuid=98972e54-7ea2-4e57-92b9-1c2d8bcc3a1e&at=AN_67v1ChZb7Wtpz3Hw4O_Rdo-km:1728461138796

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 KiB

View File

@ -0,0 +1,3 @@
[ZoneTransfer]
ZoneId=3
HostUrl=https://drive.usercontent.google.com/download?id=1MtAc_I9TUymkXKMh41Nx208KBjKsqK21&export=download&authuser=0&confirm=t&uuid=cac5c1a0-7658-4532-afb2-b74efbfd45b4&at=AN_67v2qM12LBSDhg2T9tCA_ptoN:1728461140923

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -0,0 +1,3 @@
[ZoneTransfer]
ZoneId=3
HostUrl=https://drive.usercontent.google.com/download?id=1mGoByZ4ZWw4QaclGCOdPTCHYHYsWxSYz&export=download&authuser=0&confirm=t&uuid=ff98dd99-0489-4e31-90c1-0d963e34095e&at=AN_67v2G42lRs8jP7qQB8gLgAJlm:1728461145677

43
author.hbs Normal file
View File

@ -0,0 +1,43 @@
{{!< default}}
{{!-- The tag above means: insert everything in this file into the body of the default.hbs template --}}
<main class="gh-main gh-outer">
{{#author}}
<section class="gh-archive{{#if @custom.show_publication_info_sidebar}} has-sidebar{{/if}} gh-inner">
<div class="gh-archive-inner">
<div class="gh-archive-wrapper">
<h1 class="gh-article-title is-title">
{{#if website}}
<a class="gh-author-social-link" href="{{website}}" target="_blank" rel="noopener">{{name}}</a>
{{else}}
{{name}}
{{/if}}
</h1>
{{#if bio}}
<p class="gh-article-excerpt">{{bio}}</p>
{{/if}}
<footer class="gh-author-meta">
<div class="gh-author-social">
{{#if facebook}}
<a class="gh-author-social-link" href="{{facebook_url}}" target="_blank" rel="noopener">{{> "icons/facebook"}}</a>
{{/if}}
{{#if twitter}}
<a class="gh-author-social-link" href="{{twitter_url}}" target="_blank" rel="noopener">{{> "icons/twitter"}}</a>
{{/if}}
</div>
{{#if location}}
<div class="gh-author-location">{{location}}</div>
{{/if}}
</footer>
</div>
{{#if profile_image}}
<img class="gh-article-image" src="{{img_url profile_image size="s"}}" alt="{{name}}">
{{/if}}
</div>
</section>
{{/author}}
{{> "components/post-list" feed="archive" postFeedStyle=@custom.post_feed_style showTitle=false showSidebar=@custom.show_publication_info_sidebar}}
</main>

73
default.hbs Normal file
View File

@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="{{@site.locale}}">
<head>
{{!-- Basic meta - advanced meta is output with {{ghost_head}} below --}}
<title>{{meta_title}}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{!-- Preload main styles and scripts for better performance --}}
<link rel="preload" as="style" href="{{asset "built/screen.css"}}">
<link rel="preload" as="script" href="{{asset "built/source.js"}}">
{{!-- Fonts are preloaded and defined in the default template to avoid layout shift --}}
{{> "typography/fonts"}}
{{!-- Theme assets - use the {{asset}} helper to reference styles & scripts, this will take care of caching and cache-busting automatically --}}
<link rel="stylesheet" type="text/css" href="{{asset "built/screen.css"}}">
{{!-- Custom background color --}}
<style>
:root {
--background-color: {{@custom.site_background_color}}
}
</style>
<script>
/* The script for calculating the color contrast has been taken from
https://gomakethings.com/dynamically-changing-the-text-color-based-on-background-color-contrast-with-vanilla-js/ */
var accentColor = getComputedStyle(document.documentElement).getPropertyValue('--background-color');
accentColor = accentColor.trim().slice(1);
if (accentColor.length === 3) {
accentColor = accentColor[0] + accentColor[0] + accentColor[1] + accentColor[1] + accentColor[2] + accentColor[2];
}
var r = parseInt(accentColor.substr(0, 2), 16);
var g = parseInt(accentColor.substr(2, 2), 16);
var b = parseInt(accentColor.substr(4, 2), 16);
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
var textColor = (yiq >= 128) ? 'dark' : 'light';
document.documentElement.className = `has-${textColor}-text`;
</script>
{{!-- This tag outputs all your advanced SEO meta, structured data, and other important settings, it should always be the last tag before the closing head tag --}}
{{ghost_head}}
</head>
<body class="{{body_class}} has-{{#match @custom.title_font "Elegant serif"}}serif{{else match @custom.title_font "Consistent mono"}}mono{{else}}sans{{/match}}-title has-{{#match @custom.body_font "Elegant serif"}}serif{{else}}sans{{/match}}-body">
<div class="gh-viewport">
{{> "components/navigation" navigationLayout=@custom.navigation_layout}}
{{{body}}}
{{> "components/footer"}}
</div>
{{#is "post, page"}}
{{> "lightbox"}}
{{/is}}
{{!-- Scripts - handle responsive videos, infinite scroll, and navigation dropdowns --}}
<script src="{{asset "built/source.js"}}"></script>
{{!-- Ghost outputs required functional scripts with this tag, it should always be the last thing before the closing body tag --}}
{{ghost_foot}}
</body>
</html>

174
gulpfile.js Normal file
View File

@ -0,0 +1,174 @@
const {series, watch, src, dest, parallel} = require('gulp');
const pump = require('pump');
const path = require('path');
const releaseUtils = require('@tryghost/release-utils');
const inquirer = require('inquirer');
// gulp plugins and utils
const livereload = require('gulp-livereload');
const postcss = require('gulp-postcss');
const zip = require('gulp-zip');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const beeper = require('beeper');
const fs = require('fs');
// postcss plugins
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const easyimport = require('postcss-easy-import');
const REPO = 'TryGhost/Source';
const REPO_READONLY = 'TryGhost/Source';
const CHANGELOG_PATH = path.join(process.cwd(), '.', 'changelog.md');
function serve(done) {
livereload.listen();
done();
}
const handleError = (done) => {
return function (err) {
if (err) {
beeper();
}
return done(err);
};
};
function hbs(done) {
pump([
src(['*.hbs', 'partials/**/*.hbs']),
livereload()
], handleError(done));
}
function css(done) {
pump([
src('assets/css/screen.css', {sourcemaps: true}),
postcss([
easyimport,
autoprefixer(),
cssnano()
]),
dest('assets/built/', {sourcemaps: '.'}),
livereload()
], handleError(done));
}
function js(done) {
pump([
src([
// pull in lib files first so our own code can depend on it
'assets/js/lib/*.js',
'assets/js/*.js'
], {sourcemaps: true}),
concat('source.js'),
uglify(),
dest('assets/built/', {sourcemaps: '.'}),
livereload()
], handleError(done));
}
function zipper(done) {
const filename = require('./package.json').name + '.zip';
pump([
src([
'**',
'!node_modules', '!node_modules/**',
'!dist', '!dist/**',
'!yarn-error.log',
'!yarn.lock',
'!gulpfile.js'
]),
zip(filename),
dest('dist/')
], handleError(done));
}
const cssWatcher = () => watch('assets/css/**', css);
const jsWatcher = () => watch('assets/js/**', js);
const hbsWatcher = () => watch(['*.hbs', 'partials/**/*.hbs'], hbs);
const watcher = parallel(cssWatcher, jsWatcher, hbsWatcher);
const build = series(css, js);
exports.build = build;
exports.zip = series(build, zipper);
exports.default = series(build, serve, watcher);
exports.release = async () => {
// @NOTE: https://yarnpkg.com/lang/en/docs/cli/version/
// require(./package.json) can run into caching issues, this re-reads from file everytime on release
let packageJSON = JSON.parse(fs.readFileSync('./package.json'));
const newVersion = packageJSON.version;
if (!newVersion || newVersion === '') {
console.log(`Invalid version: ${newVersion}`);
return;
}
console.log(`\nCreating release for ${newVersion}...`);
const githubToken = process.env.GST_TOKEN;
if (!githubToken) {
console.log('Please configure your environment with a GitHub token located in GST_TOKEN');
return;
}
try {
const result = await inquirer.prompt([{
type: 'input',
name: 'compatibleWithGhost',
message: 'Which version of Ghost is it compatible with?',
default: '5.0.0'
}]);
const compatibleWithGhost = result.compatibleWithGhost;
const releasesResponse = await releaseUtils.releases.get({
userAgent: 'Source',
uri: `https://api.github.com/repos/${REPO_READONLY}/releases`
});
if (!releasesResponse || !releasesResponse) {
console.log('No releases found. Skipping...');
return;
}
let previousVersion = releasesResponse[0].tag_name || releasesResponse[0].name;
console.log(`Previous version: ${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 newReleaseResponse = await releaseUtils.releases.create({
draft: true,
preRelease: false,
tagName: 'v' + newVersion,
releaseName: newVersion,
userAgent: 'Source',
uri: `https://api.github.com/repos/${REPO}/releases`,
github: {
token: githubToken
},
content: [`**Compatible with Ghost ≥ ${compatibleWithGhost}**\n\n`],
changelogPath: CHANGELOG_PATH
});
console.log(`\nRelease draft generated: ${newReleaseResponse.releaseUrl}\n`);
} catch (err) {
console.error(err);
process.exit(1);
}
};

13
home.hbs Normal file
View File

@ -0,0 +1,13 @@
{{!< default}}
{{!-- The tag above means: insert everything in this file into the body of the default.hbs template --}}
{{> "components/header" headerStyle=@custom.header_style}}
{{#match @custom.header_style "!=" "Highlight"}}
{{> "components/featured" showFeatured=@custom.show_featured_posts limit=4}}
{{/match}}
{{> "components/hero"}}
{{> "components/post-list" feed="home" postFeedStyle=@custom.post_feed_style showTitle=true showSidebar=@custom.show_publication_info_sidebar}}

6
index.hbs Normal file
View File

@ -0,0 +1,6 @@
{{!< default}}
{{!-- The tag above means: insert everything in this file into the body of the default.hbs template --}}
<main class="gh-main">
{{> "components/post-list" feed="index" postFeedStyle=@custom.post_feed_style showTitle=true showSidebar=@custom.show_publication_info_sidebar}}
</main>

217
package.json Normal file
View File

@ -0,0 +1,217 @@
{
"name": "source",
"description": "A default theme for the Ghost publishing platform",
"demo": "https://source.ghost.io",
"version": "1.3.2",
"engines": {
"ghost": ">=5.0.0"
},
"license": "MIT",
"scripts": {
"dev": "gulp",
"zip": "gulp zip",
"test": "gscan .",
"test:ci": "gscan --fatal --verbose .",
"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": {
"name": "Ghost Foundation",
"email": "hello@ghost.org",
"url": "https://ghost.org/"
},
"gpm": {
"type": "theme",
"categories": [
"Minimal",
"Magazine"
]
},
"keywords": [
"ghost",
"theme",
"ghost-theme"
],
"repository": {
"type": "git",
"url": "https://github.com/TryGhost/Source.git"
},
"bugs": "https://github.com/TryGhost/Source/issues",
"contributors": "https://github.com/TryGhost/Source/graphs/contributors",
"devDependencies": {
"@tryghost/release-utils": "0.8.1",
"autoprefixer": "10.4.7",
"beeper": "2.1.0",
"cssnano": "5.1.12",
"gscan": "4.39.4",
"gulp": "4.0.2",
"gulp-concat": "2.6.1",
"gulp-livereload": "4.0.2",
"gulp-postcss": "9.0.1",
"gulp-uglify": "3.0.2",
"gulp-zip": "5.1.0",
"inquirer": "8.2.4",
"postcss": "8.2.13",
"postcss-easy-import": "4.0.0",
"pump": "3.0.0"
},
"browserslist": [
"defaults"
],
"config": {
"posts_per_page": 12,
"image_sizes": {
"xs": {
"width": 160
},
"s": {
"width": 320
},
"m": {
"width": 600
},
"l": {
"width": 960
},
"xl": {
"width": 1200
},
"xxl": {
"width": 2000
}
},
"card_assets": true,
"custom": {
"navigation_layout": {
"type": "select",
"options": [
"Logo in the middle",
"Logo on the left",
"Stacked"
],
"default": "Logo in the middle"
},
"site_background_color": {
"type": "color",
"default": "#ffffff"
},
"header_and_footer_color": {
"type": "select",
"options": [
"Background color",
"Accent color"
],
"default": "Background color"
},
"title_font": {
"type": "select",
"options": [
"Modern sans-serif",
"Elegant serif",
"Consistent mono"
],
"default": "Modern sans-serif"
},
"body_font": {
"type": "select",
"options": [
"Modern sans-serif",
"Elegant serif"
],
"default": "Modern sans-serif"
},
"signup_heading": {
"type": "text",
"description": "Used in your footer across your theme, defaults to site title when empty"
},
"signup_subheading": {
"type": "text",
"description": "Defaults to site description when empty"
},
"header_style": {
"type": "select",
"options": [
"Landing",
"Highlight",
"Magazine",
"Search",
"Off"
],
"description": "Landing is recommended for all sites, Highlight & Magazine for those with more content",
"default": "Landing",
"group": "homepage"
},
"header_text": {
"type": "text",
"group": "homepage",
"description": "Defaults to site description when empty",
"visibility": "header_style:[Landing, Search]"
},
"background_image": {
"type": "boolean",
"default": true,
"description": "Use the publication cover set on the Brand tab as your background",
"group": "homepage",
"visibility": "header_style:[Landing, Search]"
},
"show_featured_posts": {
"type": "boolean",
"default": false,
"group": "homepage",
"visibility": "header_style:[Highlight, Magazine]"
},
"post_feed_style": {
"type": "select",
"options": [
"List",
"Grid"
],
"default": "List",
"group": "homepage"
},
"show_images_in_feed": {
"type": "boolean",
"default": true,
"group": "homepage",
"visibility": "post_feed_style:List"
},
"show_author": {
"type": "boolean",
"default": true,
"group": "homepage"
},
"show_publish_date": {
"type": "boolean",
"default": true,
"group": "homepage"
},
"show_publication_info_sidebar": {
"type": "boolean",
"default": false,
"group": "homepage"
},
"show_post_metadata": {
"type": "boolean",
"default": true,
"group": "post"
},
"enable_drop_caps_on_posts": {
"type": "boolean",
"default": false,
"group": "post"
},
"show_related_articles": {
"type": "boolean",
"default": true,
"group": "post"
}
}
},
"renovate": {
"extends": [
"@tryghost:theme"
]
}
}

26
page.hbs Normal file
View File

@ -0,0 +1,26 @@
{{!< default}}
{{!-- The tag above means: insert everything in this file into the body of the default.hbs template --}}
{{#post}}
<main class="gh-main">
<article class="gh-article {{post_class}}">
{{#match @page.show_title_and_feature_image}}
<header class="gh-article-header gh-canvas">
<h1 class="gh-article-title is-title">{{title}}</h1>
{{#if custom_excerpt}}
<p class="gh-article-excerpt is-body">{{custom_excerpt}}</p>
{{/if}}
{{> "feature-image"}}
</header>
{{/match}}
<section class="gh-content gh-canvas is-body">
{{content}}
</section>
</article>
</main>
{{/post}}

View File

@ -0,0 +1,35 @@
<link rel="stylesheet" href="{{asset "css/contact.css"}}">
{{#is "page" url="/contact/"}}
<div class="contact-form">
<form action="mailto:anydev.anyway@gmail.com" method="post" enctype="text/plain" id="contactForm">
<div class="form-row">
<div class="form-field">
<label for="name">Name:</label>
<input type="text" id="name" name="name" placeholder="Your name" required>
</div>
<div class="form-field">
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="Your email" required>
</div>
</div>
<div class="form-field">
<label for="message">Message:</label>
<textarea id="message" name="message" placeholder="Your message" required></textarea>
</div>
<button type="submit" class="submit-button" id="submitBtn" disabled>Send Message</button>
</form>
</div>
<script>
const form = document.getElementById('contactForm');
const submitBtn = document.getElementById('submitBtn');
form.addEventListener('input', () => {
const isFormFilled = form.name.value && form.email.value && form.message.value;
submitBtn.disabled = !isFormFilled;
});
</script>
{{/is}}

View File

@ -0,0 +1,25 @@
{{#if @site.members_enabled}}
{{#unless @member}}
{{#match @custom.header_style "!=" "Landing"}}
{{#match @custom.header_style "!=" "Search"}}
{{#match @custom.header_style "!=" "Off"}}
{{#match posts.length ">=" 7}}
<section class="gh-cta gh-outer">
<div class="gh-cta-inner gh-inner">
<div class="gh-cta-content">
<h2 class="gh-cta-title is-title">
{{#if @custom.signup_heading}}{{@custom.signup_heading}}{{else}}{{@site.title}}{{/if}}
</h2>
<p class="gh-cta-description is-body">
{{#if @custom.signup_subheading}}{{@custom.signup_subheading}}{{else}}{{@site.description}}{{/if}}
</p>
</div>
{{> "email-subscription" email_field_id="cta-email"}}
</div>
</section>
{{/match}}
{{/match}}
{{/match}}
{{/match}}
{{/unless}}
{{/if}}

View File

@ -0,0 +1,14 @@
{{#if showFeatured}}
{{#get "posts" filter="featured:true" include="authors" limit=limit as |featured|}}
<section class="gh-featured gh-outer">
<div class="gh-featured-inner gh-inner">
<h2 class="gh-featured-title">Featured</h2>
<div class="gh-featured-feed">
{{#foreach featured}}
{{> "post-card" imageSizes="80px"}}
{{/foreach}}
</div>
</div>
</section>
{{/get}}
{{/if}}

View File

@ -0,0 +1,35 @@
<footer class="gh-footer{{#match @custom.header_and_footer_color "Accent color"}} has-accent-color{{/match}} gh-outer">
<div class="gh-footer-inner gh-inner">
<div class="gh-footer-bar">
<span class="gh-footer-logo is-title">
{{#if @site.logo}}
<img src="{{@site.logo}}" alt="{{@site.title}}">
{{else}}
{{@site.title}}
{{/if}}
</span>
<nav class="gh-footer-menu">
{{navigation type="secondary"}}
</nav>
<div class="gh-footer-copyright">
Powered by <a href="https://ghost.org/" target="_blank" rel="noopener">Ghost</a>
</div>
</div>
{{#if @site.members_enabled}}
{{#unless @member}}
<section class="gh-footer-signup">
<h2 class="gh-footer-signup-header is-title">
{{#if @custom.signup_heading}}{{@custom.signup_heading}}{{else}}{{@site.title}}{{/if}}
</h2>
<p class="gh-footer-signup-subhead is-body">
{{#if @custom.signup_subheading}}{{@custom.signup_subheading}}{{else}}{{@site.description}}{{/if}}
</p>
{{> "email-subscription" email_field_id="footer-email"}}
</section>
{{/unless}}
{{/if}}
</div>
</footer>

View File

@ -0,0 +1,81 @@
<section class="gh-header is-{{#match headerStyle "Magazine"}}magazine{{else match headerStyle "Highlight"}}highlight{{else}}classic{{/match}}{{#if @custom.background_image}}{{#if @site.cover_image}} has-image{{/if}}{{/if}} gh-outer">
{{!-- Background image --}}
{{#if @custom.background_image}}
{{#match headerStyle "!=" "Magazine"}}
{{#match headerStyle "!=" "Highlight"}}
{{#if @site.cover_image}}
<img class="gh-header-image" src="{{@site.cover_image}}" alt="{{@site.title}}">
{{/if}}
{{/match}}
{{/match}}
{{/if}}
<div class="gh-header-inner gh-inner">
{{!-- Highlight layout --}}
{{#match headerStyle "Highlight"}}
<div class="gh-header-left">
{{#foreach posts limit="1"}}
{{> "post-card" imageSizes="(max-width: 767px) calc(100vw - max(8vmin, 40px)), 640px"}}
{{/foreach}}
</div>
<div class="gh-header-middle">
{{#foreach posts from="2" limit="3"}}
{{> "post-card"}}
{{/foreach}}
</div>
<div class="gh-header-right">
{{#if @custom.show_featured_posts}}
{{> "components/featured" showFeatured=@custom.show_featured_posts limit=6}}
{{else}}
<div class="gh-featured-feed">
{{#foreach posts from="5" limit="6"}}
{{> "post-card" imageSizes="80px"}}
{{/foreach}}
</div>
{{/if}}
</div>
{{/match}}
{{!-- Magazine layout --}}
{{#match headerStyle "Magazine"}}
{{#foreach posts limit="7"}}
{{#match @number 2}}
<div class="gh-header-left">
{{/match}}
{{#match @number 5}}
<div class="gh-header-right">
{{/match}}
{{#if @first}}
{{> "post-card" imageSizes="640px"}}
{{else}}
{{> "post-card"}}
{{/if}}
{{#match @number 4}}
</div>
{{/match}}
{{#match @number 7}}
</div>
{{/match}}
{{/foreach}}
{{/match}}
{{!-- Landing layout --}}
{{#match headerStyle "Landing"}}
<h1 class="gh-header-title is-title">{{#if @custom.header_text}}{{@custom.header_text}}{{else}}{{@site.description}}{{/if}}</h1>
{{> "email-subscription" email_field_id="header-email"}}
{{/match}}
{{!-- Search layout --}}
{{#match headerStyle "Search"}}
<h1 class="gh-header-title is-title">{{#if @custom.header_text}}{{@custom.header_text}}{{else}}{{@site.description}}{{/if}}</h1>
<form class="gh-form">
{{> "icons/search"}}
<button class="gh-form-input" data-ghost-search>Search posts, tags and authors</button>
</form>
{{/match}}
</div>
</section>

View File

@ -0,0 +1,19 @@
{{#match headerStyle "!=" "Off"}}
{{#match headerStyle "Highlight"}}
{{> "components/header-content"}}
{{else match headerStyle "Magazine"}}
{{> "components/header-content"}}
{{else}}
{{#match headerStyle "Landing"}}
{{#if @site.members_enabled}}
{{#unless @member}}
{{> "components/header-content"}}
{{/unless}}
{{/if}}
{{else}}
{{> "components/header-content"}}
{{/match}}
{{/match}}
{{/match}}

View File

@ -0,0 +1,26 @@
<link rel="stylesheet" href="{{asset "css/hero.css"}}">
<section class="hero">
<div class="hero-inner">
<!-- Left content with logo and text -->
<div class="hero-content">
<img src="assets/images/anyway_logo_black.png" alt="Logo" class="hero-logo">
<h1 class="hero-title">Our new app is available for download</h1>
<a href="https://anydev.info/download/" class="hero-button">Start now</a>
</div>
<!-- Right content with app screenshots -->
<div class="hero-screenshots">
<div class="screenshot">
<img src="assets/screenshots/home_1.jpg" alt="Screenshot 1">
</div>
<div class="screenshot">
<img src="assets/screenshots/home_2.jpg" alt="Screenshot 2">
</div>
<div class="screenshot">
<img src="assets/screenshots/home_3.jpg" alt="Screenshot 3">
</div>
</div>
</div>
</section>

View File

@ -0,0 +1,53 @@
<header id="gh-navigation" class="gh-navigation is-{{#match navigationLayout "Logo on the left"}}left-logo{{else match navigationLayout "Stacked"}}stacked{{else}}middle-logo{{/match}}{{#match @custom.header_and_footer_color "Accent color"}} has-accent-color{{/match}} gh-outer">
<div class="gh-navigation-inner gh-inner">
<div class="gh-navigation-brand">
<a class="gh-navigation-logo is-title" href="{{@site.url}}">
{{#if @site.logo}}
<img src="{{@site.logo}}" alt="{{@site.title}}">
{{else}}
{{@site.title}}
{{/if}}
</a>
{{> "search-toggle"}}
<button class="gh-burger gh-icon-button" aria-label="Menu">
{{> "icons/burger"}}
{{> "icons/close"}}
</button>
</div>
<nav class="gh-navigation-menu">
{{navigation}}
{{#unless @site.members_enabled}}
{{#match navigationLayout "Stacked"}}
{{> "search-toggle"}}
{{/match}}
{{/unless}}
</nav>
<div class="gh-navigation-actions">
{{#unless @site.members_enabled}}
{{^match navigationLayout "Stacked"}}
{{> "search-toggle"}}
{{/match}}
{{else}}
{{> "search-toggle"}}
<div class="gh-navigation-members">
{{#unless @member}}
{{#unless @site.members_invite_only}}
<a href="#/portal/signin" data-portal="signin">Sign in</a>
{{#unless hideSubscribeButton}}
<a class="gh-button" href="#/portal/signup" data-portal="signup">Subscribe</a>
{{/unless}}
{{else}}
<a class="gh-button" href="#/portal/signin" data-portal="signin">Sign in</a>
{{/unless}}
{{else}}
<a class="gh-button" href="#/portal/account" data-portal="account">Account</a>
{{/unless}}
</div>
{{/unless}}
</div>
</div>
</header>

View File

@ -0,0 +1,123 @@
{{!--
Parameters:
* feed (index, home, archive, recent)
* postFeedStyle (list, grid)
* showTitle (true, false)
* showSidebar (true, false)
--}}
<section class="gh-container is-{{#match postFeedStyle "List"}}list{{else}}grid{{/match}}{{#if showSidebar}} has-sidebar{{/if}}{{#unless @custom.show_images_in_feed}} no-image{{/unless}} gh-outer">
<div class="gh-container-inner gh-inner">
{{#if showTitle}}
<h2 class="gh-container-title">
{{#unless title}}Latest{{else}}{{title}}{{/unless}}
</h2>
{{/if}}
<main class="gh-main">
<div class="gh-feed">
{{!-- Homepage --}}
{{#match feed "home"}}
{{#match @custom.header_style "Highlight"}}
{{#if @custom.show_featured_posts}}
{{#match posts.length ">=" 4}}
{{#get "posts" include="authors" limit="16"}}
{{#foreach posts from="5"}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
{{/get}}
{{/match}}
{{else}}
{{#match posts.length ">=" 10}}
{{#get "posts" include="authors" limit="22"}}
{{#foreach posts from="11"}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
{{/get}}
{{/match}}
{{/if}}
{{else match @custom.header_style "Magazine"}}
{{#match posts.length ">=" 7}}
{{#get "posts" include="authors" limit="19"}}
{{#foreach posts from="8"}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
{{/get}}
{{/match}}
{{else}}
{{#get "posts" include="authors" limit="12"}}
{{#foreach posts}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
{{/get}}
{{/match}}
{{/match}}
{{!-- All posts --}}
{{#match feed "index"}}
{{#match pagination.page 2}}
{{#get "posts" include="authors" limit=@config.posts_per_page as |recent|}}
{{#foreach recent}}
{{> "post-card"}}
{{/foreach}}
{{/get}}
{{/match}}
{{#foreach posts}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
{{/match}}
{{!-- Tag and author pages --}}
{{#match feed "archive"}}
{{#foreach posts}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
{{/match}}
</div>
{{#match pagination.pages ">" 1}}
<div class="gh-more is-title">
<a href="{{@site.url}}/page/2">See all {{> "icons/arrow"}}</a>
</div>
{{/match}}
</main>
{{#if showSidebar}}
<aside class="gh-sidebar">
<div class="gh-sidebar-inner">
<section class="gh-about">
{{#if @site.icon}}
<img class="gh-about-icon" src="{{@site.icon}}" alt="{{@site.title}}" loading="lazy">
{{/if}}
<h3 class="gh-about-title is-title">{{@site.title}}</h3>
{{#if @site.description}}
<p class="gh-about-description is-body">{{@site.description}}</p>
{{/if}}
{{#if @site.members_enabled}}
{{#unless @member}}
<button class="gh-button" data-portal="signup">Subscribe</button>
{{else}}
{{#if @site.paid_members_enabled}}
{{#unless @member.paid}}
<button class="gh-button" data-portal="upgrade">Upgrade</button>
{{/unless}}
{{/if}}
{{/unless}}
{{/if}}
</section>
{{#if @site.recommendations_enabled}}
<section class="gh-recommendations">
<h4 class="gh-sidebar-title">Recommendations</h4>
{{recommendations}}
<button data-portal="recommendations">See all {{> "icons/arrow"}}</button>
</section>
{{/if}}
</div>
</aside>
{{/if}}
</div>
</section>

View File

@ -0,0 +1,9 @@
<form class="gh-form" data-members-form>
<input class="gh-form-input" id="{{email_field_id}}" name="email" type="email" placeholder="jamie@example.com" required data-members-email>
<button class="gh-button" type="submit" aria-label="Subscribe">
<span><span>Subscribe</span> {{> "icons/arrow"}}</span>
{{> "icons/loader"}}
{{> "icons/checkmark"}}
</button>
<p data-members-error></p>
</form>

View File

@ -0,0 +1,17 @@
{{#if feature_image}}
<figure class="gh-article-image">
<img
srcset="{{img_url feature_image size="s"}} 320w,
{{img_url feature_image size="m"}} 600w,
{{img_url feature_image size="l"}} 960w,
{{img_url feature_image size="xl"}} 1200w,
{{img_url feature_image size="xxl"}} 2000w"
sizes="(max-width: 1200px) 100vw, 1120px"
src="{{img_url feature_image size="xl"}}"
alt="{{title}}"
>
{{#if feature_image_caption}}
<figcaption>{{feature_image_caption}}</figcaption>
{{/if}}
</figure>
{{/if}}

1
partials/icons/arrow.hbs Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M224.49,136.49l-72,72a12,12,0,0,1-17-17L187,140H40a12,12,0,0,1,0-24H187L135.51,64.48a12,12,0,0,1,17-17l72,72A12,12,0,0,1,224.49,136.49Z"></path></svg>

After

Width:  |  Height:  |  Size: 264 B

View File

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

After

Width:  |  Height:  |  Size: 308 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128ZM40,72H216a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16ZM216,184H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Z"></path></svg>

After

Width:  |  Height:  |  Size: 282 B

View File

@ -0,0 +1,24 @@
<svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
<style>
.checkmark {
width: 40px;
height: 40px;
display: block;
stroke-width: 2.5;
stroke: currentColor;
stroke-miterlimit: 10;
}
.checkmark__check {
transform-origin: 50% 50%;
stroke-dasharray: 48;
stroke-dashoffset: 48;
animation: stroke .3s cubic-bezier(0.650, 0.000, 0.450, 1.000) forwards;
}
@keyframes stroke {
100% { stroke-dashoffset: 0; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 716 B

1
partials/icons/close.hbs Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path></svg>

After

Width:  |  Height:  |  Size: 313 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M23.9981 11.9991C23.9981 5.37216 18.626 0 11.9991 0C5.37216 0 0 5.37216 0 11.9991C0 17.9882 4.38789 22.9522 10.1242 23.8524V15.4676H7.07758V11.9991H10.1242V9.35553C10.1242 6.34826 11.9156 4.68714 14.6564 4.68714C15.9692 4.68714 17.3424 4.92149 17.3424 4.92149V7.87439H15.8294C14.3388 7.87439 13.8739 8.79933 13.8739 9.74824V11.9991H17.2018L16.6698 15.4676H13.8739V23.8524C19.6103 22.9522 23.9981 17.9882 23.9981 11.9991Z"/></svg>

After

Width:  |  Height:  |  Size: 531 B

3
partials/icons/fire.hbs Normal file
View File

@ -0,0 +1,3 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.49365 4.58752C3.53115 6.03752 2.74365 7.70002 2.74365 9.25002C2.74365 10.6424 3.29678 11.9778 4.28134 12.9623C5.26591 13.9469 6.60127 14.5 7.99365 14.5C9.38604 14.5 10.7214 13.9469 11.706 12.9623C12.6905 11.9778 13.2437 10.6424 13.2437 9.25002C13.2437 6.00002 10.9937 3.50002 9.16865 1.68127L6.99365 6.25002L4.49365 4.58752Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>

After

Width:  |  Height:  |  Size: 538 B

17
partials/icons/loader.hbs Normal file
View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
<g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
<g class="nc-loop-dots-4-24-icon-o">
<circle cx="4" cy="12" r="3"></circle>
<circle cx="12" cy="12" r="3"></circle>
<circle cx="20" cy="12" r="3"></circle>
</g>
<style data-cap="butt">
.nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
.nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
.nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
.nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
.nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
@keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
</style>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

1
partials/icons/lock.hbs Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" height="20" width="20" id="Lock-1--Streamline-Ultimate"><defs></defs><title>lock-1</title><path d="M4.375 8.125h11.25s1.25 0 1.25 1.25v8.75s0 1.25 -1.25 1.25H4.375s-1.25 0 -1.25 -1.25v-8.75s0 -1.25 1.25 -1.25" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M5.625 8.125V5a4.375 4.375 0 0 1 8.75 0v3.125" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m10 12.5 0 2.5" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 678 B

1
partials/icons/rss.hbs Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="6.18" cy="17.82" r="2.18"/><path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/></svg>

After

Width:  |  Height:  |  Size: 263 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>

After

Width:  |  Height:  |  Size: 248 B

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" fill="currentColor"><g><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path></g></svg>

After

Width:  |  Height:  |  Size: 231 B

41
partials/lightbox.hbs Normal file
View File

@ -0,0 +1,41 @@
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<div class="pswp__bg"></div>
<div class="pswp__scroll-wrap">
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title="Share"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)"></button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)"></button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>

47
partials/post-card.hbs Normal file
View File

@ -0,0 +1,47 @@
<article class="gh-card {{post_class}}{{#unless @custom.show_images_in_feed}} no-image{{/unless}}">
<a class="gh-card-link" href="{{url}}">
{{#if feature_image}}
<figure class="gh-card-image">
<img
srcset="{{img_url feature_image size="xs" format="webp"}} 160w,
{{img_url feature_image size="s" format="webp"}} 320w,
{{img_url feature_image size="m" format="webp"}} 600w,
{{img_url feature_image size="l" format="webp"}} 960w,
{{img_url feature_image size="xl" format="webp"}} 1200w,
{{img_url feature_image size="xxl" format="webp"}} 2000w"
sizes="{{#if imageSizes}}{{imageSizes}}{{else}}320px{{/if}}"
src="{{img_url feature_image size="m"}}"
alt="{{#if feature_image_alt}}{{feature_image_alt}}{{else}}{{title}}{{/if}}"
{{#if lazyLoad}}loading="lazy"{{/if}}
>
</figure>
{{/if}}
<div class="gh-card-wrapper">
{{#if primary_tag}}
<p class="gh-card-tag">{{primary_tag.name}}</p>
{{/if}}
<h3 class="gh-card-title is-title">{{title}}</h3>
{{#if custom_excerpt}}
<p class="gh-card-excerpt is-body">{{custom_excerpt}}</p>
{{/if}}
{{#unless custom_excerpt}}
{{#if excerpt}}
<p class="gh-card-excerpt is-body">{{excerpt}}</p>
{{/if}}
{{/unless}}
<footer class="gh-card-meta">
{{#unless access}}
{{^has visibility="public"}}
{{> "icons/lock"}}
{{/has}}
{{/unless}}<!--
-->{{#if @custom.show_author}}
<span class="gh-card-author">By {{#foreach authors}}{{#if @first}}{{name}}{{else}}, {{name}}{{/if}}{{/foreach}}</span>
{{/if}}
{{#if @custom.show_publish_date}}
<time class="gh-card-date" datetime="{{date format="YYYY-MM-DD"}}">{{date}}</time>
{{/if}}<!--
--></footer>
</div>
</a>
</article>

View File

@ -0,0 +1,3 @@
<button class="gh-search gh-icon-button" aria-label="Search this site" data-ghost-search>
{{> "icons/search"}}
</button>

View File

@ -0,0 +1,15 @@
{{!-- Sans-serif font is always loaded, because it's the default font for some part of the theme regardless of the settings --}}
{{> "typography/sans"}}
{{#match @custom.title_font "Modern sans-serif"}}
{{#match @custom.body_font "Elegant serif"}}
{{> "typography/serif"}}
{{/match}}
{{else match @custom.title_font "Elegant serif"}}
{{> "typography/serif"}}
{{else}}
{{> "typography/mono"}}
{{#match @custom.body_font "Elegant serif"}}
{{> "typography/serif"}}
{{/match}}
{{/match}}

View File

@ -0,0 +1,21 @@
<link rel="preload" as="font" type="font/woff2" href="{{asset "fonts/jetbrains-mono-roman.woff2"}}" crossorigin="anonymous">
<link rel="preload" as="font" type="font/woff2" href="{{asset "fonts/jetbrains-mono-italic.woff2"}}" crossorigin="anonymous">
<style>
@font-face {
font-family: "JetBrains Mono";
font-style: normal;
font-weight: 100 800;
font-display: optional;
src: url({{asset "fonts/jetbrains-mono-roman.woff2"}}) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "JetBrains Mono";
font-style: italic;
font-weight: 100 800;
font-display: optional;
src: url({{asset "fonts/jetbrains-mono-italic.woff2"}}) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
</style>

View File

@ -0,0 +1,11 @@
<link rel="preload" as="font" type="font/woff2" href="{{asset "fonts/inter-roman.woff2"}}" crossorigin="anonymous">
<style>
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 100 900;
font-display: optional;
src: url({{asset "fonts/inter-roman.woff2"}}) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
</style>

View File

@ -0,0 +1,21 @@
<link rel="preload" as="font" type="font/woff2" href="{{asset "fonts/eb-garamond-roman.woff2"}}" crossorigin="anonymous">
<link rel="preload" as="font" type="font/woff2" href="{{asset "fonts/eb-garamond-italic.woff2"}}" crossorigin="anonymous">
<style>
@font-face {
font-family: "EB Garamond";
font-style: normal;
font-weight: 400 800;
font-display: optional;
src: url({{asset "fonts/eb-garamond-roman.woff2"}}) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "EB Garamond";
font-style: italic;
font-weight: 400 800;
font-display: optional;
src: url({{asset "fonts/eb-garamond-italic.woff2"}}) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
</style>

80
post.hbs Normal file
View File

@ -0,0 +1,80 @@
{{!< default}}
{{!-- The tag above means: insert everything in this file into the body of the default.hbs template --}}
{{#post}}
<main class="gh-main">
<article class="gh-article {{post_class}}">
<header class="gh-article-header gh-canvas">
{{#if primary_tag}}
<a class="gh-article-tag" href="{{primary_tag.url}}">{{primary_tag.name}}</a>
{{/if}}
<h1 class="gh-article-title is-title">{{title}}</h1>
{{#if custom_excerpt}}
<p class="gh-article-excerpt is-body">{{custom_excerpt}}</p>
{{/if}}
{{#if @custom.show_post_metadata}}
<div class="gh-article-meta">
<div class="gh-article-author-image instapaper_ignore">
{{#foreach authors}}
{{#if profile_image}}
<a href="{{url}}">
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}">
</a>
{{else}}
<a href="{{url}}">{{> "icons/avatar"}}</a>
{{/if}}
{{/foreach}}
</div>
<div class="gh-article-meta-wrapper">
<h4 class="gh-article-author-name">{{authors}}</h4>
<div class="gh-article-meta-content">
<time class="gh-article-meta-date" datetime="{{date format="YYYY-MM-DD"}}">{{date}}</time>
{{#if reading_time}}
<span class="gh-article-meta-length"><span class="bull">—</span> {{reading_time}}</span>
{{/if}}
</div>
</div>
</div>
{{/if}}
{{> "feature-image"}}
</header>
<section class="gh-content gh-canvas is-body{{#if @custom.enable_drop_caps_on_posts}} drop-cap{{/if}}">
{{content}}
</section>
</article>
{{#if comments}}
<div class="gh-comments gh-canvas">
{{comments}}
</div>
{{/if}}
</main>
{{/post}}
{{#if @custom.show_related_articles}}
{{#get "posts" include="authors" filter="id:-{{post.id}}" limit="4" as |next|}}
{{#if next}}
<section class="gh-container is-grid gh-outer">
<div class="gh-container-inner gh-inner">
<h2 class="gh-container-title">Read more</h2>
<div class="gh-feed">
{{#foreach next}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
</div>
</div>
</section>
{{/if}}
{{/get}}
{{/if}}

22
tag.hbs Normal file
View File

@ -0,0 +1,22 @@
{{!< default}}
{{!-- The tag above means: insert everything in this file into the body of the default.hbs template --}}
<main class="gh-main gh-outer">
{{#tag}}
<section class="gh-archive{{#if feature_image}} has-image{{/if}}{{#if @custom.show_publication_info_sidebar}} has-sidebar{{/if}} gh-inner">
<div class="gh-archive-inner">
<header class="gh-archive-wrapper">
<h1 class="gh-article-title is-title">{{name}}</h1>
{{#if description}}
<p class="gh-article-excerpt">{{description}}</p>
{{/if}}
</header>
{{> "feature-image"}}
</div>
</section>
{{/tag}}
{{> "components/post-list" feed="archive" postFeedStyle=@custom.post_feed_style showTitle=false showSidebar=@custom.show_publication_info_sidebar}}
</main>