diff --git a/.eslintrc.json b/.eslintrc.json
index dfded19..902ad5e 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -36,7 +36,24 @@
"react/jsx-props-no-spreading": "off",
"jsx-a11y/anchor-is-valid": "off",
"react/no-unescaped-entities": "off",
- "react/jsx-indent": "warn"
+ "react/jsx-indent": "warn",
+ "no-return-await": "off",
+ "no-console": "warn",
+ "array-callback-return": "off",
+ "no-param-reassign": "off",
+ "no-plusplus": "off",
+ "prefer-const": "warn",
+ "prefer-destructuring": [
+ "warn",
+ {
+ "array": false,
+ "object": true
+ },
+ {
+ "enforceForRenamedProperties": false
+ }
+ ],
+ "react/no-danger": "off"
},
"ignorePatterns": [
".DS_Store",
diff --git a/components/Blog/Featured.module.scss b/components/Blog/Featured.module.scss
new file mode 100644
index 0000000..cdfb7a9
--- /dev/null
+++ b/components/Blog/Featured.module.scss
@@ -0,0 +1,36 @@
+@use 'styles/colors';
+@use 'styles/breakpoints';
+@use 'styles/functions' as *;
+
+$radial-size: 0.75px;
+
+.root {
+ margin: 1em;
+ margin-top: 0;
+ background-image: radial-gradient(colors.$primary $radial-size, transparent $radial-size);
+ background-size: css-calc(10 * $radial-size) css-calc(10 * $radial-size);
+
+ @media (max-width: breakpoints.$tablet) {
+ margin-top: 1em;
+ }
+}
+
+.wrapper {
+ background-color: map-get(colors.$background, primary);
+ transform: translate(20px, -20px);
+ padding-left: 0.5em;
+ padding-bottom: 0.5em;
+ padding-top: 10px;
+}
+
+.icon {
+ font-size: 0.85em;
+}
+
+.posts {
+ margin-top: 0.5em;
+}
+.post + .post {
+ margin-top: 0.75em;
+}
+
diff --git a/components/Blog/Featured.tsx b/components/Blog/Featured.tsx
new file mode 100644
index 0000000..d4b1fef
--- /dev/null
+++ b/components/Blog/Featured.tsx
@@ -0,0 +1,43 @@
+import { useContext } from 'react';
+
+import { FaBookmark } from 'react-icons/fa';
+import FeaturedPost from './FeaturedPost';
+import featured from './Featured.module.scss';
+
+import { PostsContext } from '../../pages/blog/index';
+
+const Featured = () => {
+ const posts = useContext(PostsContext);
+ const featuredPosts = posts
+ .filter((post) => post.featured === true)
+ .sort(() => 0.5 - Math.random())
+ .slice(0, 3);
+
+ featuredPosts.map((post) => {
+ post.tagList = post.tags.map((tag) => tag.name).join(' ');
+ });
+
+ return (
+
+
+
+ Featured
+
+
+ {featuredPosts.map((post) => (
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default Featured;
diff --git a/components/Blog/FeaturedPost.module.scss b/components/Blog/FeaturedPost.module.scss
new file mode 100644
index 0000000..66dd369
--- /dev/null
+++ b/components/Blog/FeaturedPost.module.scss
@@ -0,0 +1,13 @@
+@use 'styles/colors';
+
+.root {
+ font-size: 0.8em;
+
+ p {
+ color: colors.$gray;
+ }
+
+ h3:hover {
+ text-decoration: underline;
+ }
+}
diff --git a/components/Blog/FeaturedPost.tsx b/components/Blog/FeaturedPost.tsx
new file mode 100644
index 0000000..4ec7a2a
--- /dev/null
+++ b/components/Blog/FeaturedPost.tsx
@@ -0,0 +1,18 @@
+import Link from 'next/link';
+
+import styles from './FeaturedPost.module.scss';
+
+const FeaturedPost = (props) => {
+ return (
+
+
+
+
{props.title}
+
{props.tags}
+
+
+
+ );
+};
+
+export default FeaturedPost;
diff --git a/components/Blog/Form.module.scss b/components/Blog/Form.module.scss
new file mode 100644
index 0000000..0a6013c
--- /dev/null
+++ b/components/Blog/Form.module.scss
@@ -0,0 +1,51 @@
+@use 'styles/colors';
+
+.root {
+ margin-top: 1em;
+ input {
+ background-color: map-get(colors.$background, secondary);
+ padding: 0.5em 1em;
+ width: 100%;
+ color: map-get(colors.$text, primary);
+ border-radius: max(0.3em, 5px);
+ outline: none;
+ border: none;
+ font-size: 1rem;
+
+ &[type='submit'] {
+ color: colors.$white;
+ background-color: colors.$gray;
+ transition: all 0.1s ease-in-out;
+
+ &:hover,
+ &:active {
+ cursor: pointer;
+ }
+
+ &:hover {
+ transform: scale(1.08);
+ }
+
+ &:active {
+ transform: scale(1);
+ }
+ }
+ }
+
+ label {
+ font-size: 0.8rem;
+ padding: 0.25em 1em;
+ }
+}
+
+.group + .group {
+ display: flex;
+ flex-direction: column;
+ margin-top: 0.5em;
+}
+
+.inline {
+ display: grid;
+ grid-template-columns: 7fr 3fr;
+ grid-gap: 0.5em;
+}
diff --git a/components/Blog/Form.tsx b/components/Blog/Form.tsx
new file mode 100644
index 0000000..9688de3
--- /dev/null
+++ b/components/Blog/Form.tsx
@@ -0,0 +1,22 @@
+import form from './Form.module.scss';
+
+const Form = () => {
+ return (
+
+
Newsletter
+
+
+ {/* Email */}
+
+
+
+ );
+};
+
+export default Form;
diff --git a/components/Blog/Header.module.scss b/components/Blog/Header.module.scss
new file mode 100644
index 0000000..1626733
--- /dev/null
+++ b/components/Blog/Header.module.scss
@@ -0,0 +1,17 @@
+@use 'styles/colors';
+@use 'styles/breakpoints';
+
+.root {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ // gap: 1em;
+
+ @media (max-width: breakpoints.$tablet) {
+ display: flex;
+ flex-direction: column;
+ }
+}
+
+.wrapper:nth-of-type(2) {
+ padding-top: 15px;
+}
\ No newline at end of file
diff --git a/components/Blog/Header.tsx b/components/Blog/Header.tsx
new file mode 100644
index 0000000..b6e6aaf
--- /dev/null
+++ b/components/Blog/Header.tsx
@@ -0,0 +1,19 @@
+import header from './Header.module.scss';
+import Form from './Form';
+import Featured from './Featured';
+
+const Header = () => {
+ return (
+
+
+
Thoughts.
+
+
+
+
+
+
+ );
+};
+
+export default Header;
diff --git a/components/Blog/PostPreview.module.scss b/components/Blog/PostPreview.module.scss
new file mode 100644
index 0000000..b60a83e
--- /dev/null
+++ b/components/Blog/PostPreview.module.scss
@@ -0,0 +1,20 @@
+@use 'styles/colors';
+
+.root {
+ font-size: 0.8em;
+ max-width: 65ch;
+ margin: 1.5em auto;
+
+ h2:hover {
+ text-decoration: underline;
+ }
+}
+
+.meta {
+ color: colors.$gray;
+}
+
+.icon {
+ font-size: 0.8em;
+ margin-left: 0.25em;
+}
diff --git a/components/Blog/PostPreview.tsx b/components/Blog/PostPreview.tsx
new file mode 100644
index 0000000..c2c41f2
--- /dev/null
+++ b/components/Blog/PostPreview.tsx
@@ -0,0 +1,24 @@
+import Link from 'next/link';
+import { FaExternalLinkAlt } from 'react-icons/fa';
+import postPreview from './PostPreview.module.scss';
+
+const PostPreview = (props) => {
+ return (
+
+
+
+
+ {props.title}
+
+
+ {props.pubDate} | {props.readTime} | {props.tags}
+
+
+
{props.excerpt}
+
+
+
+ );
+};
+
+export default PostPreview;
diff --git a/components/Post.module.scss b/components/Copy.module.scss
similarity index 100%
rename from components/Post.module.scss
rename to components/Copy.module.scss
diff --git a/components/Post.tsx b/components/Copy.tsx
similarity index 78%
rename from components/Post.tsx
rename to components/Copy.tsx
index 692a4af..97ae8d9 100644
--- a/components/Post.tsx
+++ b/components/Copy.tsx
@@ -1,5 +1,5 @@
import Link from 'next/link';
-import post from './Post.module.scss';
+import post from './Copy.module.scss';
const Post = (props) => {
return {props.children}
;
diff --git a/components/Layouts/Layout.module.scss b/components/Layouts/Layout.module.scss
index 13a57b0..21537c3 100644
--- a/components/Layouts/Layout.module.scss
+++ b/components/Layouts/Layout.module.scss
@@ -13,6 +13,6 @@
.contentWrapper {
padding: 0.5em 1em;
margin: 0 auto;
+ width: 100%;
max-width: 65ch;
- max-width: 750px;
}
diff --git a/components/Nav.tsx b/components/Nav.tsx
index bd7d622..1c12615 100644
--- a/components/Nav.tsx
+++ b/components/Nav.tsx
@@ -12,20 +12,20 @@ const Nav = () => {
@saharshy29
- {/*
);
};
diff --git a/package.json b/package.json
index c7c13b7..8487e5d 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"start": "next start"
},
"dependencies": {
+ "@tryghost/content-api": "^1.4.4",
"imagemin-optipng": "^8.0.0",
"next": "9.5.3",
"next-optimized-images": "^2.6.2",
@@ -17,11 +18,11 @@
"sass": "^1.26.10"
},
"devDependencies": {
- "@types/node": "^14.6.2",
- "@types/react": "^16.9.49",
- "@typescript-eslint/parser": "^3.10.1",
- "@typescript-eslint/eslint-plugin": "^4.0.0",
- "eslint": "^7.8.1",
+ "@types/node": "^14.6.0",
+ "@types/react": "^16.9.47",
+ "@typescript-eslint/eslint-plugin": "^3.10.0",
+ "@typescript-eslint/parser": "^3.10.0",
+ "eslint": "^7.7.0",
"eslint-config-airbnb": "^18.2.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.0",
diff --git a/pages/api/posts.js b/pages/api/posts.js
new file mode 100644
index 0000000..4e9a5ee
--- /dev/null
+++ b/pages/api/posts.js
@@ -0,0 +1,22 @@
+import GhostContentAPI from '@tryghost/content-api';
+
+// Create API instance with site credentials
+const api = new GhostContentAPI({
+ url: 'https://shuy.ghost.io',
+ key: '4b1a5b85b966c4f3072ca8b307', // This shouldn't be publicly exposed, but it's not a big deal since it will not work soon enough, and even now, it does not provide access to much.
+ version: 'v3',
+});
+
+export async function getPosts() {
+ return await api.posts
+ .browse({
+ include: 'tags',
+ limit: 'all',
+ })
+ .catch((err) => {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ });
+}
+
+export default getPosts();
diff --git a/pages/blog/[slug].tsx b/pages/blog/[slug].tsx
new file mode 100644
index 0000000..ad3405e
--- /dev/null
+++ b/pages/blog/[slug].tsx
@@ -0,0 +1,55 @@
+import { GetStaticProps, GetStaticPaths } from 'next';
+import { useRouter } from 'next/router';
+import Layout from '../../components/Layouts/Layout';
+import Copy from '../../components/Copy';
+
+import { getPosts } from '../api/posts';
+
+const Post = (props) => {
+ const { slug } = useRouter().query;
+ let post = props.posts.filter((p) => p.slug === slug);
+ post = post[0];
+
+ return (
+
+ {post.title}
+ {post.tagList}
+
+
+
+
+
+ );
+};
+
+export default Post;
+
+export const getStaticProps = async () => {
+ const posts = await getPosts();
+
+ posts.map((post) => {
+ post.dateFormatted = new Intl.DateTimeFormat('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ }).format(new Date(post.published_at));
+ });
+
+ posts.map((post) => {
+ post.tagList = post.tags.map((tag) => tag.name).join(' ');
+ });
+
+ return {
+ props: {
+ posts,
+ },
+ };
+};
+
+export async function getStaticPaths() {
+ const posts = await getPosts();
+ return {
+ paths: posts?.map((post) => `/blog/${post.slug}`),
+ fallback: false,
+ };
+}
diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx
new file mode 100644
index 0000000..350d9e1
--- /dev/null
+++ b/pages/blog/index.tsx
@@ -0,0 +1,65 @@
+import { GetStaticProps } from 'next';
+import { createContext } from 'react';
+
+import Layout from '../../components/Layouts/Layout';
+import Header from '../../components/Blog/Header';
+import PostPreview from '../../components/Blog/PostPreview';
+
+import { getPosts } from '../api/posts';
+
+interface Post {
+ Post: [];
+ title: string;
+ featured: boolean;
+ tags: any;
+ tagList: string;
+ uuid: string;
+ slug: string;
+}
+
+export const PostsContext = createContext([]);
+
+const Home = (props) => {
+ return (
+
+
+
+
+ {props.posts.map((post) => (
+
+ ))}
+
+
+ );
+};
+
+export default Home;
+
+export const getStaticProps = async () => {
+ const posts = await getPosts();
+
+ posts.map((post) => {
+ post.dateFormatted = new Intl.DateTimeFormat('en-US', {
+ month: 'short',
+ day: 'numeric',
+ }).format(new Date(post.published_at));
+ });
+
+ posts.map((post) => {
+ post.tagList = post.tags.map((tag) => tag.name).join(' ');
+ });
+
+ return {
+ props: {
+ posts,
+ },
+ };
+};
diff --git a/pages/index.tsx b/pages/index.tsx
index 4a57dd8..a5aa24a 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -1,6 +1,6 @@
import Link from 'next/link';
import Layout from '../components/Layouts/Layout';
-import Post from '../components/Post';
+import Post from '../components/Copy';
const Home = () => {
return (
diff --git a/pages/tutoring.tsx b/pages/tutoring.tsx
index ac58bd6..7d39e87 100644
--- a/pages/tutoring.tsx
+++ b/pages/tutoring.tsx
@@ -1,5 +1,5 @@
import Layout from '../components/Layouts/Layout';
-import Post from '../components/Post';
+import Post from '../components/Copy';
import CalendlyEmbed from '../components/CalendlyEmbed';
const Tutoring = () => {
diff --git a/styles/_colors.scss b/styles/_colors.scss
index b7ee7ca..d5991f4 100644
--- a/styles/_colors.scss
+++ b/styles/_colors.scss
@@ -12,5 +12,5 @@ $background: (
$text: (
primary: var(--text--primary),
- opposite: var(--text--color--opposite),
+ secondary: var(--text--color--secondary),
);
diff --git a/styles/_functions.scss b/styles/_functions.scss
index 8b97919..bbf4f9e 100644
--- a/styles/_functions.scss
+++ b/styles/_functions.scss
@@ -10,6 +10,10 @@
@return m#{a}x(#{$numbers});
}
+@function css-calc($numbers...) {
+ @return c#{a}lc(#{$numbers});
+}
+
@function headings($from:1, $to:6) {
@if $from == $to {
@return 'h#{$from}';
diff --git a/styles/globals.scss b/styles/globals.scss
index 62852af..1258db4 100644
--- a/styles/globals.scss
+++ b/styles/globals.scss
@@ -17,7 +17,7 @@
--background--secondary: var(--color--white);
--text--primary: var(--color--black);
- --text-color--opposite: var(--color--white);
+ --text--color--secondary: var(--color--white);
@media (prefers-color-scheme: dark) {
--color--primary: #ff9a1f;
@@ -27,7 +27,7 @@
--background--secondary: #2d3b4e;
--text--primary: var(--color--white);
- --text-color--opposite: var(--color--black);
+ --text--color--secondary: var(--color--black);
}
}
@@ -47,11 +47,15 @@ body {
// For sticky footer
min-height: 100vh;
- @media (max-width: breakpoints.$mobile) {
+ @media (max-width: breakpoints.$mobile) {
// font-size: 1em;
}
}
+#{headings(1,6)} {
+ font-family: fonts.$serif;
+}
+
* {
padding: 0;
margin: 0;
@@ -61,7 +65,49 @@ body {
a,
a:visited,
a:hover,
-a:active{
+a:active {
color: currentColor;
text-decoration: none;
-}
\ No newline at end of file
+}
+
+hr {
+ border-top: 1px solid colors.$gray;
+ margin: 0.5em 0;
+
+ &.fancy {
+ background: linear-gradient(
+ to left,
+ currentColor calc(50% - 12px),
+ transparent calc(50% - 12px),
+ transparent calc(50% + 12px),
+ currentColor calc(50% + 12px)
+ );
+ background-color: transparent;
+ border: none;
+ height: 0.08rem;
+ overflow: visible;
+ position: relative;
+ color: var(--color--gray);
+ margin: 1.2rem auto;
+
+ &::before,
+ &::after {
+ background: currentColor;
+ content: '';
+ display: block;
+ height: 0.8rem;
+ position: absolute;
+ top: calc(50% - 0.4rem);
+ transform: rotate(22.5deg);
+ width: 0.1rem;
+ }
+
+ &::before {
+ left: calc(50% - 0.25rem);
+ }
+
+ &::after {
+ right: calc(50% - 0.25rem);
+ }
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
index 908001c..1ceb1ea 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -15,6 +15,6 @@
"jsx": "preserve",
"baseUrl": "."
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "components/CalendlyEmbed.tsx"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
diff --git a/yarn.lock b/yarn.lock
index fc7daa5..a9d657c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1109,6 +1109,13 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
+"@tryghost/content-api@^1.4.4":
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/@tryghost/content-api/-/content-api-1.4.4.tgz#ae52a86e694edbe50a7460693f4b7fff619d6174"
+ integrity sha512-5q/M/fcE9OwPHKM+DQUrrjQD7CaNShBxtI/3/aBrcbQPX/aIL2FjbFA5pi3KJ/oe2f0IFlUPGeM1IGuXWsIV4Q==
+ dependencies:
+ axios "0.19.2"
+
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@@ -1716,6 +1723,13 @@ axe-core@^3.5.4:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227"
integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q==
+axios@0.19.2:
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
+ integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
+ dependencies:
+ follow-redirects "1.5.10"
+
axobject-query@^2.1.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
@@ -2635,6 +2649,13 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "^2.1.1"
+debug@=3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+ dependencies:
+ ms "2.0.0"
+
debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -3622,6 +3643,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
+follow-redirects@1.5.10:
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
+ integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
+ dependencies:
+ debug "=3.1.0"
+
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"