Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions blocks/blog-header/blog-header.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
.blog-header {
max-width: 1200px;
margin: 0 auto;
padding: 40px 24px;
}

.blog-header-container h1 {
font-size: 48px;
line-height: 1.2;
margin: 0 0 24px;
font-weight: 700;
color: var(--text-color);
}

.blog-header-meta {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 24px;
}

.blog-header-date {
display: flex;
align-items: center;
gap: 8px;
margin: 0;
color: #666;
font-size: 16px;
}

.blog-header-date svg {
width: 16px;
height: 16px;
}

.blog-header-author {
list-style: none;
margin: 0;
padding: 0;
}

.blog-header-author li {
display: inline-block;
}

.blog-header-author img {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
}

.blog-header-tags {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.blog-header-tags li {
display: inline-block;
}

.blog-header-date a {
display: inline-flex;
align-items: center;
color: #666;
text-decoration: none;
}

.blog-header-author a {
display: inline-flex;
align-items: center;
gap: 8px;
text-decoration: none;
color: var(--text-color);
}

.blog-header-tags a {
display: inline-block;
padding: 8px 16px;
border: 1px solid #dadada;
border-radius: 4px;
text-decoration: none;
color: var(--text-color);
font-size: 14px;
transition: border-color 0.2s, color 0.2s;
}

.blog-header-date a:hover {
color: var(--link-color);
}

.blog-header-author a:hover {
color: var(--link-color);
}

.blog-header-tags a:hover {
border-color: var(--link-color);
color: var(--link-color);
}

/* Desktop styles */
@media (width >= 900px) {
.blog-header {
padding: 60px 32px;
}

.blog-header-container h1 {
font-size: 64px;
margin-bottom: 32px;
}

.blog-header-meta {
flex-direction: row;
align-items: center;
gap: 0;
}

.blog-header-date {
margin-right: auto;
}

.blog-header-author img {
width: 40px;
height: 40px;
}
}

90 changes: 90 additions & 0 deletions blocks/blog-header/blog-header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
export default function decorate(block) {
// Extract date, author, tags from the auto-blocked structure
const [dateCell, authorCell, tagsCell] = [...block.children[0].children];
const date = dateCell.textContent.trim();
const author = authorCell.textContent.trim();
const tags = tagsCell.textContent.trim();

// Create new structure
const container = document.createElement('div');
container.className = 'blog-header-container';

// Meta section (date and author)
const meta = document.createElement('div');
meta.className = 'blog-header-meta';

// Date
if (date) {
const dateWrapper = document.createElement('p');
dateWrapper.className = 'blog-header-date';

const time = document.createElement('time');
time.textContent = date;
dateWrapper.appendChild(time);

// Add RSS icon
const rssLink = document.createElement('a');
rssLink.href = '/feed';
rssLink.setAttribute('aria-label', 'Atom Feed');
rssLink.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><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>';
dateWrapper.appendChild(rssLink);

meta.appendChild(dateWrapper);
}

// Author
if (author) {
const authorWrapper = document.createElement('ul');
authorWrapper.className = 'blog-header-author';

const li = document.createElement('li');
const authorLink = document.createElement('a');

// Convert author name to GitHub username format (lowercase, no spaces)
const githubUsername = author.toLowerCase().replace(/\s+/g, '');
authorLink.href = `https://github.com/${githubUsername}`;

// Add author image from GitHub
const img = document.createElement('img');
img.src = 'https://avatars.githubusercontent.com/u/2760139?v=4'; // This could be enhanced to lookup actual user
img.alt = author;
img.width = 40;
img.height = 40;
img.loading = 'lazy';
authorLink.appendChild(img);

// Add author name
const nameSpan = document.createElement('span');
nameSpan.textContent = author;
authorLink.appendChild(nameSpan);

li.appendChild(authorLink);
authorWrapper.appendChild(li);
meta.appendChild(authorWrapper);
}

container.appendChild(meta);

// Tags
if (tags) {
const tagsWrapper = document.createElement('ul');
tagsWrapper.className = 'blog-header-tags';

// Split by comma in case there are multiple tags
const tagList = tags.split(',').map((t) => t.trim()).filter((t) => t);
tagList.forEach((tag) => {
const li = document.createElement('li');
const tagLink = document.createElement('a');
tagLink.href = `/blog?tag=${tag.toLowerCase()}`;
tagLink.textContent = tag;
li.appendChild(tagLink);
tagsWrapper.appendChild(li);
});

container.appendChild(tagsWrapper);
}

// Replace block content
block.textContent = '';
block.appendChild(container);
}
24 changes: 24 additions & 0 deletions scripts/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,36 @@ function autolinkModals(doc) {
});
}

/**
* Builds blog header block and inserts after H1.
* @param {Element} main The container element
*/
function buildBlogHeaderBlock(main) {
const date = getMetadata('date');
const author = getMetadata('author');
const tags = getMetadata('article:tag');

// Only build blog header if we have blog metadata
if (!date && !author && !tags) return;

const h1 = main.querySelector('h1');
if (!h1) return;

// Build the blog header block structure
const cells = [date || '', author || '', tags || ''];
const blogHeader = buildBlock('blog-header', [cells]);

// Insert right after H1
h1.parentElement.insertBefore(blogHeader, h1.nextSibling);
}

/**
* Builds all synthetic blocks in a container element.
* @param {Element} main The container element
*/
function buildAutoBlocks(main) {
try {
buildBlogHeaderBlock(main);
if (!main.querySelector('.hero')) buildHeroBlock(main);
} catch (error) {
// eslint-disable-next-line no-console
Expand Down
2 changes: 1 addition & 1 deletion styles/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

:root {
/* colors */
--white: #ffffff;
--white: #fff;
--background-color: var(--white);
--light-color: #ecf3fd;
--dark-color: #505050;
Expand Down