From 9f4737320e49ee59b37de87e4de0cbf44e5a8e05 Mon Sep 17 00:00:00 2001 From: xypnox Date: Wed, 29 Nov 2023 19:56:07 +0530 Subject: [PATCH 01/16] Refactor a few files WIP --- .prettierrc | 2 +- .../AnimatedBackground.jsx | 103 ++++++-- .../AnimatedBackground.styled.jsx | 14 ++ components/blog/PostPreview/PostPreview.tsx | 110 +++++---- components/features-grid/FeaturesGrid.jsx | 28 ++- .../features-grid/FeaturesGrid.styled.jsx | 19 +- components/footer/Footer.jsx | 99 -------- components/footer/Footer.tsx | 77 ++++++ components/forms/button.tsx | 20 ++ components/header/Header.jsx | 155 ------------ components/header/Header.tsx | 72 ++++++ components/logo/CSLogo.tsx | 27 +++ components/logo/YCLogo.tsx | 24 ++ components/sections/hero.tsx | 66 ++++++ next.config.js | 43 ++-- package.json | 74 +++--- pages/_app.page.js | 66 +++--- pages/blog/index.page.tsx | 94 ++++---- pages/index.page.tsx | 223 +++++++----------- styles/CodeStory.colours.jsx | 7 +- styles/Crucible.dark.theme.jsx | 5 +- styles/Global.styled.jsx | 19 +- tsconfig.json | 76 +++--- 23 files changed, 726 insertions(+), 697 deletions(-) delete mode 100644 components/footer/Footer.jsx create mode 100644 components/footer/Footer.tsx create mode 100644 components/forms/button.tsx delete mode 100644 components/header/Header.jsx create mode 100644 components/header/Header.tsx create mode 100644 components/logo/CSLogo.tsx create mode 100644 components/logo/YCLogo.tsx create mode 100644 components/sections/hero.tsx diff --git a/.prettierrc b/.prettierrc index 800824a..0e00479 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { "bracketSpacing": true, "printWidth": 120, - "tabWidth": 4, + "tabWidth": 2, "trailingComma": "es5", "useTabs": false } diff --git a/components/animated-background/AnimatedBackground.jsx b/components/animated-background/AnimatedBackground.jsx index 7cd3f1e..3739142 100644 --- a/components/animated-background/AnimatedBackground.jsx +++ b/components/animated-background/AnimatedBackground.jsx @@ -2,72 +2,125 @@ import React from "react"; import { AnimatedBackgroundStyled } from "./AnimatedBackground.styled"; - const AnimatedBackground = () => { return (
+ - - + + - - - + + + - - - - + + + + - - + + - - + + - - + + - + + attributeName="transform" + type="rotate" + from="0 50 50" + to="360 50 50" + dur="40s" + repeatCount="indefinite" + /> - + + attributeName="transform" + type="rotate" + from="0 50 50" + to="360 50 50" + dur="80s" + repeatCount="indefinite" + /> - + + attributeName="transform" + type="rotate" + from="360 50 50" + to="0 50 50" + dur="50s" + repeatCount="indefinite" + />
diff --git a/components/animated-background/AnimatedBackground.styled.jsx b/components/animated-background/AnimatedBackground.styled.jsx index cc5fba3..de80a02 100644 --- a/components/animated-background/AnimatedBackground.styled.jsx +++ b/components/animated-background/AnimatedBackground.styled.jsx @@ -9,4 +9,18 @@ export const AnimatedBackgroundStyled = styled(motion.div)` width : 100%; height : 100%; opacity : 0.24; + svg { + --color1-start: rgba(255, 292, 255, 1); + --color1-end: rgba(99, 292, 255, 0); + --color2-start: rgba(60, 186, 146, 1); + --color2-end: rgba(60, 186, 146, 0); + --color3-start: rgba(0, 255, 255, 1); + --color3-end: rgba(0, 255, 255, 0); + --color4-start: rgba(0, 255, 0, 1); + --color4-end: rgba(0, 255, 0, 0); + --color5-start: rgba(0,0,255, 1); + --color5-end: rgba(0,0,255, 0); + --color6-start: rgba(255,0,0, 1); + --color6-end: rgba(255,0,0, 0); + } `; diff --git a/components/blog/PostPreview/PostPreview.tsx b/components/blog/PostPreview/PostPreview.tsx index 5de8329..3f0c229 100644 --- a/components/blog/PostPreview/PostPreview.tsx +++ b/components/blog/PostPreview/PostPreview.tsx @@ -1,57 +1,77 @@ import Image from "next/image"; import Link from "next/link"; -import { Card, Element, Heading, Portion, Row, Text } from "fictoan-react"; +import styled from "styled-components"; -import { PostPreviewStyled } from "@/components/blog/PostPreview/PostPreview.styled"; import DateFormatter from "@/utils/date-formatter"; import type Author from "@/interfaces/author"; +const PostPreviewStyled = styled.div` + /* Add your custom styles here */ +`; + +const Card = styled.div` + /* Add your custom styles here */ +`; + +const Element = styled.div` + /* Add your custom styles here */ +`; + +const Heading = styled.h5` + /* Add your custom styles here */ +`; + +const Portion = styled.div` + /* Add your custom styles here */ +`; + +const Row = styled.div` + /* Add your custom styles here */ +`; + +const Text = styled.p` + /* Add your custom styles here */ +`; + type Props = { - title: string; - coverImage: string; - date: string; - excerpt: string; - author: Author; - slug: string; + title: string; + coverImage: string; + date: string; + excerpt: string; + author: Author; + slug: string; }; -const PostPreview = ({ - title, - date, - excerpt, - slug, - author, -}: Props) => { - return ( - - - - - {title} - - - - - - - - - {author.name} - - - - - - - - - - - - ); +const PostPreview = ({ title, date, excerpt, slug, author }: Props) => { + return ( + + + + + + {title} + + + + + + + + + + {author.name} + + + + + + + + + + + + ); }; export default PostPreview; diff --git a/components/features-grid/FeaturesGrid.jsx b/components/features-grid/FeaturesGrid.jsx index 64c0b1b..b092a1f 100644 --- a/components/features-grid/FeaturesGrid.jsx +++ b/components/features-grid/FeaturesGrid.jsx @@ -1,6 +1,6 @@ import React, { useRef, useEffect } from "react"; -import { Card, Element, Heading, Text, Portion, Row, HRule } from "fictoan-react"; +import { Card, Element, Heading, Text, Portion, Row, HRule, CodeBlock } from "fictoan-react"; import Debugging from "../../public/images/home/debugging.mp4"; import NLPSearch from "../../public/images/home/nlp-search.png"; @@ -22,6 +22,7 @@ import LogoGo from "../../public/images/logo-go.svg"; import { FeaturesGridStyled } from "./FeaturesGrid.styled"; import { Player } from "video-react"; import { DownloadButtons } from "../download/download"; +import { CodeStoryColours } from "../../styles/CodeStory.colours"; const FeaturesGrid = ({ matchingRelease, latestRelease, os }) => { @@ -57,7 +58,7 @@ const FeaturesGrid = ({ matchingRelease, latestRelease, os }) => { {/* ////////////////////////////////////////////////////////////////////////////////////////////////// */} {/* { - + {/* */} {/* */} @@ -168,7 +169,8 @@ const FeaturesGrid = ({ matchingRelease, latestRelease, os }) => { { Make changes to your codebase @@ -203,11 +205,11 @@ const FeaturesGrid = ({ matchingRelease, latestRelease, os }) => { Debug issues in the code @@ -219,11 +221,11 @@ const FeaturesGrid = ({ matchingRelease, latestRelease, os }) => { Understand unfamiliar code @@ -235,11 +237,11 @@ const FeaturesGrid = ({ matchingRelease, latestRelease, os }) => { Eliminate all your tech debt @@ -374,7 +376,7 @@ const FeaturesGrid = ({ matchingRelease, latestRelease, os }) => { {featureItem.heading} diff --git a/components/features-grid/FeaturesGrid.styled.jsx b/components/features-grid/FeaturesGrid.styled.jsx index 2e42fdf..5a9eabe 100644 --- a/components/features-grid/FeaturesGrid.styled.jsx +++ b/components/features-grid/FeaturesGrid.styled.jsx @@ -33,7 +33,7 @@ export const FeaturesGridStyled = styled.section` overflow : hidden; backdrop-filter : blur(6px); background : radial-gradient(circle at top center, - ${transparentize(0.6, CodeStoryColours.canopy)} 0%, ${transparentize(0.6, CodeStoryColours.shrub)} 70%); + ${transparentize(0.6, CodeStoryColours.primary)} 0%, ${transparentize(0.6, CodeStoryColours.ash)} 70%); } // AIDE INTRO CARD @@ -84,21 +84,17 @@ export const FeaturesGridStyled = styled.section` padding: 1px; } - .prompt-examples { - padding : 4px; - border-radius : 16px; - background : linear-gradient(0.2turn, #bffa4a, #66bd43); - border : none; - } - #prompt-examples img { border-radius: 16px; } .prompt-wrapper { - background : linear-gradient(0.2turn, #82de5d, #bffa4a); + background : ${CodeStoryColours.shrub}; border : none; } + .prompt-wrapper .card-text { + color: ${CodeStoryColours.primary}; + } // CODE EXPLANATIONS ====================================================== #code-explanation { @@ -178,7 +174,7 @@ export const FeaturesGridStyled = styled.section` .kbd { background-color: ${CodeStoryColours.shrub}; color: ${CodeStoryColours.ash}; - border: 1px solid ${CodeStoryColours.undergrowth}; + border: 1px solid ${CodeStoryColours.primary}; border-radius: 12px; border-width: 1px 1px 3px 1px; padding: 8px !important; @@ -245,9 +241,6 @@ export const FeaturesGridStyled = styled.section` width : 100%; inset : 0px; border-radius : inherit; - background : radial-gradient(800px circle at var(--x-px) var(--y-px), - rgba(60, 186, 146, 0.4), - transparent 40%); } .feature:before { z-index : -1; } diff --git a/components/footer/Footer.jsx b/components/footer/Footer.jsx deleted file mode 100644 index 05302b0..0000000 --- a/components/footer/Footer.jsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from "react"; -import Link from "next/link"; - -import { Card, Element, HRule, Portion, Row, Text } from "fictoan-react"; - -import { FooterStyled } from "./Footer.styled"; - -import CSLogo from "../../public/images/logo-cs.svg"; -import YCLogo from "../../public/images/logo-yc.svg"; - - -const Footer = () => { - return ( - - - - - - - - - - - - - - - Backed by - - - - - - - - - - - Docs - - - - - Blog - - - - Changelog - - - - About - - - - - - - - YC - - - - Discord - - - - Twitter - - - - LinkedIn - - - - - - - - ); -}; - -export default Footer; diff --git a/components/footer/Footer.tsx b/components/footer/Footer.tsx new file mode 100644 index 0000000..dc7a13c --- /dev/null +++ b/components/footer/Footer.tsx @@ -0,0 +1,77 @@ +import React from "react"; +import Link from "next/link"; +import styled from "styled-components"; + +import { FooterStyled } from "./Footer.styled"; + +import { CSLogoText as CSLogo } from "../logo/CSLogo"; +import { YCLogo } from "../logo/YCLogo"; + +const Backedby = styled.div` + display: flex; + align-items: center; + gap: 1rem; +`; + +const Row = styled.div` + display: flex; + align-items: center; + gap: 1rem; +`; + +const Footer = () => { + const links = [ + { href: "https://docs.codestory.ai", label: "Docs", external: true }, + { href: "/blog", label: "Blog" }, + { href: "/changelog", label: "Changelog" }, + { href: "/about", label: "About" }, + ]; + + const socialLinks = [ + { + href: "https://www.ycombinator.com/launches/JCn-codestory-an-ai-first-ide-re-imagined-for-the-future", + label: "YC", + external: true, + }, + { href: "https://discord.gg/DNnh6tC9VA", label: "Discord", external: true }, + { href: "https://twitter.com/codestoryai", label: "Twitter", external: true }, + { href: "https://www.linkedin.com/company/codestory-ai", label: "LinkedIn", external: true }, + ]; + + return ( + + + + + + + Backed by + + + + + {links.map((link, index) => ( + + {link.external ? ( + + {link.label} + + ) : ( + {link.label} + )} + + ))} + + + + {socialLinks.map((link, index) => ( + + {link.label} + + ))} + + + ); +}; + +export default Footer; diff --git a/components/forms/button.tsx b/components/forms/button.tsx new file mode 100644 index 0000000..f96d203 --- /dev/null +++ b/components/forms/button.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import styled from "styled-components"; + +const ButtonStyled = styled.div` + padding: 1rem; + background-color: #000; + display: flex; + align-items: center; + width: max-content; + border-radius: 4px; +`; + +interface ButtonProps extends React.HTMLAttributes { + onClick: () => void; + disabled?: boolean; +} + +export const Button = (props: ButtonProps) => { + return ; +}; diff --git a/components/header/Header.jsx b/components/header/Header.jsx deleted file mode 100644 index 721bc9b..0000000 --- a/components/header/Header.jsx +++ /dev/null @@ -1,155 +0,0 @@ -import React, { useState, useRef, useEffect } from "react"; -import Link from "next/link"; -import { Heading, Element, Portion, Row, Text, HRule } from "fictoan-react"; - -import { HeaderStyled } from "./Header.styled"; -import CSLogo from "../../public/images/logo-cs.svg"; -import CSLogoMark from "../../public/assets/logos/cs-logomark.svg"; -import YCLogo from "../../public/images/logo-yc.svg"; - -const Header = () => { - const node = useRef(); - const [showMobileHeader, setShowMobileHeader] = useState(false); - - const handleRedirect = () => { - setShowMobileHeader(false); - } - - const handleClickOutside = (e) => { - if (node.current.contains(e.target)) { - return; - } - setShowMobileHeader(false); - }; - - useEffect(() => { - if (showMobileHeader) { - document.addEventListener("mousedown", handleClickOutside); - } else { - document.removeEventListener("mousedown", handleClickOutside); - } - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [showMobileHeader]); - - return ( - - - - - - - - - - - - - - — - - - backed by - - - - - - - - - DOCS - - - - BLOG - - - CHANGELOG - - - ABOUT - - - setShowMobileHeader(!showMobileHeader)} - > - - — - - - - – - - - - - - - - - - - - - setShowMobileHeader(false)}> - × - - - - - - - - - - - Docs - - - - - - - - - - Blog - - - - - - - - - Changelog - - - - - - - - - About - - - - - - - - - ); -}; - -export default Header; diff --git a/components/header/Header.tsx b/components/header/Header.tsx new file mode 100644 index 0000000..8a195a5 --- /dev/null +++ b/components/header/Header.tsx @@ -0,0 +1,72 @@ +import React, { useState, useRef, useEffect } from "react"; +import Link from "next/link"; + +import { CSLogoText } from "../logo/CSLogo"; +import { YCLogo } from "../logo/YCLogo"; + +import styled from "styled-components"; +import { CodeStoryColours } from "../../styles/CodeStory.colours"; + +const HeaderStyled = styled.header` + position: sticky; + top: 0; + display: flex; + align-items: center; + justify-content: space-between; + z-index: 100; + color: ${CodeStoryColours.primary}; + background-color: ${CodeStoryColours.ash}; +`; + +const Header = () => { + const node = useRef(null); + const [showMobileHeader, setShowMobileHeader] = useState(false); + const handleRedirect = () => { + setShowMobileHeader(false); + }; + const handleClickOutside = (event: MouseEvent) => { + if (node.current && !node.current.contains(event.target as Node)) { + setShowMobileHeader(false); + } + }; + + useEffect(() => { + if (showMobileHeader) { + document.addEventListener("mousedown", handleClickOutside); + } else { + document.removeEventListener("mousedown", handleClickOutside); + } + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [showMobileHeader]); + + const menuItems = [ + { url: "https://docs.codestory.ai", title: "Docs", external: true }, + { url: "/blog", title: "Blog" }, + { url: "/changelog", title: "Changelog" }, + { url: "/about", title: "About" }, + ]; + + return ( + + + + +
+ backed by + +
+ {menuItems.map((item) => ( +

+ {item.title} +

+ ))} + +
+ ); +}; + +export default Header; diff --git a/components/logo/CSLogo.tsx b/components/logo/CSLogo.tsx new file mode 100644 index 0000000..b7ba2d7 --- /dev/null +++ b/components/logo/CSLogo.tsx @@ -0,0 +1,27 @@ +import React from "react"; + +const CSLogo = () => { + return ( + + + + + ); +}; + +const CSLogoText = () => { + return ( +
+ + CodeStory +
+ ); +}; + +export { CSLogo, CSLogoText }; diff --git a/components/logo/YCLogo.tsx b/components/logo/YCLogo.tsx new file mode 100644 index 0000000..d48c6df --- /dev/null +++ b/components/logo/YCLogo.tsx @@ -0,0 +1,24 @@ +import React from "react"; + +export const YCLogo = () => { + return ( + + + + + + + + + + + + + ); +}; diff --git a/components/sections/hero.tsx b/components/sections/hero.tsx new file mode 100644 index 0000000..848ae8e --- /dev/null +++ b/components/sections/hero.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import styled from "styled-components"; +import { DownloadButtons } from "../../components/download/download"; + +const StyledRow = styled.div` + margin-top: ${(props) => props.marginTop}; + margin-bottom: ${(props) => props.marginBottom}; +`; + +const StyledPortion = styled.div` + flex: ${(props) => props.desktopSpan}; + margin-bottom: ${(props) => props.marginBottom}; +`; + +const StyledHeading = styled.h2` + color: ${(props) => props.textColour}; + font-weight: ${(props) => props.weight}; + opacity: ${(props) => props.opacity}; + margin-bottom: ${(props) => props.marginBottom}; +`; + +const StyledSubHeading = styled.h5` + color: ${(props) => props.textColour}; + font-weight: ${(props) => props.weight}; + opacity: ${(props) => props.opacity}; + margin-bottom: ${(props) => props.marginBottom}; +`; + +interface HeroProps { + matchingRelease: string; + latestRelease: string; + os: string; +} + +const Hero: React.FC = ({ matchingRelease, latestRelease, os }) => { + return ( + + + Introducing Aide. + + + The AI-powered mod of VSCode. + + + + + Instruct AI agents to build your software. + + + + Editing across files, searching, debugging, refactoring—our agent can do everything you do within the IDE. And + Aide is built on VSCode, so you can migrate seamlessly and continue using your favourite extensions. + + + + + + ); +}; + +export default Hero; diff --git a/next.config.js b/next.config.js index b1cdca2..0745ae1 100644 --- a/next.config.js +++ b/next.config.js @@ -1,25 +1,28 @@ const withVideos = require('next-videos'); module.exports = withVideos({ - pageExtensions : ["page.tsx", "page.ts", "page.jsx", "page.js"], - webpack(config, options) { - config.module.rules.push({ - test : /\.svg$/, - use : ["@svgr/webpack"] - }); - config.module.rules.push({ - test : /\.(woff|woff2|eot|ttf|otf)$/i, - type : "javascript/auto", - use : [{ - loader : "file-loader", - options : { - name : "[hash].[ext]", - outputPath : "static/fonts/", - publicPath : "/_next/static/fonts/" - } - }] - }); + pageExtensions: ["page.tsx", "page.ts", "page.jsx", "page.js"], - return config; - } + webpack(config, options) { + + config.module.rules.push({ + test: /\.svg$/, + use: ["@svgr/webpack"] + }); + + config.module.rules.push({ + test: /\.(woff|woff2|eot|ttf|otf)$/i, + type: "javascript/auto", + use: [{ + loader: "file-loader", + options: { + name: "[hash].[ext]", + outputPath: "static/fonts/", + publicPath: "/_next/static/fonts/" + } + }] + }); + + return config; + } }); diff --git a/package.json b/package.json index 45d93a1..6a9b796 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,39 @@ { - "name": "boilerplate-next", - "version": "0.1.4", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start" - }, - "dependencies": { - "@svgr/webpack": "^6.2.1", - "@types/ua-parser-js": "^0.7.37", - "date-fns": "^2.30.0", - "fictoan-react": "0.46.8", - "framer-motion": "7.6.1", - "gray-matter": "^4.0.3", - "next": "^12.3.1", - "next-seo": "^6.1.0", - "next-videos": "^1.4.1", - "polished": "^4.2.2", - "react": "18.2.0", - "react-dom": "^18.2.0", - "react-icons": "^4.10.1", - "react-markdown": "^8.0.7", - "react-syntax-highlighter": "^15.5.0", - "rehype-raw": "^6.1.1", - "remark-gfm": "^3.0.1", - "styled-components": "^5.3.0", - "ua-parser-js": "^1.0.36", - "video-react": "^0.16.0" - }, - "devDependencies": { - "@types/node": "^15.0.3", - "@types/react": "^17.0.5", - "@types/react-syntax-highlighter": "^15.5.7", - "file-loader": "^6.2.0", - "typescript": "5.1.6" - } + "name": "aide-website", + "version": "0.2.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@svgr/webpack": "^6.2.1", + "@types/ua-parser-js": "^0.7.37", + "date-fns": "^2.30.0", + "fictoan-react": "0.46.8", + "framer-motion": "7.6.1", + "gray-matter": "^4.0.3", + "next": "^12.3.1", + "next-seo": "^6.1.0", + "next-videos": "^1.4.1", + "polished": "^4.2.2", + "react": "18.2.0", + "react-dom": "^18.2.0", + "react-icons": "^4.10.1", + "react-markdown": "^8.0.7", + "react-syntax-highlighter": "^15.5.0", + "rehype-raw": "^6.1.1", + "remark-gfm": "^3.0.1", + "styled-components": "^5.3.0", + "ua-parser-js": "^1.0.36", + "video-react": "^0.16.0" + }, + "devDependencies": { + "@types/node": "^15.0.3", + "@types/react": "^17.0.5", + "@types/react-syntax-highlighter": "^15.5.7", + "file-loader": "^6.2.0", + "typescript": "5.1.6" + } } diff --git a/pages/_app.page.js b/pages/_app.page.js index c5bc0d4..8c32695 100644 --- a/pages/_app.page.js +++ b/pages/_app.page.js @@ -2,7 +2,7 @@ import React, { useState } from "react"; import Script from "next/script"; import { ThemeProvider } from "fictoan-react"; -import 'video-react/dist/video-react.css'; +import "video-react/dist/video-react.css"; import { CrucibleLightTheme } from "../styles/Crucible.light.theme"; import { CrucibleDarkTheme } from "../styles/Crucible.dark.theme"; import { GlobalStyle } from "../styles/Global.styled"; @@ -11,48 +11,48 @@ import { GlobalStyle } from "../styles/Global.styled"; import "../styles/fonts.css"; import Header from "../components/header/Header"; -function MyApp({Component, pageProps}) { - let [currentTheme, setCurrentTheme] = useState("dark"); +function MyApp({ Component, pageProps }) { + let [currentTheme, setCurrentTheme] = useState("dark"); - const toggleTheme = () => { - if (currentTheme === "light") { - setDocsTheme("dark"); - } else { - setDocsTheme("light"); - } + const toggleTheme = () => { + if (currentTheme === "light") { + setDocsTheme("dark"); + } else { + setDocsTheme("light"); } - - const setDocsTheme = (theme) => { - setCurrentTheme(theme); - localStorage.setItem("theme", theme); - } - - const modifiedPageProps = {...pageProps, toggleTheme} - - return ( - <> - + - - + + -
- - - - ); +
+ + + + ); } export default MyApp; diff --git a/pages/blog/index.page.tsx b/pages/blog/index.page.tsx index 6314323..c641d60 100644 --- a/pages/blog/index.page.tsx +++ b/pages/blog/index.page.tsx @@ -8,61 +8,61 @@ import PostPreview from "@/components/blog/PostPreview/PostPreview"; import Post from "@/interfaces/post"; type BlogHomeProps = { - allPosts: Post[]; + allPosts: Post[]; }; export default function BlogHome({ allPosts }: BlogHomeProps) { - const heroPost = allPosts[0]; - const morePosts = allPosts.slice(1); + const heroPost = allPosts[0]; + const morePosts = allPosts.slice(1); - return ( - - - - - Blog - - + return ( + + + + + Blog + + - - - {allPosts.map((post) => ( - - - - ))} - - - + + + {allPosts.map((post) => ( + + + + ))} + + + -