diff --git a/app.js b/app.js
index 083aa8b..beb2dc1 100644
--- a/app.js
+++ b/app.js
@@ -55,6 +55,12 @@ app.get("/healthz", function(req, res) {
var md = markdown({html: true});
md.parser.use(emoji);
+const proxy = (tokens, idx, options, env, self) => self.renderToken(tokens, idx, options);
+const defaultTableOpenRenderer = md.parser.renderer.rules.table_open || proxy;
+md.parser.renderer.rules.table_open = function(tokens, idx, options, env, self) {
+ tokens[idx].attrJoin("role", "grid");
+ return defaultTableOpenRenderer(tokens, idx, options, env, self)
+};
Handlebars.registerHelper('markdown', function(text) {
if(!text) return;
diff --git a/src/css/_glitch.scss b/src/css/_glitch.scss
new file mode 100644
index 0000000..8b9c186
--- /dev/null
+++ b/src/css/_glitch.scss
@@ -0,0 +1,98 @@
+
+.glitch-image {
+ animation: shift 3s ease-in-out infinite alternate;
+}
+
+.glitch, a:hover {
+ position: relative;
+ text-shadow: 0.05em 0 0 var(--primary-hover), -0.03em -0.04em 0 var(--primary), 0.025em 0.04em 0 var(--primary-inverse);
+ animation: glitch 2s ease alternate infinite, shift 2s ease alternate infinite;
+
+ &::before, &::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+
+ &::before {
+ animation: glitch 1s infinite;
+ clip-path: polygon(0 0, 100% 0, 100% 35%, 0 35%);
+ transform: translate(-0.04em, -0.03em);
+ opacity: 0.75;
+ }
+
+ &::after {
+ animation: glitch .5s infinite;
+ clip-path: polygon(0 65%, 100% 65%, 100% 100%, 0 100%);
+ transform: translate(0.04em, 0.03em);
+ opacity: 0.75;
+ }
+}
+
+@keyframes glitch {
+ 0% {
+ text-shadow: 0.05em 0 0 var(--primary-hover), -0.03em -0.04em 0 var(--primary),
+ 0.025em 0.04em 0 var(--primary-inverse);
+ }
+ 15% {
+ text-shadow: 0.05em 0 0 var(--primary-hover), -0.03em -0.04em 0 var(--primary),
+ 0.025em 0.04em 0 var(--primary-inverse);
+ }
+ 16% {
+ text-shadow: -0.05em -0.025em 0 var(--primary-hover), 0.025em 0.035em 0 var(--primary),
+ -0.05em -0.05em 0 var(--primary-inverse);
+ }
+ 49% {
+ text-shadow: -0.05em -0.025em 0 var(--primary-hover), 0.025em 0.035em 0 var(--primary),
+ -0.05em -0.05em 0 var(--primary-inverse);
+ }
+ 50% {
+ text-shadow: 0.05em 0.035em 0 var(--primary-hover), 0.03em 0 0 var(--primary),
+ 0 -0.04em 0 var(--primary-inverse);
+ }
+ 99% {
+ text-shadow: 0.05em 0.035em 0 var(--primary-hover), 0.03em 0 0 var(--primary),
+ 0 -0.04em 0 var(--primary-inverse);
+ }
+ 100% {
+ text-shadow: -0.05em 0 0 var(--primary-hover), -0.025em -0.04em 0 var(--primary),
+ -0.04em -0.025em 0 var(--primary-inverse);
+ }
+}
+
+@keyframes shift {
+ 0%,40%, 44%, 58%, 61%, 65%,69%,73%,100% {
+ transform: skewX(0deg);
+ filter: invert(0%);
+ fill: var(--text-color);
+ }
+ 41% {
+ transform: skewX(50deg);
+ fill: var(--primary);
+ }
+ 42% {
+ transform: skewX(-20deg);
+ filter: invert(40%);
+ }
+ 59% {
+ transform: skewX(50deg);
+ fill: var(--primary-hover);
+ }
+ 60% {
+ transform: skewX(-40deg);
+ filter: invert(10%);
+ }
+ 63% {
+ transform: skewX(10deg);
+ filter: invert(30%);
+ fill: var(--primary-hover);
+ }
+ 70% {
+ transform: skewX(-30deg);
+ fill: var(--primary);
+ }
+ 71% {
+ transform: skewX(15deg);
+ filter: invert(100%);
+ }
+}
diff --git a/src/css/_underlines.scss b/src/css/_underlines.scss
deleted file mode 100644
index 15666c5..0000000
--- a/src/css/_underlines.scss
+++ /dev/null
@@ -1,64 +0,0 @@
-@mixin textShadowToCropUnderline($color) {
- text-shadow:
- .01em 0 $color,
- -.01em 0 $color,
- 0 .01em $color,
- 0 -.01em $color,
-
- .06em 0 $color,
- -.06em 0 $color,
- .09em 0 $color,
- -.09em 0 $color,
-
- .12em 0 $color,
- -.12em 0 $color,
- .15em 0 $color,
- -.15em 0 $color;
-}
-
-@mixin linkUnderlines($background, $color, $hoverColor) {
- color: $color;
- text-decoration: none;
- @include textShadowToCropUnderline($background);
-
- background-image:
- linear-gradient($background, $background),
- linear-gradient($background, $background),
- linear-gradient($color, $color);
- background-size:
- .05em 2px,
- .05em 2px,
- 2px 2px;
- background-repeat:
- no-repeat,
- no-repeat,
- repeat-x;
- background-position: 0% 1.02em, 100% 1.02em, 0% 1.04em;
-
- &::selection {
- @include textShadowToCropUnderline($selectionColor);
- background-color: $selectionColor;
- }
- &::-moz-selection {
- @include textShadowToCropUnderline($selectionColor);
- background-color: $selectionColor;
- }
- &:before,
- &:after,
- *,
- *:before,
- *:after {
- text-shadow: none;
- }
- &:visited {
- color: $color;
- }
-
- &:hover {
- color: $hoverColor;
- background-image:
- linear-gradient($background, $background),
- linear-gradient($background, $background),
- linear-gradient($hoverColor, $hoverColor);
- }
-}
diff --git a/src/css/_utils.scss b/src/css/_utils.scss
new file mode 100644
index 0000000..eede0c6
--- /dev/null
+++ b/src/css/_utils.scss
@@ -0,0 +1,7 @@
+.center {
+ text-align: center;
+}
+
+.hide {
+ display: none;
+}
diff --git a/src/css/main.scss b/src/css/main.scss
deleted file mode 100644
index f01017a..0000000
--- a/src/css/main.scss
+++ /dev/null
@@ -1,260 +0,0 @@
-$link-color: #AD4E4E;
-$selectionColor: #D2D2D2;
-$background-color: #FFF;
-
-@import "_underlines.scss";
-
-
-body {
- margin: 0;
- padding: 0;
- overflow: none;
-}
-
-* {
- &::selection {
- background-color: $selectionColor;
- }
- &::-moz-selection {
- background-color: $selectionColor;
- }
-}
-
-a {
- color: $link-color;
- transition: color ease .3s;
-
- &:hover {
- color: $link-color;
- text-decoration: none;
- }
-
- &+img {
- border-bottom: none;
- }
-}
-
-.center {
- text-align: center;
-}
-
-header {
- font-family: 'Lucida Grande', Arial, sans-serif;
- font-size: .8em;
-
- h1 {
- font-weight: normal;
- margin: .8em 0 .2em 8px;
-
- img {
- height: 2em;
- border-radius: 8px;
- }
-
- a {
- color: #000 !important;
- text-decoration: none;
- margin: 0;
- }
-
- .social {
- text-decoration: none;
-
- svg {
- height: 20px;
- }
-
- &:hover svg path,
- &:hover svg rect {
- fill: $link-color !important;
- }
- }
- }
-
- hr {
- margin: 0 auto;
- width: 95%;
- height: 1px;
- background-color: rgba(0, 0, 0, .2);
- }
-}
-
-pre code {
- overflow-x: auto;
- overflow-y: auto;
- padding-bottom: 15px;
- font-size: 0.85em;
-}
-
-p > code {
- background-color: #3f3f3f;
- color: #dcdcdc;
- font-size: 0.9em;
- padding: 2px 5px;
-}
-
-blockquote {
- border-left: 8px solid rgba(121, 130, 139, 0.52);
- margin-left: 0;
- padding-left: 1em;
- color: #79828B;
-}
-
-figure {
- max-width: 100%;
- margin: 0 auto;
-
- img {
- max-width: 100%;
- border: 1px solid rgba(100,100,100,.2);
- }
-
- p {
- margin: 0;
- }
-}
-
-iframe {
- max-width: 100%;
- border: 0;
- overflow: scroll;
-}
-
-hr {
- border: 0;
- height: 2px;
- background: rgba(121, 130, 139, 0.52);
- width: 80%;
-}
-
-details {
- font-size: 0.8em;
- border: 1px dashed #79828B;
- padding: 2px 8px;
-
- summary {
- cursor: pointer;
- }
-
- p {
- margin: 0;
- }
-}
-
-.emoji {
- display: inline !important;
- margin: 0 !important;
-}
-
-.container {
- max-width: 1020px;
- margin: 0 auto;
-}
-
-.post-list {
- list-style: none;
- padding: 0;
-}
-
-.post-preview {
- opacity: 0.6;
-}
-
-.pagination {
- text-align: center;
-}
-.pagination a {
- font-size: 1.4mem;
- color: #000;
-}
-
-.post {
- font-family: 'Lucida Grande', Arial, sans-serif;
- font-size: 18px;
- line-height: 28px;
- padding: 2px 8px;
-
- a {
- @include linkUnderlines($background-color, #4a4a4a, $link-color);
- }
-
- .post-title {
- color: #000;
- font-size: 32px;
- line-height: 34px;
- margin: 21px 0 0;
- font-weight: 700;
-
- a {
- color: #000;
- text-decoration: none;
- margin: 0;
- }
- }
-
- .full-post-link {
- font-style: italic;
- font-size: 0.9em;
- }
-
- .post-meta {
- color: #3d4145;
- font-size: 15px;
- line-height: 17px;
- margin: 0 0 12px 0;
- }
-}
-
-footer {
- margin: 20px 0 10px;
- color: #79828B;
- text-align: center;
-}
-
-.social-icons {
- a {
- text-decoration: none !important;
- background: none !important;
- text-shadow: none !important;
- }
- a svg {
- height: 40px;
- }
-}
-
-.share {
- background: transparent;
- border: none;
- display: none;
- cursor: pointer;
-
- &.show {
- display: initial;
- }
-
- &:hover, &:active {
- fill: $link-color;
- color: $link-color;
- }
-
- svg {
- height: 20px;
- vertical-align: bottom;
- }
-}
-
-table {
- width: 100%;
- border-spacing: 0;
- border-collapse: collapse;
-
- &, tr, td, th, tbody, thead {
- margin: 0;
- padding: 0;
- }
-
- th, td {
- padding: 5px;
- border: 1px solid #3d4145;
- }
-}
diff --git a/src/css/style.scss b/src/css/style.scss
new file mode 100644
index 0000000..c01831c
--- /dev/null
+++ b/src/css/style.scss
@@ -0,0 +1,228 @@
+/* Light Theme */
+[data-theme="light"],
+:root:not([data-theme="dark"]) {
+ --primary: #fd7e0b;
+ --primary-hover: #326ce5;
+ --primary-focus: #fefefe;
+ --primary-inverse: #d6efff;
+ --color: #131b23;
+ --text-color: #131b23;
+}
+
+/* Dark Theme */
+@media only screen and (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) {
+ --primary: #fd7e0b;
+ --primary-hover: #326ce5;
+ --primary-focus: #131b23;
+ --primary-inverse: #d6efff;
+ --color: #fefefe;
+ --text-color: #fefefe;
+ }
+}
+[data-theme="dark"] {
+ --primary: #fd7e0b;
+ --primary-hover: #326ce5;
+ --primary-focus: #131b23;
+ --primary-inverse: #d6efff;
+ --color: #fefefe;
+ --text-color: #fefefe;
+}
+
+:root:not([data-theme="dark"]),
+:root:not([data-theme="light"]){
+ --form-element-active-border-color: var(--primary);
+ --form-element-focus-color: var(--primary-focus);
+ --switch-color: var(--primary-inverse);
+ --switch-checked-background-color: var(--primary);
+ --blockquote-border-color: var(--primary-hover);
+
+ --font-family: 'Orkney', system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu",
+ "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
+ "Segoe UI Symbol", "Noto Color Emoji";
+}
+
+h2 {
+ margin-bottom: 10px;
+}
+
+// Links
+
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+ color: var(--text-color)
+}
+
+a:hover {
+ color: var(--primary);
+}
+
+@media only screen and (prefers-color-scheme: light) {
+ a.social {
+ svg {
+ opacity: 0.5;
+ }
+ }
+}
+[data-theme="light"] {
+ a.social {
+ svg {
+ opacity: 0.5;
+ }
+ }
+}
+a.social {
+ svg {
+ margin: 4px;
+ fill: var(--text-color);
+ }
+
+ &:hover {
+ text-decoration: none;
+
+ svg {
+ animation: shift 1.5s ease-in-out infinite;
+ }
+ }
+}
+
+// Layout
+
+header {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 2em 0 !important;
+
+ & + main {
+ padding-top: 0;
+ }
+}
+
+footer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 1em 0 !important;
+}
+
+article {
+ margin-top: 0;
+ padding-top: 1.5em;
+
+ blockquote, dl, figure, form, ol, p, pre, table, ul {
+ font-size: calc(var(--font-size) + 2px) !important;
+ }
+
+ a {
+ display: inline-block;
+ }
+}
+
+figure p,
+blockquote p,
+blockquote ul {
+ margin: 0;
+}
+
+figure a:hover {
+ text-shadow: initial;
+ animation: none;
+}
+
+details {
+ font-size: 0.8em;
+ border: 1px dashed #79828B;
+ padding: 2px 8px;
+
+ summary {
+ cursor: pointer;
+
+ &::after {
+ float: unset;
+ display: inline-block;
+ height: 10px;
+ }
+ }
+
+ p {
+ margin: 0;
+ }
+}
+
+code, kbd, pre {
+ color: #dcdcdc;
+ background-color: #3f3f3f;
+}
+
+hr {
+ margin: 1em auto;
+ padding: 0;
+ border: 0;
+ border-top: solid 3px #57688d;
+ text-align: center;
+ width: 60%;
+ position: relative;
+
+
+ &::after {
+ content: "✨";
+ display: inline-block;
+ position: absolute;
+ top: -0.7em;
+ padding: 0 5px;
+ font-size: 2rem;
+ filter: grayscale(70%);
+ transform: translateX(-50%);
+ }
+
+ &::before {
+ content: ' ';
+ background-color: var(--background-color);
+ display: inline-block;
+ position: absolute;
+ top: -1.2em;
+ padding: 0;
+ width: 50px;
+ height: 50px;
+ border-radius: 23px;
+ transform: translateX(-50%);
+
+ article & {
+ background-color: var(--card-background-color) !important;
+ }
+ }
+}
+
+section:last-of-type hr {
+ display: none;
+}
+
+// Custom Classes
+
+.site-logo {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.post-meta {
+ font-size: 0.7em;
+ font-family: 'OrkneyLight';
+ vertical-align: text-bottom;
+ margin-bottom: 0.5em;
+
+ &::before {
+ content: "📆 ";
+ filter: grayscale(70%);
+ }
+}
+
+.pagination {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-evenly;
+}
+
+
+@import "_utils.scss";
+@import "_glitch.scss";
diff --git a/src/posts/2019-10-21-meetup-alternatives.md b/src/posts/2019-10-21-meetup-alternatives.md
index 04d7baf..2e77221 100644
--- a/src/posts/2019-10-21-meetup-alternatives.md
+++ b/src/posts/2019-10-21-meetup-alternatives.md
@@ -6,6 +6,17 @@ tags: Meetup
summary: "A look at the various alternatives to Meetup.com after recent online backlash to their proposed new pricing model."
---
+Changelog
+
+2019-10-21: Migrated from a [Notion page](https://www.notion.so/jsoxford/Meetup-Alternatives-36d73649d34f4bba9e2065f1fa8cd03f) to my blog.
+
+2019-10-21: Added Eventsyay, Mixily and Helm.
+
+2020-09-30: Added section covering TechTalks.io.
+
+Changelog
+
+2020-05-28: Added big list of naughty strings test
+
+