Wrangle up some CSS

pull/1/head
Starbeamrainbowlabs 2021-08-09 17:50:20 +01:00
parent 9dcee48717
commit 73a1576233
No known key found for this signature in database
GPG Key ID: 1BE5172E637709C2
6 changed files with 313 additions and 17 deletions

View File

@ -32,13 +32,12 @@ async function filter_asset(src) {
let type = await filetype.fromBuffer(content);
if(typeof type === "undefined") {
// Failed, try to extract from the URL
let match = src.match(/(?<=\.)[a-zA-Z0-9-_]$/);
let match = src.match(/(?<=\.)[a-zA-Z0-9-_]+$/);
// Failed, just go with no file type extension at all
if(match === null) match = [ null, "" ];
type = { ext: match[1] }
type = { ext: match[0] };
}
if(type.ext.length > 0) type.ext = `.${type.ext}`;
// It's a URL - download it
filename = `${hash(src)}${type.ext}`;
await fs.promises.writeFile(

View File

@ -37,23 +37,25 @@ description: "Hull Blogs provides aggregated content from University of Hull stu
</a></h1>
<p>{{ tagline }}</p>
</header>
<nav class="shadow-bottom">
<nav>
<ul>
{% for navitem in collections.navigable %}
<li {% if page.url == navitem.url %}aria-current="page"{% endif %}>
<a href="{{ navitem.url }}" class="nav">{{ navitem.data.title }}</a>
<a href="{{ navitem.url }}" class="nav invisilink">{{ navitem.data.title }}</a>
</li>
{% endfor %}
<li><a href="https://github.com/sbrl/Minetest-WorldEditAdditions/" class="nav image"><img src="{% asset 'images/github.svg' %}" alt="GitHub" title="GitHub" /></a></li>
<li><a href="https://github.com/sbrl/Minetest-WorldEditAdditions/" class="nav image"><img class="large-icon invert-when-light" src="{% asset 'images/github.svg' %}" alt="GitHub" title="GitHub" /></a></li>
</ul>
</nav>
{{ content | safe }}
<main>
{{ content | safe }}
</main>
<footer class="shadow-top">
<p>hullblogs.com built with ❤️ by <a href="https://starbeamrainbowlabs.com/"><img src="{% asset 'https://starbeamrainbowlabs.com/images/sbrl/SBRL-Small-200.png' %}" alt="Starbeamrainbowlabs' logo" aria-hidden="true" />Starbeamrainbowlabs</p>
<p>hullblogs.com built with ❤️ by <a href="https://starbeamrainbowlabs.com/"><img class="large-icon" src="{% asset 'https://starbeamrainbowlabs.com/images/sbrl/SBRL-Small-200.png' %}" alt="Starbeamrainbowlabs' logo" aria-hidden="true" />Starbeamrainbowlabs</p>
<p>Tech: <a href="https://www.11ty.dev/">Eleventy</a> (this website), <a href="https://www.heropatterns.com/">Hero Patterns</a> (background patterns), <a href="https://github.com/shssoichiro/oxipng">Oxipng</a> (PNG image compression), <a href="https://www.npmjs.com/package/feedme"><code>feedme</code></a> (for prsing feeds)</p>
<p>Tech: <a href="https://www.11ty.dev/">Eleventy</a> (this website), <a href="https://useiconic.com/open">Open Iconic</a> (icons)<!--, <a href="https://www.heropatterns.com/">Hero Patterns</a> (background patterns)-->, <a href="https://github.com/shssoichiro/oxipng">Oxipng</a> (PNG image compression), <a href="https://www.npmjs.com/package/feedme"><code>feedme</code></a> (for prsing feeds)</p>
<p>Licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache Licence 2.0</a> (<a href="https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)">tldr</a>)</p>
</footer>
</body>

View File

@ -0,0 +1,255 @@
/* Base CSS */
/*
* This CSS file contains (for me) logical style defaults that are easy to read.
*
* This file is quite often used as a starting point for other projects.
*
* Todo:
* <button>
* <inputs>
* <progress>
* <meter>
*/
:root {
--bg-main: #f3f3f3;
--bg-bright: #fe6330;
--text-main: #232323;
--text-alt: #515151;
--shadow: hsla(0, 0%, 25%, 0.2);
}
@media (prefers-color-scheme: dark) {
:root {
--bg-main: #232323;
--text-main: #f3f3f3;
--text-alt: #f3f3f3;
--shadow: hsla(0, 0%, 100%, 0.2);
}
.invert-when-dark {
filter: invert(100%);
}
}
@media not (prefers-color-scheme: dark) {
.invert-when-light {
filter: invert(100%);
}
}
/* rem is relative to the html element, and em is relative to the current element. */
html, body { font-size: 100%; margin: 0; padding: 0; }
body
{
font-family: "Open Sans", "Roboto", "Helvetica", sans-serif; /* Serif is awful :( */
background: var(--bg-main); /* Don't forget to update the @page one too for paged media */
color: var(--text-main);
display: grid;
grid-template-columns: 10% auto 10%;
grid-template-rows: auto auto auto auto;
grid-template-areas: "header header header"
"nav nav nav"
". content ."
"footer footer footer";
}
title { string-set: page-title content(text); }
/* Special tweaks for paged media (e.g. PDFs) */
@page {
font-family: sans-serif;
background: var(--bg-main); /* Set the background colour to cover the whole page */
@top-left {
content: string(page-title);
opacity: 0.6;
}
@top-right {
content: "Hull Blogs";
opacity: 0.6;
}
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
opacity: 0.6;
}
}
header {
grid-area: header;
background: var(--text-main);
color: var(--bg-bright);
text-align: center;
}
body > nav {
grid-area: nav;
background: var(--text-main);
color: var(--bg-bright);
}
body > nav > ul {
margin: 0;
padding: 0;
list-style-type: none;
font-size: 1.25em; font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
}
body > nav > ul > li {
padding: 0.5em;
}
main {
grid-area: content;
}
.posts {
margin: 2em 0;
}
.posts > :nth-child(even) {
background: var(--shadow);
}
.post {
--margin-vertical-content: 1rem;
margin: 2em 0;
padding: 0 var(--margin-vertical-content) 0 0;
display: grid;
grid-template-columns: 25% auto;
grid-template-rows: auto auto auto;
grid-template-areas: "image title"
"image content"
"image footer";
}
.post > h2 { margin: var(--margin-vertical-content) 0 0 0; grid-area: title; }
.post > .post-image {
grid-area: image;
/* HACK: Using !important should be done *very* sparingly */
background-size: cover !important;
background-position: center center !important;
background-repeat: no-repeat !important;
margin-right: 1em;
}
.post > .post-extract {
grid-area: content;
margin: 1em 0;
}
.post > .post-footer {
grid-area: footer;
margin: 0 0 var(--margin-vertical-content) 0;
}
nav.pagination {
margin: 5em 0 0 0;
display: flex;
justify-content: center;
align-items: center;
background: var(--text-alt);
color: var(--bg-bright);
}
nav.pagination > span {
margin: 0.5em;
}
nav.paginatioon > span[aria-current] {
font-weight: bolder;
font-size: 1.25em;
}
footer {
padding-bottom: 3em;
grid-area: footer;
background: var(--bg-bright);
color: var(--text-alt);
text-align: center;
}
a:not(.invisilink) { font-weight: bold; color: var(--bg-bright); }
a:not(.invisilink):hover { color: hsl(14, 99%, 52%); }
a:not(.invisilink):active { color: hsl(14, 99%, 46%); }
a:not(.invisilink):visited { color: hsl(14, 98%, 40%); }
/* A small tweak to things more responsive */
iframe, object, embed, img, table, video, audio
{
max-width: 100%;
}
/* Turn the user's cursor into a hand when over things they can click */
button, summary
{
cursor: pointer;
}
th, td
{
margin: 4px 6px;
padding: 4px 6px;
}
pre { page-break-inside: avoid; break-inside: avoid; }
pre, code {
white-space: pre-wrap;
-moz-tab-size: 4;
tab-size: 4;
}
/* todo add the rest of the textbox like inputs here */
input[type=text], input[type=number], textarea
{
margin: 3px 5px;
padding: 5px 8px;
background: var(--bg-bright);
border: 0;
border-radius: 5px;
}
/* Utility / layout aids */
.float.left { float: left; }
.float.right { float: right; }
.flex { display: flex; }
.flex-1 { flex: 1; }
.flex-2 { flex: 2; }
.flex-3 { flex: 3; }
.flex-4 { flex: 4; }
.flex-5 { flex: 5; }
.flex-6 { flex: 6; }
.small-spacing { margin: 0.25em 0.35em; padding: 0.25em 0.35em; }
.med-spacing { margin: 0.45em 0.65em; padding: 0.45em 0.65em; }
.high-spacing { margin: 1em 1.25em; padding: 1em 1.25em; }
.text-left { text-align: left; }
.text-centre { text-align: center; }
.text-right { text-align: right; }
.small-text { font-size: 0.8rem; }
.medium-text { font-size: 1rem; }
.large-text { font-size: 1.3rem; }
.bold-text { font-weight: bolder; }
.block { display: block; }
.inline { display: inline; }
.inline.block { display: inline-block; }
.invisilink { text-decoration: none; color: inherit; }
.invisilist { list-style-type: none; margin: 5px; padding: 5px; }
.large-icon { max-width: 1.5em; max-height: 1.5em; vertical-align: middle; }
.tiny-image { max-width: 5em; max-height: 5em; }
.small-image { max-width: 10em; max-height: 10em; }
.medium-image { max-width: 20em; max-height: 20em; }
.large-image { max-width: 30em; max-height: 30emx; }
.img-text-middle{ vertical-align: middle; }

3
src/images/clock.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M4 0c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 1c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm-.5 1v2.22l.16.13.5.5.34.38.72-.72-.38-.34-.34-.34v-1.81h-1z" />
</svg>

After

Width:  |  Height:  |  Size: 272 B

View File

@ -62,13 +62,13 @@ module.exports = async function() {
let temp_image = item.content.match(/<img[^>]+?\bsrc=["']([^>]+?)["'][^>]+?\/?>/);
if(temp_image !== null)
item.media_image = temp_image[1];
console.log(temp_image);
}
console.log(`MEDIA IMAGE`, item.media_image);
return item;
})));
global.feed_items.sort((a, b) => b.pubdate_obj - a.pubdate_obj);
// console.log(feed_items.map(el => el.title));
}
return {

View File

@ -4,14 +4,17 @@
{% for post in pagination.items %}
<article class="post">
<h2><a href="{{ post.link }}">{{ post.title | striphtml }}</a></h2>
<img src="{% asset post.media_image %}" alt="">
<div class="post-image" style="background: url({% asset post.media_image %});"></div>
<div class="post-extract">
{{ post.description | striphtml }}…
<span><a href="{{ post.link }}">Read more</a></span>
<div><a href="{{ post.link }}">Read more</a></div>
</div>
<div>
<span><a href="{{ post.parent.data.link | htmlentities }}"><img src="{{ post.author_image }}" alt="{{ post.author_name | htmlentities }} avatar" aria-hidden="true" /> {{ post.author_name | htmlentities }}</a></span>
<span><time datetime="{{ post.pubdate_iso }}">{{ post.pubdate_display | htmlentities }}</time></span>
<div class="post-footer">
<span><a href="{{ post.parent.data.link | htmlentities }}"><img class="large-icon" src="{{ post.author_image }}" alt="{{ post.author_name | htmlentities }} avatar" aria-hidden="true" /> {{ post.author_name | htmlentities }}</a></span>
<span>
<img class="large-icon open-iconic invert-when-dark" src="{% asset 'images/clock.svg' %}" alt="Clock icon" aria-hidden="hidden" />
<time datetime="{{ post.pubdate_iso }}">{{ post.pubdate_display | htmlentities }}</time>
</span>
</div>
</article>
@ -19,16 +22,50 @@
</section>
<nav>
<nav class="pagination">
<span>{% if page.url != pagination.href.first %}<a href="{{ pagination.href.first }}">First</a>{% else %}First{% endif %}</span>
<span>{% if pagination.href.previous %}<a href="{{ pagination.href.previous }}">Previous</a>{% else %}Previous{% endif %}</span>
{% for pageKey in pagination.pages %}
<a href="{{ pagination.hrefs[ loop.index0 ] }}"{% if pagination.hrefs[ loop.index0 ] == page.url %} aria-current="page"{% endif %}>{{ loop.index }}</a>
<span><a href="{{ pagination.hrefs[ loop.index0 ] }}"{% if pagination.hrefs[ loop.index0 ] == page.url %} aria-current="page"{% endif %}>{{ loop.index }}</a></span>
{% endfor %}
<span>{% if pagination.href.next %}<a href="{{ pagination.href.next }}">Next</a>{% else %}Next{% endif %}</span>
<span>{% if page.url != pagination.href.last %}<a href="{{ pagination.href.last }}">Last</a>{% else %}Last{% endif %}</span>
</nav>
<script async defer>
/**
* Renders a given number of seconds as something that humans can understand more easily.
* @package core
* @param int seconds The number of seconds to render.
* @return string The rendered time.
*/
function human_time(seconds)
{
const tokens = [
{ unit: 31536000, text: 'year' },
{ unit: 2592000, text: 'month' },
{ unit: 604800, text: 'week' },
{ unit: 86400, text: 'day' },
{ unit: 3600, text: 'hour' },
{ unit: 60, text: 'minute' },
{ unit: 1, text: 'second' }
];
for (const { unit, text } of tokens) {
if (seconds < unit) continue;
let numberOfUnits = Math.floor(seconds / unit);
return `${numberOfUnits} ${text}${numberOfUnits>1?'s':''} ago`;
}
}
window.addEventListener("load", (event) => {
let els_time = document.querySelectorAll("time");
for(let i = 0; i < els_time.length; i++) {
let time_next = new Date(els_time[i].getAttribute("datetime"));
els_time[i].textContent = human_time((new Date() - time_next) / 1000);
}
})
</script>