Fixed some bugs in the newsletter images, made some minor improvements in other areas. Added in unsubscribe functionality #52

Merged
rodude123 merged 1 commits from bug-fixes-and-unsubscribe into master 2023-12-13 03:11:40 +00:00
15 changed files with 138 additions and 63 deletions
Showing only changes of commit a5f17a70ed - Show all commits

View File

@ -296,17 +296,18 @@ class blogData
$latest = $this->getLatestBlogPost();
$prevTitle = $latest["title"];
$prevAbstract = $latest["abstract"];
$prevHeaderImage = $latest["headerImg"];
$prevHeaderImage = substr($latest["headerImg"], 10);
$prevHeaderImage = str_ireplace("%2F", "/", $prevHeaderImage);
$headerImage = $targetFile["imgLocation"];
$headerImage = rawurlencode("../" . $targetFile["imgLocation"]);
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, abstract, body, bodyText, categories, keywords, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :abstract, :body, :bodyText, :categories, :keywords, :folderID);");
$stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated);
$isFeatured = $featured ? 1 : 0;
$stmt->bindParam(":featured", $isFeatured);
// $isFeatured = $featured ? 1 : 0;
$stmt->bindParam(":featured", $featured);
$stmt->bindParam(":headerImg", $headerImage);
$stmt->bindParam(":abstract", $abstract);
$stmt->bindParam(":body", $newBody);
@ -323,6 +324,8 @@ class blogData
$stmtEmails = $conn->prepare("SELECT email FROM newsletter;");
$stmtEmails->execute();
$emails = $stmtEmails->fetchAll(PDO::FETCH_ASSOC);
$headerImage = substr($headerImage, 10);
$headerImage = str_ireplace("%2F", "/", $headerImage);
$emailBody = <<<EOD
<!DOCTYPE html>
@ -533,7 +536,7 @@ class blogData
<div class="postContainer">
<h2>latest post</h2>
<div class="image">
<img src="$headerImage" alt="header image of the latest post">
<img src="https://rohitpai.co.uk/$headerImage" alt="header image of the latest post">
</div>
<div class="post">
@ -546,7 +549,7 @@ class blogData
<div class="postContainer">
<h2>in case you missed the previous post</h2>
<div class="image">
<img src="$prevHeaderImage" alt="header image of the previous post">
<img src="https://rohitpai.co.uk/$prevHeaderImage" alt="header image of the previous post">
</div>
<div class="post">
@ -556,21 +559,26 @@ class blogData
</div>
</div>
</main>
EOD;
foreach ($emails as $email)
{
$emailFooter = <<<EOD
<footer>
<div class="nav">
<ul>
<li><a href="https://rohitpai.co.uk/blog">&lt;https://rohitpai.co.uk/blog&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe">&lt;Unsubscribe&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe/$email">&lt;Unsubscribe&gt;</a></li>
</ul>
</div>
<div class="date">2023</div>
</footer>
</body>
</html>
EOD;
EOD;
$emailBody .= $emailFooter;
foreach ($emails as $email)
{
$this->sendMail($email["email"], $emailBody, "Hey, Rohit's blog has a new post!");
}
@ -642,7 +650,8 @@ EOD;
unlink($result["headerImg"]);
$stmt = $conn->prepare("UPDATE blog SET headerImg = :headerImg WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$location = urldecode("../" . $targetFile["imgLocation"]);
$stmt->bindParam(":headerImg", $location);
$stmt->execute();
if ($stmt->rowCount() > 0)
{
@ -1168,7 +1177,7 @@ EOD;
<div class="nav">
<ul>
<li><a href="https://rohitpai.co.uk/blog">&lt;https://rohitpai.co.uk/blog&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe">&lt;Unsubscribe&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe/$email">&lt;Unsubscribe&gt;</a></li>
</ul>
</div>
<div class="date">2023</div>

View File

@ -282,17 +282,18 @@ class blogRoutes implements routesInterface
if ($message === "email not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, email not found")));
$response->getBody()->write(json_encode(array("message" => "Woah, you're already trying to leave without signing up?")));
return $response->withStatus(404);
}
if ($message === "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, something went wrong")));
$response->getBody()->write(json_encode(array("message" => "Error, something went wrong")));
return $response->withStatus(500);
}
$response->getBody()->write(json_encode(array("message" => "Sorry to see you go! You'll no longer receive any emails from me. If you change your mind, you can always sign up again.")));
return $response;
});
@ -324,8 +325,8 @@ class blogRoutes implements routesInterface
$headerImg = null;
}
$featured = $data["featured"] === "true";
$insertedID = $this->blogData->createPost($data["title"], $data["abstract"], $data["body"], $data["bodyText"], $data["dateCreated"], $featured, $data["categories"], $headerImg);
// $featured = $data["featured"] === "true";
$insertedID = $this->blogData->createPost($data["title"], $data["abstract"], $data["body"], $data["bodyText"], $data["dateCreated"], intval($data["featured"]), $data["categories"], $headerImg);
if (!is_int($insertedID))
{
// uh oh something went wrong

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Rohit Pai - Blog</title><meta name="title" content="Rohit Pai - Blog"><meta name="description" content="This is all the blog posts that Rohit Pai has posted. You'll find posts on various topics, mostly on tech but some on various other random topics."><meta name="keywords" content="Blog, all posts, rohit, pai, rohit pai, tech, web development, self-hosting, hosting"><meta name="robots" content="index, follow"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="language" content="English"><meta name="author" content="Rohit Pai"><link rel="stylesheet" href="/blog/css/main.css"><script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script><script type="text/javascript" src="https://platform-api.sharethis.com/js/sharethis.js#property=6550cdc47a115e0012964576&product=sop" async="async"></script></head><body><nav><input type="checkbox" id="nav-check"><h1><a href="/" class="link">rohit pai</a></h1><div class="nav-btn"><label for="nav-check"><span></span> <span></span> <span></span></label></div><ul><li><a href="/#about" class="textShadow link">about</a></li><li><a href="/#curriculumVitae" class="textShadow link">cv</a></li><li><a href="/#projects" class="textShadow link">projects</a></li><li><a href="/#contact" class="textShadow link">contact</a></li><li><a href="/blog" class="textShadow link active">blog</a></li></ul></nav><header><div><h1>full stack developer</h1><a href="/#sayHello" class="btn btnPrimary boxShadowIn boxShadowOut">Contact Me</a> <a href="" id="arrow"><i class="fa-solid fa-chevron-down"></i></a></div></header><div class="menuBar"><div class="menu"><ul><li><a href="/blog" class="link active">All posts</a></li><li><a href="/blog/category" class="link">categories</a></li><li><label for="searchField" aria-hidden="true" hidden>search</label> <input type="search" name="search" id="searchField" placeholder="Search..."> <button type="submit" id="searchBtn" class="btn btnPrimary"><i class="fa fa-search"></i></button></li></ul></div></div><main id="main"></main><div class="modal-container" id="cookiePopup"><div class="modal"><div class="modal-content"><h2><i class="fas fa-cookie-bite"></i> Hey I use cookies btw</h2><p>Just to let you know, I use cookies to give you the best experience on my blog. By clicking agree I'll assume that you are happy with it. <a href="/blog/policy/cookie" class="link">Read more</a></p><div class="flexRow"><button class="btn btnPrimary" id="cookieAccept">agree</button> <a href="https://google.co.uk" class="btn btnPrimary">disagree</a></div></div></div></div><footer class="flexRow"><div class="nav"><ul><li><a href="/blog/policy/privacy" class="link">privacy policy</a></li><li><a href="/blog/policy/cookie" class="link">cookie policy</a></li></ul></div><p>&copy; <span id="year"></span> Rohit Pai all rights reserved</p><div class="button"><button id="goBackToTop"><i class="fa-solid fa-chevron-up"></i></button></div></footer><script src="/js/typewriter.js"></script><script src="/blog/js/index.js"></script><script id="dsq-count-scr" src="https://rohitpaiportfolio.disqus.com/count.js" async></script></body></html>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Rohit Pai - Blog</title><meta name="title" content="Rohit Pai - Blog"><meta name="description" content="This is all the blog posts that Rohit Pai has posted. You'll find posts on various topics, mostly on tech but some on various other random topics."><meta name="keywords" content="Blog, all posts, rohit, pai, rohit pai, tech, web development, self-hosting, hosting"><meta name="robots" content="index, follow"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="language" content="English"><meta name="author" content="Rohit Pai"><link rel="stylesheet" href="/blog/css/main.css"><script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script><script type="text/javascript" src="https://platform-api.sharethis.com/js/sharethis.js#property=6550cdc47a115e0012964576&product=sop" async="async"></script></head><body><nav><input type="checkbox" id="nav-check"><h1><a href="/" class="link">rohit pai</a></h1><div class="nav-btn"><label for="nav-check"><span></span> <span></span> <span></span></label></div><ul><li><a href="/#about" class="textShadow link">about</a></li><li><a href="/#curriculumVitae" class="textShadow link">cv</a></li><li><a href="/#projects" class="textShadow link">projects</a></li><li><a href="/#contact" class="textShadow link">contact</a></li><li><a href="/blog" class="textShadow link active">blog</a></li></ul></nav><header><div><h1>full stack developer</h1><a href="/#sayHello" class="btn btnPrimary boxShadowIn boxShadowOut">Contact Me</a> <a href="" id="arrow"><i class="fa-solid fa-chevron-down"></i></a></div></header><div class="menuBar"><div class="menu"><ul><li><a href="/blog" class="link active">All posts</a></li><li><a href="/blog/category" class="link">categories</a></li><li><label for="searchField" aria-hidden="true" hidden>search</label> <input type="search" name="search" id="searchField" placeholder="Search..."> <button type="submit" id="searchBtn" class="btn btnPrimary"><i class="fa fa-search"></i></button></li></ul></div></div><main id="main"></main><div class="modal-container" id="cookiePopup"><div class="modal"><div class="modal-content"><h2><i class="fas fa-cookie-bite"></i> Hey I use cookies btw</h2><p>Just to let you know, I use cookies to give you the best experience on my blog. By clicking agree I'll assume that you are happy with it. <a href="/blog/policy/cookie" class="link">Read more</a></p><div class="flexRow"><button class="btn btnPrimary" id="cookieAccept">agree</button></div></div></div></div><footer class="flexRow"><div class="nav"><ul><li><a href="/blog/policy/privacy" class="link">privacy policy</a></li><li><a href="/blog/policy/cookie" class="link">cookie policy</a></li></ul></div><p>&copy; <span id="year"></span> Rohit Pai all rights reserved</p><div class="button"><button id="goBackToTop"><i class="fa-solid fa-chevron-up"></i></button></div></footer><script src="/js/typewriter.js"></script><script src="/blog/js/index.js"></script><script id="dsq-count-scr" src="https://rohitpaiportfolio.disqus.com/count.js" async></script></body></html>

File diff suppressed because one or more lines are too long

2
dist/css/main.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -296,17 +296,18 @@ class blogData
$latest = $this->getLatestBlogPost();
$prevTitle = $latest["title"];
$prevAbstract = $latest["abstract"];
$prevHeaderImage = $latest["headerImg"];
$prevHeaderImage = substr($latest["headerImg"], 10);
$prevHeaderImage = str_ireplace("%2F", "/", $prevHeaderImage);
$headerImage = $targetFile["imgLocation"];
$headerImage = rawurlencode("../" . $targetFile["imgLocation"]);
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, abstract, body, bodyText, categories, keywords, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :abstract, :body, :bodyText, :categories, :keywords, :folderID);");
$stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated);
$isFeatured = $featured ? 1 : 0;
$stmt->bindParam(":featured", $isFeatured);
// $isFeatured = $featured ? 1 : 0;
$stmt->bindParam(":featured", $featured);
$stmt->bindParam(":headerImg", $headerImage);
$stmt->bindParam(":abstract", $abstract);
$stmt->bindParam(":body", $newBody);
@ -323,6 +324,8 @@ class blogData
$stmtEmails = $conn->prepare("SELECT email FROM newsletter;");
$stmtEmails->execute();
$emails = $stmtEmails->fetchAll(PDO::FETCH_ASSOC);
$headerImage = substr($headerImage, 10);
$headerImage = str_ireplace("%2F", "/", $headerImage);
$emailBody = <<<EOD
<!DOCTYPE html>
@ -533,7 +536,7 @@ class blogData
<div class="postContainer">
<h2>latest post</h2>
<div class="image">
<img src="$headerImage" alt="header image of the latest post">
<img src="https://rohitpai.co.uk/$headerImage" alt="header image of the latest post">
</div>
<div class="post">
@ -546,7 +549,7 @@ class blogData
<div class="postContainer">
<h2>in case you missed the previous post</h2>
<div class="image">
<img src="$prevHeaderImage" alt="header image of the previous post">
<img src="https://rohitpai.co.uk/$prevHeaderImage" alt="header image of the previous post">
</div>
<div class="post">
@ -556,21 +559,26 @@ class blogData
</div>
</div>
</main>
EOD;
foreach ($emails as $email)
{
$emailFooter = <<<EOD
<footer>
<div class="nav">
<ul>
<li><a href="https://rohitpai.co.uk/blog">&lt;https://rohitpai.co.uk/blog&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe">&lt;Unsubscribe&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe/$email">&lt;Unsubscribe&gt;</a></li>
</ul>
</div>
<div class="date">2023</div>
</footer>
</body>
</html>
EOD;
EOD;
$emailBody .= $emailFooter;
foreach ($emails as $email)
{
$this->sendMail($email["email"], $emailBody, "Hey, Rohit's blog has a new post!");
}
@ -642,7 +650,8 @@ EOD;
unlink($result["headerImg"]);
$stmt = $conn->prepare("UPDATE blog SET headerImg = :headerImg WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$location = urldecode("../" . $targetFile["imgLocation"]);
$stmt->bindParam(":headerImg", $location);
$stmt->execute();
if ($stmt->rowCount() > 0)
{
@ -1168,7 +1177,7 @@ EOD;
<div class="nav">
<ul>
<li><a href="https://rohitpai.co.uk/blog">&lt;https://rohitpai.co.uk/blog&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe">&lt;Unsubscribe&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe/$email">&lt;Unsubscribe&gt;</a></li>
</ul>
</div>
<div class="date">2023</div>

View File

@ -282,17 +282,18 @@ class blogRoutes implements routesInterface
if ($message === "email not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, email not found")));
$response->getBody()->write(json_encode(array("message" => "Woah, you're already trying to leave without signing up?")));
return $response->withStatus(404);
}
if ($message === "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, something went wrong")));
$response->getBody()->write(json_encode(array("message" => "Error, something went wrong")));
return $response->withStatus(500);
}
$response->getBody()->write(json_encode(array("message" => "Sorry to see you go! You'll no longer receive any emails from me. If you change your mind, you can always sign up again.")));
return $response;
});
@ -324,8 +325,8 @@ class blogRoutes implements routesInterface
$headerImg = null;
}
$featured = $data["featured"] === "true";
$insertedID = $this->blogData->createPost($data["title"], $data["abstract"], $data["body"], $data["bodyText"], $data["dateCreated"], $featured, $data["categories"], $headerImg);
// $featured = $data["featured"] === "true";
$insertedID = $this->blogData->createPost($data["title"], $data["abstract"], $data["body"], $data["bodyText"], $data["dateCreated"], intval($data["featured"]), $data["categories"], $headerImg);
if (!is_int($insertedID))
{
// uh oh something went wrong

View File

@ -79,7 +79,7 @@ article a {
article a::before,
article a::after {
visibility: hidden;
visibility: visible;
position: absolute;
margin-top: 1px;
}
@ -93,9 +93,9 @@ article a::after {
content: '>';
}
article a:hover::before,
article a:hover::after {
visibility: visible;
article a:hover,
article a:hover {
font-weight: bold;
}
article h1 {
@ -106,6 +106,10 @@ article h3 {
margin-top: 0;
}
article h3:not(div.byLine > h3), .otherPosts h3 {
font-weight: bold;
}
aside.sideContent {
display: flex;
flex-direction: column;

View File

@ -108,14 +108,18 @@ section.largePost .outerContent .postContent a {
align-self: flex-end;
}
#main .errorFof {
#main .errorFof, #main .unsubscribe {
display: table;
width: 100%;
height: 100vh;
height: 100dvh;
text-align: center;
}
.fof {
#main .unsubscribe {
height: 50dvh;
}
.centered {
display: table-cell;
vertical-align: middle;
}

View File

@ -81,7 +81,6 @@
</p>
<div class="flexRow">
<button class="btn btnPrimary" id="cookieAccept">agree</button>
<a href="https://google.co.uk" class="btn btnPrimary">disagree</a>
</div>
</div>
</div>

View File

@ -38,7 +38,7 @@ function goToURL(url)
document.querySelector('#cookiePopup').classList.add('hidden');
}
if (url === '/blog/' || url === '/blog')
if (url === '/blog' || url === 'blog')
{
loadHomeContent();
// window.history.pushState(null, null, url);
@ -88,6 +88,15 @@ function goToURL(url)
}
}
if (urlArray[2] === 'unsubscribe')
{
if (urlArray[3])
{
unsubscribe(urlArray[urlArray.length - 1]);
return;
}
}
show404();
}
@ -136,7 +145,7 @@ function submitNewsletter()
{
document.querySelector('#newsletterMessage').classList.add('error');
document.querySelector('#newsletterMessage').classList.remove('success');
document.querySelector('#newsletterMessage div').innerHTML = 'You\'ve already signed up you silly goose!';
document.querySelector('#newsletterMessage div').innerHTML = 'You"ve already signed up you silly goose!';
return;
}
@ -153,6 +162,31 @@ function submitNewsletter()
}));
}
function unsubscribe(email)
{
fetch(`/api/blog/newsletter/${email}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
}).then(res => res.json().then(json =>
{
document.querySelector('#main').innerHTML = '';
let post = document.createElement('section');
post.classList.add('unsubscribe');
post.id = 'unsubscribe';
let mainContent = document.createElement('div');
mainContent.classList.add('centered');
mainContent.innerHTML = `
<h1>Unsubscribe</h1>
<p>${json.message}</p>
<a href="/blog/" class="btn btnPrimary">See all blog posts</a>
`;
post.appendChild(mainContent);
document.querySelector('#main').appendChild(post);
}));
}
/**
* Creates a formatted date
* @param {string} dateString - the date string
@ -175,7 +209,7 @@ function createLargePost(post)
outerContent.classList.add('outerContent');
let img = document.createElement('img');
img.className = 'banner';
img.src = post.headerImg;
img.src = post.headerImg.replaceAll('%2F', '/');
img.alt = post.title;
outerContent.appendChild(img);
let content = document.createElement('div');
@ -464,7 +498,7 @@ function createMetaTag(nameOrProperty, attribute, value)
*/
function insertMetaTags(json)
{
let metaDesc = document.querySelector('meta[name="description"]');
let metaDesc = document.querySelector('meta[name=\'description\']');
metaDesc.setAttribute('content', json.abstract);
// Twitter meta tags
@ -478,11 +512,11 @@ function insertMetaTags(json)
createMetaTag('og:image', 'content', json.headerImg);
createMetaTag('og:url', 'content', window.location.href);
createMetaTag('og:type', 'content', 'blog');
createMetaTag('og:site_name', 'content', 'Rohit Pai\'s Blog');
createMetaTag('og:site_name', 'content', 'Rohit Pai"s Blog');
createMetaTag('og:locale', 'content', 'en_GB');
//Keywords
let metKeywords = document.querySelector('meta[name="keywords"]');
let metKeywords = document.querySelector('meta[name=\'keywords\']');
let keywords = metKeywords.getAttribute('content');
keywords += `, ${json.keywords}`;
metKeywords.setAttribute('content', keywords);
@ -515,6 +549,7 @@ async function loadIndividualPost(title)
let mainContent = document.createElement('div');
mainContent.classList.add('mainContent');
let article = document.createElement('article');
let headerImg = json.headerImg.replaceAll('%2F', '/');
article.innerHTML = `
<h1>${json.title}</h1>
<div class="byLine">
@ -522,9 +557,9 @@ async function loadIndividualPost(title)
<h3>${createButtonCategories([csvToArray(json.categories.replace(/\s*,\s*/g, ','))])}</h3>
<div class="sharethis-inline-share-buttons" data-url="https://rohitpai.co.uk/blog/post/${title}"
data-title="${json.title}" data-description="${json.abstract}"
data-image="https://rohitpai.co.uk/${json.headerImg}" data-username="@rohitpai123"></div>
data-image="https://rohitpai.co.uk/${headerImg}" data-username="@rohitpai123"></div>
</div>
<div class="cover" style="background-image: url('${json.headerImg}')"></div>
<div class="cover" style="background-image: url('${headerImg}')"></div>
${json.body}
`;
let comments = document.createElement('section');
@ -662,7 +697,7 @@ function loadPrivacyPolicy()
<h2>Privacy Policy</h2>
<p>Last Updated: Nov 12, 2023</p>
<p>
Thank you for visiting the Privacy Policy of Rohit Pai's Blog. This Privacy Policy explains how I, Rohit Pai, collect, use, and share information about you (“you”, “yours” or “user”) when you access or use my website (“Services”). You are responsible for any third-party data you provide or share through the Services and confirm that you have the third party's consent to provide such data to me.
Thank you for visiting the Privacy Policy of Rohit Pai"s Blog. This Privacy Policy explains how I, Rohit Pai, collect, use, and share information about you (you, yours or user) when you access or use my website (Services). You are responsible for any third-party data you provid'e or share through the Services and confirm that you have the third partys consent to provide such data to me.
</p>
<br>
@ -833,7 +868,7 @@ function show404()
{
document.querySelector('#main').innerHTML = `
<div class="errorFof">
<div class="fof">
<div class="centered">
<h1>Blog post, Category or page not found</h1>
<a href="/blog/" class="btn btnPrimary">See all blog posts</a>
</div>

View File

@ -334,11 +334,18 @@ a.link {
a.link::before,
a.link::after {
visibility: hidden;
visibility: visible;
position: absolute;
margin-top: 1px;
}
nav a.link::before,
nav a.link::after,
.nav a.link::before,
.nav a.link::after {
visibility: hidden;
}
a.link::before {
content: ' <';
margin-left: -0.5em;
@ -348,11 +355,21 @@ a.link::after {
content: '> ';
}
a.link:hover::before,
a.link:hover::after {
a.link:hover {
font-weight: bold;
}
nav a.link:hover::before,
nav a.link:hover::after,
.nav a.link:hover::before,
.nav a.link:hover::after {
visibility: visible;
}
nav a.link:hover, .nav a.link:hover {
font-weight: normal;
}
div.error, div.success {
color: #FFFFFF;
padding: 0.5em 0.8em;

View File

@ -234,7 +234,7 @@ section#projects form.projItem:not(.editing) div.formControl.infoContainer texta
}
section#addPost form, section#editPost form {
margin: auto 4rem;
margin: auto 4rem 4rem;
}
form .formControl .ck.ck-editor__top .ck-sticky-panel .ck-toolbar {
@ -276,10 +276,6 @@ section#editPost table td, th {
min-width: 10rem;
}
section#editPost form {
margin-bottom: 2em;
}
section#newsletter form {
margin: 0 5em;
}