├── .github └── FUNDING.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── components ├── AnimatedCounter.jsx ├── docs │ ├── DocPager.jsx │ ├── DocsLayout.jsx │ └── TableOfContents.jsx ├── headerIcons.jsx ├── home │ ├── HeroSection.jsx │ ├── StargazersSection.jsx │ └── TabsSection.jsx ├── layout │ ├── Footer.jsx │ ├── Header.jsx │ └── Layout.jsx ├── loader.jsx ├── mdx-components.jsx └── ui │ ├── Accordions.jsx │ ├── Avatars.jsx │ ├── Backgrounds.jsx │ ├── Buttons.jsx │ ├── Cards.jsx │ ├── Carousels.jsx │ ├── Docks.jsx │ ├── Grids.jsx │ ├── Loaders.jsx │ ├── Marquees.jsx │ ├── Paginations.jsx │ ├── Pointers.jsx │ ├── Separators.jsx │ ├── Tables.jsx │ ├── Tabs.jsx │ └── Texts.jsx ├── content └── docs │ ├── accordions.mdx │ ├── avatars.mdx │ ├── backgrounds.mdx │ ├── buttons.mdx │ ├── cards.mdx │ ├── carousels.mdx │ ├── changelog.mdx │ ├── docks.mdx │ ├── grids.mdx │ ├── index.mdx │ ├── loaders.mdx │ ├── marquees.mdx │ ├── paginations.mdx │ ├── pointers.mdx │ ├── separators.mdx │ ├── tables.mdx │ ├── tabs.mdx │ └── texts.mdx ├── context ├── GithubContext.js └── ThemeContext.js ├── jsconfig.json ├── lib └── docs.js ├── next.config.mjs ├── package-lock.json ├── package.json ├── pages ├── 404.js ├── _app.js ├── _document.js ├── docs │ └── [[...slug]].jsx ├── index.js └── templates │ └── index.jsx ├── public ├── apple-touch-icon.png ├── default-og-image.png ├── favicon.ico ├── logo.png ├── next.svg ├── vercel.svg └── videos │ ├── Portfolio-Template.mov │ ├── SaaS-Template.mp4 │ ├── Startup-Template.mov │ └── nature.mp4 ├── styles └── globals.css ├── theme └── index.js └── utils └── constants.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [AbhiVarde] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node modules 2 | node_modules/ 3 | 4 | # Env files 5 | .env 6 | .env.* 7 | 8 | # Logs 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # OS-related 15 | .DS_Store 16 | Thumbs.db 17 | 18 | # Build output 19 | dist/ 20 | .next/ 21 | out/ 22 | build/ 23 | 24 | # IDEs/editors 25 | .vscode/ 26 | .idea/ 27 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | abhivarde.contact@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Sync UI 2 | 3 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 4 | 5 | - Reporting a bug 6 | - Discussing the current state of the code 7 | - Submitting a fix 8 | - Proposing new features 9 | 10 | ## We Develop with Github 11 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 12 | 13 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html) 14 | Pull requests are the best way to propose changes to the codebase. 15 | 16 | 1. Fork the repo and create your branch from `main`. 17 | 2. If you've added code that should be tested, add tests. 18 | 3. If you've changed APIs, update the documentation. 19 | 4. Ensure the test suite passes. 20 | 5. Make sure your code lints. 21 | 6. Issue that pull request! 22 | 23 | ## Any contributions you make will be under the MIT Software License 24 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. 25 | 26 | ## Report bugs using Github's [issues](https://github.com/AbhiVarde/syncui/issues) 27 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/AbhiVarde/syncui/issues/new); it's that easy! 28 | 29 | ## Write bug reports with detail, background, and sample code 30 | 31 | **Great Bug Reports** tend to have: 32 | 33 | - A quick summary and/or background 34 | - Steps to reproduce 35 | - Be specific! 36 | - Give sample code if you can. 37 | - What you expected would happen 38 | - What actually happens 39 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 40 | 41 | ## Use a Consistent Coding Style 42 | 43 | * 2 spaces for indentation rather than tabs 44 | * You can try running `npm run lint` for style unification 45 | 46 | ## License 47 | By contributing, you agree that your contributions will be licensed under its [MIT License](http://choosealicense.com/licenses/mit/). 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sync UI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

🚀 Introducing Sync UI ✨

3 |

A sleek UI library for Design Engineers, offering beautifully designed components built with MUI and Framer Motion.

4 | Twitter Follow 5 | License 6 |

7 | default-og-image 8 |

9 |
10 | 11 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/AbhiVarde/abhivarde.in) 12 | 13 | # syncui.design 14 | 15 | - **Library**: [React](https://react.dev/) 16 | - **Framework**: [Next.js](https://nextjs.org/) 17 | - **Styling**: [Material UI](https://mui.com/) 18 | - **Animation**: [Framer Motion](https://www.framer.com/motion/) 19 | - **Analytics**: [Vercel Analytics](https://vercel.com/analytics) 20 | - **Deployment**: [Vercel](https://vercel.com) 21 | 22 | ## Features 23 | 24 | - 🎨 Beautifully designed components 25 | - 📁 Ready-made templates for faster builds 26 | - 🛠️ Built with MUI and Framer Motion 27 | - ♿ Accessible and customizable 28 | - 🆓 100% Free and Open Source. 29 | - 🚀 Perfect for your next project 30 | 31 | ## Templates 32 | 33 | Explore our fresh, production-ready templates 🚀 34 | 35 | - 🚀 [Startup Template – $29](https://abhivarde.gumroad.com/l/startup-template-syncui) 36 | - 🧩 [SaaS Template – $29](https://abhivarde.gumroad.com/l/saas-template-syncui) 37 | - 🎯 [Portfolio Template – $29](https://abhivarde.gumroad.com/l/portfolio-template-syncui) 38 | - 🧃 [Bundle (All 3 Templates) – $79](https://abhivarde.gumroad.com/l/syncui-templates-bundle) 39 | 40 | → [See all templates](https://syncui.design/templates) 41 | 42 | *Tip: Right-click on links and select "Open in new tab" to keep this page open while browsing templates.* 43 | 44 | ## Documentation 45 | 46 | Visit [https://syncui.design/docs](https://syncui.design/docs) to view the full documentation and get started with Sync UI. 47 | 48 | --- 49 | 50 | 💖 **Love Sync UI? Support with a one-time gift!** 51 | From ☕ **$9** to keep development rolling, to 🚀 **$999** for a full promo of your work. 52 | [Become a Sponsor →](https://github.com/sponsors/AbhiVarde) 53 | 54 | --- 55 | 56 | ## Contributing 57 | 58 | We truly ❤️ pull requests! If you wish to help, you can learn more about how you can contribute to this project in the [contribution guide](https://github.com/AbhiVarde/syncui/blob/main/CONTRIBUTING.md). 59 | 60 | ## Let's talk 61 | 62 | - [Twitter](https://x.com/syncuidesign) 63 | 64 | ## License 65 | 66 | Sync UI is licensed under the [MIT License](http://choosealicense.com/licenses/mit/). All rights reserved. 67 | 68 | ## Authors 69 | 70 | Sync UI is created and maintained by [Abhi Varde](https://www.abhivarde.in/). 71 | -------------------------------------------------------------------------------- /components/AnimatedCounter.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, memo, useRef } from "react"; 2 | 3 | const AnimatedCounter = memo( 4 | ({ 5 | value, 6 | duration = 2, 7 | className = "", 8 | formatter = (val) => val.toLocaleString(), 9 | delay = 0, 10 | }) => { 11 | const [counter, setCounter] = useState(0); 12 | const previousValueRef = useRef(0); 13 | const isFirstRender = useRef(true); 14 | 15 | useEffect(() => { 16 | if (isFirstRender.current) { 17 | setCounter(value); 18 | previousValueRef.current = value; 19 | isFirstRender.current = false; 20 | return; 21 | } 22 | 23 | if (value === previousValueRef.current) { 24 | return; 25 | } 26 | 27 | // Animate from previous value to new value 28 | const startValue = previousValueRef.current; 29 | const endValue = value; 30 | const difference = endValue - startValue; 31 | 32 | if (difference === 0) return; 33 | 34 | const steps = Math.abs(difference); 35 | const incrementValue = difference / steps; 36 | const incrementTime = (duration * 1000) / steps; 37 | 38 | const delayTimer = setTimeout(() => { 39 | let current = startValue; 40 | 41 | setCounter(current); 42 | 43 | const timer = setInterval(() => { 44 | current += incrementValue; 45 | 46 | if ( 47 | (incrementValue > 0 && current >= endValue) || 48 | (incrementValue < 0 && current <= endValue) 49 | ) { 50 | clearInterval(timer); 51 | setCounter(endValue); 52 | previousValueRef.current = endValue; 53 | } else { 54 | setCounter(current); 55 | } 56 | }, incrementTime); 57 | 58 | return () => clearInterval(timer); 59 | }, delay * 1000); 60 | 61 | return () => clearTimeout(delayTimer); 62 | }, [value, duration, delay]); 63 | 64 | return {formatter(Math.round(counter))}; 65 | } 66 | ); 67 | 68 | export default AnimatedCounter; 69 | -------------------------------------------------------------------------------- /components/docs/DocPager.jsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { 3 | Box, 4 | ButtonBase, 5 | Typography, 6 | useMediaQuery, 7 | useTheme, 8 | } from "@mui/material"; 9 | import { RxChevronLeft, RxChevronRight } from "react-icons/rx"; 10 | import Link from "next/link"; 11 | 12 | const PagerButton = ({ direction, page }) => { 13 | const theme = useTheme(); 14 | const isMobile = useMediaQuery(theme.breakpoints.down("sm")); 15 | 16 | return ( 17 | 18 | 36 | 43 | {isMobile ? ( 44 | 54 | {direction === "prev" && } 55 | {direction === "prev" ? "Previous" : "Next"} 56 | {direction === "next" && } 57 | 58 | ) : ( 59 | 66 | 76 | {direction === "prev" && } 77 | {direction === "prev" ? "Previous" : "Next"} 78 | {direction === "next" && } 79 | 80 | 81 | {page ? page.title : "No page"} 82 | 83 | 84 | )} 85 | 86 | 87 | 88 | ); 89 | }; 90 | 91 | const findAdjacentPages = (docsTree, currentSlug) => { 92 | const flatTree = flattenTree(docsTree); 93 | const currentIndex = flatTree.findIndex( 94 | (item) => item.url === `/docs${currentSlug ? `/${currentSlug}` : ""}` 95 | ); 96 | 97 | let prevPage = null; 98 | let nextPage = null; 99 | 100 | if (currentIndex > 0) { 101 | prevPage = flatTree[currentIndex - 1]; 102 | } 103 | 104 | if (currentIndex < flatTree.length - 1) { 105 | nextPage = flatTree[currentIndex + 1]; 106 | } 107 | 108 | return { prevPage, nextPage }; 109 | }; 110 | 111 | const flattenTree = (tree) => { 112 | const flattenedTree = []; 113 | 114 | // Add Setup page 115 | const setupPage = tree.find((node) => node.title === "Setup"); 116 | if (setupPage) { 117 | flattenedTree.push({ title: "Setup", url: "/docs" }); 118 | } 119 | 120 | // Add Changelog page 121 | const changelogPage = tree.find((node) => node.title === "Changelog"); 122 | if (changelogPage) { 123 | flattenedTree.push({ title: "Changelog", url: "/docs/changelog" }); 124 | } 125 | 126 | // Add all other pages 127 | tree.forEach((node) => { 128 | if (node.title !== "Setup" && node.title !== "Changelog") { 129 | if (!node.children) { 130 | flattenedTree.push({ 131 | title: node.title, 132 | url: `/docs/${ 133 | node.slug || node.title.toLowerCase().replace(/\s+/g, "-") 134 | }`, 135 | }); 136 | } else { 137 | node.children.forEach((child) => { 138 | flattenedTree.push({ 139 | title: child.title, 140 | url: `/docs/${ 141 | child.slug || child.title.toLowerCase().replace(/\s+/g, "-") 142 | }`, 143 | }); 144 | }); 145 | } 146 | } 147 | }); 148 | 149 | return flattenedTree; 150 | }; 151 | 152 | export const DocPager = ({ slug, docsTree }) => { 153 | const { prevPage, nextPage } = useMemo( 154 | () => findAdjacentPages(docsTree, slug), 155 | [docsTree, slug] 156 | ); 157 | 158 | return ( 159 | 166 | 167 | {prevPage && } 168 | 169 | 170 | {nextPage && } 171 | 172 | 173 | ); 174 | }; 175 | -------------------------------------------------------------------------------- /components/docs/DocsLayout.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, Fragment } from "react"; 2 | import { Box, Typography, Breadcrumbs } from "@mui/material"; 3 | import Link from "next/link"; 4 | import { TableOfContents } from "./TableOfContents"; 5 | import { useRouter } from "next/router"; 6 | import { RxChevronRight, RxTextAlignLeft } from "react-icons/rx"; 7 | 8 | const DocsLayout = ({ children, toc, docsTree }) => { 9 | const router = useRouter(); 10 | const [activeId, setActiveId] = useState(""); 11 | 12 | useEffect(() => { 13 | const observer = new IntersectionObserver( 14 | (entries) => { 15 | entries.forEach((entry) => { 16 | if (entry.isIntersecting) { 17 | setActiveId(entry.target.id); 18 | } 19 | }); 20 | }, 21 | { rootMargin: "-20% 0px -60% 0px", threshold: 0.1 } 22 | ); 23 | 24 | toc.forEach((item) => { 25 | const element = document.getElementById(item.id); 26 | if (element) observer.observe(element); 27 | }); 28 | 29 | return () => observer.disconnect(); 30 | }, [toc]); 31 | 32 | const renderBreadcrumbs = () => { 33 | const activeTocItem = toc.find((item) => item.id === activeId); 34 | const activeText = activeTocItem ? activeTocItem.text : ""; 35 | 36 | return ( 37 | } 39 | aria-label="breadcrumb" 40 | sx={{ mb: 2, display: { xs: "none", md: "flex", lg: "none" } }} 41 | > 42 | 53 | 54 | On this page 55 | 56 | {activeText && ( 57 | 63 | {activeText} 64 | 65 | )} 66 | 67 | ); 68 | }; 69 | 70 | return ( 71 | 77 | 89 | `1px solid ${theme.palette.divider}`, 94 | }} 95 | > 96 | {/* Scrollable content container */} 97 | 104 | 202 | 203 | {/* Fixed footer */} 204 | `1px solid ${theme.palette.divider}`, 211 | p: 1.5, 212 | bgcolor: "background.default", 213 | }} 214 | > 215 | 216 | Brought to you by{" "} 217 | 228 | abhivarde.in 229 | 230 | . 231 | 232 | 233 | 234 | 235 | 246 | {renderBreadcrumbs()} 247 | {children} 248 | 249 | 261 | 262 | 263 | 264 | ); 265 | }; 266 | 267 | const groupDocsTree = (docsTree) => { 268 | const grouped = { 269 | "Getting Started": [], 270 | }; 271 | 272 | // Add Templates to the Getting Started section 273 | grouped["Getting Started"].push({ 274 | title: "Templates", 275 | url: "/templates", 276 | slug: "templates", 277 | }); 278 | 279 | docsTree.forEach((item) => { 280 | if (item.title === "Setup" || item.title === "Changelog") { 281 | grouped["Getting Started"].push(item); 282 | } else { 283 | if (!grouped["Components"]) { 284 | grouped["Components"] = []; 285 | } 286 | grouped["Components"].push(item); 287 | } 288 | }); 289 | 290 | grouped["Getting Started"].sort((a, b) => { 291 | const order = { Setup: 1, Changelog: 2, Templates: 3 }; 292 | return (order[a.title] || 99) - (order[b.title] || 99); 293 | }); 294 | 295 | return grouped; 296 | }; 297 | 298 | export default DocsLayout; 299 | -------------------------------------------------------------------------------- /components/docs/TableOfContents.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { Box, Typography, List, ListItem } from "@mui/material"; 3 | import { RxTextAlignLeft, RxStar, RxStarFilled } from "react-icons/rx"; 4 | import { GITHUB_URL, SPONSOR_URL } from "../../utils/constants"; 5 | import { useGitHub } from "@/context/GithubContext"; 6 | import { RiGithubFill, RiHeartFill, RiHeartLine } from "react-icons/ri"; 7 | import AnimatedCounter from "../AnimatedCounter"; 8 | import { AnimatePresence, motion } from "framer-motion"; 9 | 10 | const buttonStyles = { 11 | mt: 1, 12 | mx: 1, 13 | display: "flex", 14 | alignItems: "center", 15 | justifyContent: "center", 16 | gap: 1.5, 17 | width: "100%", 18 | height: 40, 19 | borderRadius: "10px", 20 | color: "inherit", 21 | textDecoration: "none", 22 | bgcolor: (theme) => theme.palette.action.hover, 23 | border: "1px solid", 24 | borderColor: "divider", 25 | transition: "all 0.2s ease-in-out", 26 | "&:hover": { 27 | bgcolor: (theme) => theme.palette.action.selected, 28 | transform: "translateY(-2px)", 29 | boxShadow: (theme) => `0 4px 8px ${theme.palette.divider}`, 30 | }, 31 | }; 32 | 33 | export const TableOfContents = ({ toc }) => { 34 | const { stars, loading } = useGitHub(); 35 | const [activeId, setActiveId] = useState(""); 36 | const [isHeartHovered, setIsHeartHovered] = useState(false); 37 | const [isStarHovered, setIsStarHovered] = useState(false); 38 | 39 | useEffect(() => { 40 | const observer = new IntersectionObserver( 41 | (entries) => { 42 | entries.forEach((entry) => { 43 | if (entry.isIntersecting) { 44 | setActiveId(entry.target.id); 45 | } 46 | }); 47 | }, 48 | { rootMargin: "-20% 0px -60% 0px", threshold: 0.1 } 49 | ); 50 | 51 | toc.forEach((item) => { 52 | const element = document.getElementById(item.id); 53 | if (element) observer.observe(element); 54 | }); 55 | 56 | return () => observer.disconnect(); 57 | }, [toc]); 58 | 59 | const handleClick = (e, id) => { 60 | e.preventDefault(); 61 | const element = document.getElementById(id); 62 | if (element) { 63 | element.scrollIntoView({ behavior: "smooth", block: "start" }); 64 | setActiveId(id); 65 | } 66 | }; 67 | 68 | return ( 69 | 70 | 81 | On this page 82 | 83 | 92 | {toc.map((item) => ( 93 | handleClick(e, item.id)} 98 | sx={{ 99 | borderLeft: 1, 100 | borderColor: activeId === item.id ? "" : "divider", 101 | pl: item.level > 1 ? (item.level - 1) * 0.5 : 0.5, 102 | transition: "all 0.3s ease-in-out", 103 | "&:hover": { 104 | backgroundColor: "transparent", 105 | }, 106 | }} 107 | > 108 | 118 | {item.text} 119 | 120 | 121 | ))} 122 | 123 | 124 | setIsHeartHovered(true)} 130 | onMouseLeave={() => setIsHeartHovered(false)} 131 | sx={buttonStyles} 132 | > 133 | 134 | 135 | {isHeartHovered ? ( 136 | 144 | 145 | 146 | ) : ( 147 | 155 | 156 | 157 | )} 158 | 159 | 160 | 161 | Support Sync UI 162 | 163 | 164 | 165 | setIsStarHovered(true)} 171 | onMouseLeave={() => setIsStarHovered(false)} 172 | sx={buttonStyles} 173 | > 174 | {!loading ? ( 175 | 176 | {/* Left side: GitHub icon + Star label */} 177 | 178 | 179 | 189 | Star 190 | 191 | 192 | 193 | {/* Right side: Counter + Star animation */} 194 | 202 | 203 | 204 | 205 | 206 | 207 | 220 | {isStarHovered ? ( 221 | 222 | ) : ( 223 | 224 | )} 225 | 226 | 227 | 228 | 229 | ) : ( 230 | 231 | 232 | 233 | Star 234 | 235 | 236 | )} 237 | 238 | 239 | ); 240 | }; 241 | -------------------------------------------------------------------------------- /components/headerIcons.jsx: -------------------------------------------------------------------------------- 1 | import { RiGithubFill, RiTwitterXLine } from "react-icons/ri"; 2 | import { RxMoon, RxSun, RxDotsVertical } from "react-icons/rx"; 3 | import { Box, IconButton, Typography } from "@mui/material"; 4 | import { GITHUB_URL, TWITTER_URL } from "@/utils/constants"; 5 | import AnimatedCounter from "./AnimatedCounter"; 6 | 7 | const ICON_SIZE = 20; 8 | const CONTAINER_SIZE = 36; 9 | 10 | const IconContainer = ({ component = "a", children, ...props }) => ( 11 | theme.palette.action.hover, 21 | height: CONTAINER_SIZE, 22 | minWidth: CONTAINER_SIZE, 23 | borderRadius: "8px", 24 | transition: "all 0.2s ease-in-out", 25 | border: "1px solid", 26 | borderColor: "divider", 27 | gap: 1, 28 | px: 1.5, 29 | "&:hover": { 30 | bgcolor: (theme) => theme.palette.action.selected, 31 | transform: "translateY(-1px)", 32 | }, 33 | }} 34 | {...props} 35 | > 36 | {children} 37 | 38 | ); 39 | 40 | const HeaderIcons = ({ 41 | stars, 42 | loading, 43 | isDarkMode, 44 | toggleTheme, 45 | isMediumUp, 46 | handleToggle, 47 | anchorRef, 48 | menuOpen, 49 | }) => { 50 | return ( 51 | <> 52 | {(typeof stars === "number" || loading) && ( 53 | 58 | 59 | {!loading && stars > 0 && ( 60 | 61 | val.toLocaleString()} 65 | delay={0.2} 66 | /> 67 | 68 | )} 69 | 70 | )} 71 | 72 | 77 | 78 | 79 | 80 | 85 | {isDarkMode ? : } 86 | 87 | 88 | {!isMediumUp && ( 89 | 99 | 100 | 101 | )} 102 | 103 | ); 104 | }; 105 | 106 | export default HeaderIcons; 107 | -------------------------------------------------------------------------------- /components/home/HeroSection.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { 3 | Typography, 4 | Button, 5 | Box, 6 | Container, 7 | Tooltip, 8 | useTheme, 9 | } from "@mui/material"; 10 | import { motion, AnimatePresence } from "framer-motion"; 11 | import { RxArrowRight } from "react-icons/rx"; 12 | import Link from "next/link"; 13 | import { 14 | SiReact, 15 | SiNextdotjs, 16 | SiFramer, 17 | SiJavascript, 18 | SiMui, 19 | } from "react-icons/si"; 20 | import { IoSparkles } from "react-icons/io5"; 21 | 22 | const MotionButton = motion(Button); 23 | 24 | const HeroSection = () => { 25 | const theme = useTheme(); 26 | const [isHovered, setIsHovered] = useState(false); 27 | 28 | return ( 29 | 30 | 37 | {/* Introducing Button */} 38 | 43 | 44 | 65 | 66 | 67 | Introducing Templates 68 | 69 | 70 | 71 | 72 | 73 | {/* Main Heading */} 74 | 75 | 80 | 88 | 94 | Beautifully designed components 95 | 96 |
97 | 103 | built with MUI and Framer Motion 104 | 105 |
106 |
107 | 108 | 113 | 118 | Accessible and customizable components that you can copy and paste 119 | into your apps. Built with MUI and Framer Motion for seamless 120 | integration and stunning animations. Free. Open Source. Ready for 121 | your next project. 122 | 123 | 124 |
125 | 126 | {/* Tech Stack Icons */} 127 | 132 | 141 | {[ 142 | { Icon: SiMui, title: "MUI", color: "#007FFF" }, 143 | { Icon: SiJavascript, title: "JavaScript", color: "#F7DF1E" }, 144 | { Icon: SiReact, title: "React", color: "#61DAFB" }, 145 | { 146 | Icon: SiNextdotjs, 147 | title: "Next.js", 148 | color: theme.palette.mode === "dark" ? "#FFFFFF" : "#000000", 149 | }, 150 | { Icon: SiFramer, title: "Framer Motion", color: "#FF0050" }, 151 | ].map(({ Icon, title, color }) => ( 152 | 153 | 163 | 164 | 165 | 166 | ))} 167 | 168 | 169 | 170 | {/* Modified Button Section with Peerlist Badge */} 171 | 176 | 185 | {/* What's New Button */} 186 | 187 | setIsHovered(true)} 192 | onMouseLeave={() => setIsHovered(false)} 193 | sx={{ 194 | display: "flex", 195 | alignItems: "center", 196 | transition: "0.3s", 197 | flexShrink: 0, 198 | }} 199 | > 200 | 201 | 202 | {isHovered && ( 203 | 210 | 211 | 212 | )} 213 | 214 | Get Started 215 | 216 | {!isHovered && ( 217 | 224 | 225 | 226 | )} 227 | 228 | 229 | 230 | 231 | 232 | 233 |
234 |
235 | ); 236 | }; 237 | 238 | export default HeroSection; 239 | -------------------------------------------------------------------------------- /components/layout/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | Box, 4 | Container, 5 | Typography, 6 | useTheme, 7 | useMediaQuery, 8 | } from "@mui/material"; 9 | import { RiGithubFill, RiTwitterXLine } from "react-icons/ri"; 10 | import { GITHUB_URL, TWITTER_URL } from "../../utils/constants"; 11 | 12 | const Footer = () => { 13 | const theme = useTheme(); 14 | const isMediumUp = useMediaQuery(theme.breakpoints.up("md")); 15 | 16 | const socialLinkStyle = { 17 | display: "inline-flex", 18 | alignItems: "center", 19 | gap: 0.8, 20 | color: "inherit", 21 | textDecoration: "none", 22 | bgcolor: (theme) => theme.palette.action.hover, 23 | px: 1.4, // Increased padding-x slightly for better balance 24 | py: 0.8, // Increased padding-y slightly for better balance 25 | borderRadius: "8px", 26 | transition: "all 0.2s ease-in-out", 27 | border: "1px solid", 28 | borderColor: "divider", 29 | height: "32px", // Fixed height for consistency 30 | "&:hover": { 31 | bgcolor: (theme) => theme.palette.action.selected, 32 | transform: "translateY(-1px)", 33 | }, 34 | }; 35 | 36 | return ( 37 | 44 | 51 | 57 | 58 | 59 | Brought to you by{" "} 60 | 71 | abhivarde.in 72 | 73 | . 74 | 75 | 76 | 77 | 84 | 85 | 86 | 93 | 94 | 102 | Follow 103 | 104 | 105 | 106 | 107 | 108 | 109 | ); 110 | }; 111 | 112 | export default Footer; 113 | -------------------------------------------------------------------------------- /components/layout/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box } from "@mui/material"; 3 | import Header from "./Header"; 4 | import Footer from "./Footer"; 5 | import { useRouter } from "next/router"; 6 | 7 | const Layout = ({ children, toggleTheme, isDarkMode, docsTree, toc }) => { 8 | const router = useRouter(); 9 | const isDocsPage = router.pathname.startsWith("/docs"); 10 | const is404Page = router.pathname === "/404"; 11 | 12 | return ( 13 | 14 | {!is404Page && ( 15 |
22 | )} 23 | 33 | {children} 34 | 35 | {!isDocsPage && !is404Page &&