initial setup

This commit is contained in:
Maggie Appleton 2021-09-05 15:37:21 +01:00
parent 6a2bde16e5
commit 6792dabecb
27 changed files with 4436 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/node_modules
node_modules
/.next
.next

47
components/Card.js Normal file
View file

@ -0,0 +1,47 @@
import { motion } from "framer-motion";
import Link from "next/link";
import Image from "next/image";
export default function Card({ post }) {
return (
<motion.li
className="w-60 mr-10 mb-14 space-y-2 opacity-80"
key={post.filePath}
whileHover={{
scale: 1.02,
opacity: 1,
transition: {
duration: 0.3,
ease: "easeInOut",
},
}}
>
<Link
as={`/posts/${post.filePath.replace(/\.md?$/, "")}`}
href={`/posts/[slug]`}
>
<a>
{post.data.image && (
<Image
className="rounded-md"
src={post.data.image}
width={250}
height={200}
alt={post.data.title}
/>
)}
<h3 className="text-coolGray-600 leading-tight mt-2">
{post.data.title}
</h3>
</a>
</Link>
<motion.div>
{post.data.tools &&
post.data.tools.map((tool) => <p>{tool}</p>)}
</motion.div>
<svg className="h-4 w-4 text-coolGray-600" viewBox="0 0 20 20">
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z" />
</svg>
</motion.li>
);
}

7
components/Container.js Normal file
View file

@ -0,0 +1,7 @@
export default function Container({ children }) {
return (
<div className="container flex flex-col sm:flex-row max-w-full">
{children}
</div>
);
}

9
components/Layout.js Normal file
View file

@ -0,0 +1,9 @@
import { motion } from "framer-motion";
export default function Layout({ children }) {
return (
<div className="container mt-24 mb-6 px-6 md:mx-auto h-full">
{children}
</div>
);
}

50
components/Sidemenu.js Normal file
View file

@ -0,0 +1,50 @@
import { motion } from "framer-motion";
import Link from "next/link";
export default function Sidemenu() {
return (
<motion.div
initial="hidden"
animate="visible"
variants={{
hidden: { opacity: 0, x: -50 },
visible: {
opacity: 1,
x: 0,
transition: {
delay: 0.6,
ease: "easeInOut",
duration: 1,
},
},
}}
className="flex flex-col space-y-3 mt-28 ml-6 w-60 mr-12"
>
<Link href="/what">
<a className="text-teal-700 hover:text-teal-900 text-base opacity-80 hover:opacity-100 transition-all duration-350 leading-tight font-bold">
What's a Digital Garden?
</a>
</Link>
<Link href="/directory">
<a className="text-teal-700 hover:text-teal-900 text-base opacity-80 hover:opacity-100 transition-all duration-350 leading-tight">
Garden Directory
</a>
</Link>
<Link href="/tools">
<a className="text-teal-700 hover:text-teal-900 text-base opacity-80 hover:opacity-100 transition-all duration-350 leading-tight">
Tools for Gardening
</a>
</Link>
<Link href="/tutorials">
<a className="text-teal-700 hover:text-teal-900 text-base opacity-80 hover:opacity-100 transition-all duration-350 leading-tight">
Gardening Tutorials and Guides
</a>
</Link>
<Link href="/theory">
<a className="text-teal-700 hover:text-teal-900 text-base opacity-80 hover:opacity-100 transition-all duration-350 leading-tight">
Gardening Theory and Navel-Gazing
</a>
</Link>
</motion.div>
);
}

5
next.config.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
images: {
domains: ["via.placeholder.com"],
},
};

27
package.json Normal file
View file

@ -0,0 +1,27 @@
{
"private": true,
"scripts": {
"dev": "next",
"dev:watch": "next-remote-watch ./posts",
"build": "next build",
"start": "next start",
"postbuild": "node ./scripts/build-search.js"
},
"dependencies": {
"@tailwindcss/typography": "^0.4.1",
"algoliasearch": "^4.10.5",
"dotenv": "^10.0.0",
"framer-motion": "^4.1.17",
"gray-matter": "^4.0.2",
"next": "latest",
"next-mdx-remote": "^3.0.1",
"next-remote-watch": "1.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"autoprefixer": "^10.3.4",
"postcss": "^8.3.6",
"tailwindcss": "^2.2.9"
}
}

7
pages/_app.js Normal file
View file

@ -0,0 +1,7 @@
import '../styles/tailwind.css'
function MyApp({Component, pageProps}) {
return <Component {...pageProps} />
}
export default MyApp;

9
pages/directory.js Normal file
View file

@ -0,0 +1,9 @@
import Layout from "../components/Layout";
export default function Directory() {
return (
<Layout>
<div>hello</div>
</Layout>
);
}

80
pages/index.js Normal file
View file

@ -0,0 +1,80 @@
import fs from "fs";
import matter from "gray-matter";
import Link from "next/link";
import path from "path";
import Container from "../components/Container";
import Layout from "../components/Layout";
import Sidemenu from "../components/Sidemenu";
import Card from "../components/Card";
import { gardensFilePath, GARDENS_PATH } from "../utils/mdUtils";
import { motion } from "framer-motion";
export default function Index({ posts }) {
return (
<Container>
<Sidemenu />
<Layout>
<motion.div
initial="hidden"
animate="visible"
variants={{
hidden: { opacity: 0, y: -50 },
visible: {
opacity: 1,
y: 0,
transition: {
delay: 0.1,
ease: "easeInOut",
duration: 0.7,
},
},
}}
>
<h1 className="sm:text-4xl text-coolGray-700 mb-6 font-semibold">
Garden of Digital Gardens
</h1>
<p className="text-xl text-coolGray-600 font-serif font-light max-w-5xl leading-tight">
A collection of digital gardens, tools, and resources
for gardeners
</p>
</motion.div>
<motion.ul
initial="hidden"
animate="show"
variants={{
hidden: { opacity: 0, y: 50 },
show: {
opacity: 1,
y: 0,
transition: {
delay: 0.6,
ease: "easeInOut",
duration: 0.7,
},
},
}}
className="flex flex-wrap mt-24"
>
{posts.map((post) => (
<Card post={post} />
))}
</motion.ul>
</Layout>
</Container>
);
}
export function getStaticProps() {
const posts = gardensFilePath.map((filePath) => {
const source = fs.readFileSync(path.join(GARDENS_PATH, filePath));
const { content, data } = matter(source);
return {
content,
data,
filePath,
};
});
return { props: { posts } };
}

78
pages/posts/[slug].js Normal file
View file

@ -0,0 +1,78 @@
import fs from "fs";
import matter from "gray-matter";
import { MDXRemote } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import dynamic from "next/dynamic";
import Head from "next/head";
import Link from "next/link";
import path from "path";
import Layout from "../../components/Layout";
import { gardensFilePath, GARDENS_PATH } from "../../utils/mdUtils";
// Custom components/renderers to pass to MDX.
// Since the MDX files aren't loaded by webpack, they have no knowledge of how
// to handle import statements. Instead, you must include components in scope
// here.
const components = {
// It also works with dynamically-imported components, which is especially
// useful for conditionally loading components for certain routes.
// See the notes in README.md for more details.
Head,
};
export default function GardenPage({ source, frontMatter }) {
return (
<Layout>
<header>
<nav>
<Link href="/">
<a>Go back home</a>
</Link>
</nav>
</header>
<div>
<h1>{frontMatter.title}</h1>
{frontMatter.description && <p>{frontMatter.description}</p>}
</div>
<main>
<MDXRemote {...source} components={components} />
</main>
</Layout>
);
}
export const getStaticProps = async ({ params }) => {
const gardenFilePath = path.join(GARDENS_PATH, `${params.slug}.md`);
const source = fs.readFileSync(gardenFilePath);
const { content, data } = matter(source);
const mdxSource = await serialize(content, {
// Optionally pass remark/rehype plugins
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [],
},
scope: data,
});
return {
props: {
source: mdxSource,
frontMatter: data,
},
};
};
export const getStaticPaths = async () => {
const paths = gardensFilePath
// Remove file extensions for page paths
.map((path) => path.replace(/\.md?$/, ""))
// Map the path into the static paths object required by Next.js
.map((slug) => ({ params: { slug } }));
return {
paths,
fallback: false,
};
};

9
pages/theory.js Normal file
View file

@ -0,0 +1,9 @@
import Layout from "../components/Layout";
export default function Theory() {
return (
<Layout>
<div>hello</div>
</Layout>
);
}

9
pages/tools.js Normal file
View file

@ -0,0 +1,9 @@
import Layout from "../components/Layout";
export default function Tools() {
return (
<Layout>
<div>hello</div>
</Layout>
);
}

9
pages/tutorials.js Normal file
View file

@ -0,0 +1,9 @@
import Layout from "../components/Layout";
export default function Tutorials() {
return (
<Layout>
<div>hello</div>
</Layout>
);
}

9
pages/what.js Normal file
View file

@ -0,0 +1,9 @@
import Layout from "../components/Layout";
export default function What() {
return (
<Layout>
<div>hello</div>
</Layout>
);
}

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -0,0 +1,10 @@
---
title: Andy Matuschak
description: Algorithms that make themselves known
topics: ['evergreen notes', 'education', 'tools for thought']
url: https://notes.andymatuschak.org/
image: https://via.placeholder.com/200x250
tools: ['mystery system']
---

View file

@ -0,0 +1,5 @@
---
title: Bill Seitz
image: https://via.placeholder.com/250
tools: ['']
---

View file

@ -0,0 +1,7 @@
---
title: Buster Benson
image: https://via.placeholder.com/250
tools: ['']
---
This is an example post

View file

@ -0,0 +1,5 @@
---
title: Gwern Branden
image: https://via.placeholder.com/200x250
tools: ['']
---

View file

@ -0,0 +1,6 @@
---
title: Joel Hooks
description:
image: https://via.placeholder.com/250x200
tools: ['next.js']
---

View file

@ -0,0 +1,5 @@
---
title: Tom Critchlow
image: https://via.placeholder.com/250x100
tools: ['jekyll']
---

23
scripts/build-search.js Normal file
View file

@ -0,0 +1,23 @@
const dotenv = require("dotenv");
const fetch = require("node-fetch");
const algoliasearch = require("algoliasearch/lite");
async function getAllBlogPosts() {
// write your code to fetch your data
}
(async function () {
// initialize environment variables
dotenv.config();
try {
// fetch your data
const posts = await getAllBlogPosts();
}
} catch (error) {
console.log(error);
}
})();
// To be continued: https://www.contentful.com/blog/2021/07/02/add-algolia-instantsearch-to-nextjs-app/

39
styles/tailwind.css Normal file
View file

@ -0,0 +1,39 @@
/* @import url(/fonts/stylesheet.css); */
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,400;0,600;1,400&family=Source+Serif+Pro:wght@300;400&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
h1 {
@apply text-3xl;
@apply leading-none;
}
h2 {
@apply text-2xl;
@apply leading-none;
}
h3 {
@apply text-xl;
@apply leading-none;
}
p {
@apply text-base;
}
}
body {
height: 100%;
background-repeat: no-repeat;
background-attachment: fixed;
background-color: rgb(243, 250, 218);
background: linear-gradient(
180deg,
rgb(245, 255, 247) 0%,
rgb(246, 255, 240) 70%,
rgb(213, 240, 218) 100%
);
}
/* https://tailwindcss.com/docs/adding-base-styles */

48
tailwind.config.js Normal file
View file

@ -0,0 +1,48 @@
const defaultTheme = require("tailwindcss/defaultTheme");
const colors = require("tailwindcss/colors");
module.exports = {
purge: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
fontFamily: {
sans: ["Poppins", ...defaultTheme.fontFamily.sans],
serif: ["Source Serif Pro", ...defaultTheme.fontFamily.mono],
},
fontSize: {
// Set in Perfect Fourth typescale (1.33)
base: "1em",
lg: "1.333em",
xl: "1.777em",
"2xl": "2.369em",
"3xl": "3.157em",
"4xl": "4.2em",
"5xl": "5.61em",
},
colors: {
...colors,
primary: colors.blue["500"],
secondary: colors.blue["700"],
accent: colors.blue["800"],
success: colors.green["500"],
warning: colors.orange["500"],
error: colors.red["500"],
},
},
},
variants: {
extend: {},
},
plugins: [
require("@tailwindcss/typography"),
// https://github.com/tailwindlabs/tailwindcss-typography
],
};
// https://tailwindcss.com/docs/theme
//https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js#L7

12
utils/mdUtils.js Normal file
View file

@ -0,0 +1,12 @@
import fs from "fs";
import path from "path";
// POSTS_PATH is useful when you want to get the path to a specific file
export const GARDENS_PATH = path.join(process.cwd(), "posts", "gardens");
export const TOOLS_PATH = path.join(process.cwd(), "posts", "tools");
// patternFilePath is the list of all mdx files inside the POSTS_PATH directory
export const gardensFilePath = fs
.readdirSync(GARDENS_PATH)
// Only include md(x) files
.filter((path) => /\.md?$/.test(path));

3911
yarn.lock Normal file

File diff suppressed because it is too large Load diff