├── .gitignore ├── README.md ├── dompurify.d.ts ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── ads.txt ├── app.png ├── avatars │ ├── avatar1.png │ ├── avatar2.png │ ├── avatar3.png │ ├── avatar4.png │ └── avatar5.png ├── background.png ├── background.svg ├── cover │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── clay.png │ ├── css-formatter.png │ └── scrollbar-generator.png ├── favicon.ico ├── fonts │ ├── Euclid Circular A Bold.ttf │ ├── Euclid Circular A Medium.ttf │ ├── Euclid Circular A Regular.ttf │ ├── Euclid Circular A SemiBold.ttf │ ├── Gilroy-Bold.ttf │ ├── Gilroy-ExtraBold.ttf │ └── Gilroy-Medium.ttf ├── icons │ ├── button.png │ ├── copy.png │ ├── css.png │ ├── github.png │ ├── glassmorphism.png │ ├── gradient.png │ ├── hex-to-hsl.webp │ ├── hex-to-rgb.webp │ ├── instagram.svg │ ├── palette.png │ ├── rgb-to-hex.webp │ ├── rgb-to-hsv.webp │ ├── scroll.png │ ├── stacks.png │ ├── tailwind.png │ ├── text-gradient.png │ └── underline-gradient.png ├── ilustrations │ └── 404.svg ├── logo-with-tailwind.png ├── logo.png ├── main.png ├── mocks │ ├── 300.png │ ├── banner.webp │ ├── banner2.webp │ ├── card-component.png │ ├── iphone.png │ └── tailwind-components │ │ ├── breadcrumb.png │ │ ├── card.png │ │ ├── cookies.png │ │ ├── features.png │ │ ├── footer.png │ │ ├── hero.png │ │ ├── hero2.png │ │ ├── loading.png │ │ ├── login.png │ │ ├── navbar.png │ │ ├── pagination.png │ │ ├── pricing.png │ │ └── testimonials.png ├── playground-logo-dark.png └── playground-logo-white.png ├── react-syntax-highlighter.d.ts ├── src ├── components │ ├── ADS │ │ ├── AdsBanner.tsx │ │ └── AdsBannerMobile.tsx │ ├── AnglePicker │ │ └── AnglePicker.tsx │ ├── Banner │ │ └── Banner.tsx │ ├── Breadcrumb │ │ ├── Breadcrumb.tsx │ │ └── types.ts │ ├── Button │ │ ├── index.tsx │ │ └── types.ts │ ├── Card │ │ ├── Card.tsx │ │ ├── styles.module.scss │ │ └── types.ts │ ├── CardToComponentsCategory │ │ ├── CardToComponentsCategory.tsx │ │ └── types.ts │ ├── ColorGrid │ │ └── ColorGrid.tsx │ ├── ColorPicker │ │ ├── ColorPicker.tsx │ │ └── types.ts │ ├── ControlsContainer │ │ ├── ControlsContainer.tsx │ │ └── types.ts │ ├── CopyButton │ │ ├── CopyButton.tsx │ │ └── types.ts │ ├── CustomSlider │ │ ├── CustomSlider.tsx │ │ └── types.ts │ ├── CustomSwitch │ │ └── CustomSwitch.tsx │ ├── DirectionButton │ │ └── DirectionButton.tsx │ ├── Drawer │ │ ├── Drawer.module.scss │ │ ├── Drawer.tsx │ │ └── types.ts │ ├── FavoriteButton │ │ ├── FavoriteButton.tsx │ │ └── types.ts │ ├── GithubStargazers │ │ ├── GithubStargazers.tsx │ │ └── types.ts │ ├── Info │ │ ├── Info.tsx │ │ └── types.ts │ ├── Layout.tsx │ ├── Loading │ │ ├── Loading.tsx │ │ └── style.module.scss │ ├── Navbar │ │ ├── Navbar.tsx │ │ ├── styles.module.scss │ │ └── types.ts │ ├── SEO.tsx │ ├── Sections │ │ ├── Footer │ │ │ └── Footer.tsx │ │ ├── Hero │ │ │ ├── Hero.tsx │ │ │ └── styles.module.scss │ │ └── Tools │ │ │ ├── CSSComponents.tsx │ │ │ ├── CSSConverters.tsx │ │ │ ├── CSSGenerators.tsx │ │ │ ├── CSSTools.tsx │ │ │ ├── Tools.tsx │ │ │ └── styles.module.scss │ ├── Sidebar │ │ └── Sidebar.tsx │ ├── Snackbar │ │ ├── SnackBar.tsx │ │ ├── SnackbarContainer.tsx │ │ ├── snackbarStyles.tsx │ │ └── types.ts │ ├── StarButton │ │ ├── StarButton.tsx │ │ └── styles.module.scss │ ├── Textarea │ │ ├── Textarea.tsx │ │ └── types.ts │ ├── Title │ │ ├── Title.tsx │ │ └── types.ts │ ├── Tooltip │ │ ├── Tooltip.module.scss │ │ ├── Tooltip.tsx │ │ └── types.ts │ └── TopBanner │ │ └── TopBanner.tsx ├── constants │ ├── buttonsGenerator.ts │ └── tailwind-components │ │ ├── breadcrumb.ts │ │ ├── card.ts │ │ ├── cookies.ts │ │ ├── features.ts │ │ ├── footer.ts │ │ ├── hero.ts │ │ ├── loading.ts │ │ ├── login.ts │ │ ├── navbar.ts │ │ ├── pagination.ts │ │ ├── pricing.ts │ │ └── testimonials.ts ├── context │ ├── ControlsContext.tsx │ ├── FavoriteContext.tsx │ └── ThemeContext.tsx ├── controllers │ └── gradientController.ts ├── hooks │ ├── getGradients.ts │ ├── useControls.ts │ └── useFavoriteTool.ts ├── libs │ └── db.ts ├── models │ └── Gradient.ts ├── pages │ ├── 404.tsx │ ├── _app.tsx │ ├── api │ │ ├── gradients.ts │ │ └── hello.js │ ├── background-gradient │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── css-buttons │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── css-components │ │ └── index.tsx │ ├── css-formatter │ │ └── index.tsx │ ├── css-generators │ │ └── index.tsx │ ├── css-tools │ │ └── index.tsx │ ├── glassmorphism │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── gradient-pallete │ │ └── index.tsx │ ├── hex-to-hsl │ │ └── index.tsx │ ├── hex-to-rgb │ │ └── index.tsx │ ├── index.tsx │ ├── neumorphism │ │ └── index.tsx │ ├── rgb-to-hex │ │ └── index.tsx │ ├── rgb-to-hsv │ │ └── index.tsx │ ├── scrollbar-generator │ │ └── index.tsx │ ├── tailwind-components │ │ ├── [slug].tsx │ │ └── index.tsx │ ├── tailwind-playground │ │ └── index.tsx │ ├── text-gradient │ │ ├── index.tsx │ │ └── styles.module.scss │ └── underline-gradient │ │ └── index.tsx └── utils │ ├── CSSToTailwind.ts │ ├── colors.ts │ ├── gradients.ts │ ├── hexToRGB.tsx │ ├── parseHexToRGB.ts │ ├── parseRgbToHex.ts │ ├── useCopyToClipboard.ts │ └── validateAndFormatHex.ts ├── styles ├── Home.module.css └── globals.scss ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env 30 | .env*.local 31 | 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tools4CSS - Free CSS Generators 2 |

🛠︎ Generators:

3 | 4 | 5 | - Claymorphism 6 | - Neumorphism 7 | - Glassmorphism 8 | - Text Gradient 9 | - Background Gradient 10 | 11 |

🔥 Run app

12 | 13 | ``` 14 | git clone https://github.com/gomestzx/tools4CSS.git 15 | 16 | cd tools4CSS 17 | 18 | yarn 19 | # or 20 | npm install 21 | 22 | npm start 23 | # or 24 | yarn dev 25 | ``` 26 | Open http://localhost:3000 with your browser to see the result. 27 |

🌟 Support this project

28 | 29 | ![](https://user-images.githubusercontent.com/48997634/174794647-0c851917-e5c9-4fb9-bf88-b61d89dc2f4f.gif) 30 | 31 | -------------------------------------------------------------------------------- /dompurify.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/dompurify.d.ts -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = { 8 | basePath: '/docs', 9 | } 10 | 11 | module.exports = nextConfig 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tools4css", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@codemirror/lang-html": "^6.4.9", 13 | "@codemirror/theme-one-dark": "^6.1.2", 14 | "@emotion/react": "^11.11.4", 15 | "@emotion/styled": "^11.11.5", 16 | "@fortawesome/fontawesome-svg-core": "^6.4.2", 17 | "@fortawesome/free-brands-svg-icons": "^6.4.2", 18 | "@fortawesome/free-regular-svg-icons": "^6.4.2", 19 | "@fortawesome/free-solid-svg-icons": "^6.4.2", 20 | "@fortawesome/react-fontawesome": "^0.2.0", 21 | "@mui/material": "^5.15.20", 22 | "@mui/styles": "^5.15.20", 23 | "@types/canvas-confetti": "^1.6.4", 24 | "@types/cssbeautify": "^0.3.5", 25 | "@types/dompurify": "^3.0.5", 26 | "@types/js-beautify": "^1.14.3", 27 | "@types/react-highlight": "^0.12.8", 28 | "@types/react-modal": "^3.13.1", 29 | "@types/react-syntax-highlighter": "^15.5.13", 30 | "@uiw/react-codemirror": "^4.23.4", 31 | "canvas-confetti": "^1.9.3", 32 | "clsx": "^2.0.0", 33 | "cssbeautify": "^0.3.1", 34 | "dompurify": "^3.1.7", 35 | "framer-motion": "^11.2.6", 36 | "html-react-parser": "^5.1.10", 37 | "js-beautify": "^1.15.1", 38 | "mongoose": "^8.15.0", 39 | "next": "^15.3.1", 40 | "prettier": "^3.3.2", 41 | "react": "^19.1.0", 42 | "react-colorful": "^5.6.1", 43 | "react-copy-to-clipboard": "^5.1.0", 44 | "react-dom": "^19.1.0", 45 | "react-highlight": "^0.15.0", 46 | "react-icons": "^5.2.1", 47 | "react-modal": "^3.15.1", 48 | "react-player": "^2.10.1", 49 | "react-switch": "^7.0.0", 50 | "react-syntax-highlighter": "^15.5.0", 51 | "sass": "^1.54.0", 52 | "tailwind-merge": "^2.0.0" 53 | }, 54 | "devDependencies": { 55 | "@types/node": "20.9.0", 56 | "@types/react": "18.0.15", 57 | "@types/react-copy-to-clipboard": "^5.0.3", 58 | "@types/react-dom": "18.0.6", 59 | "autoprefixer": "^10.4.16", 60 | "postcss": "^8.4.31", 61 | "tailwindcss": "^3.3.3", 62 | "typescript": "4.7.4" 63 | }, 64 | "engines": { 65 | "node": ">=20.x" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-2529229033686497, DIRECT, f08c47fec0942fa0 -------------------------------------------------------------------------------- /public/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/app.png -------------------------------------------------------------------------------- /public/avatars/avatar1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/avatars/avatar1.png -------------------------------------------------------------------------------- /public/avatars/avatar2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/avatars/avatar2.png -------------------------------------------------------------------------------- /public/avatars/avatar3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/avatars/avatar3.png -------------------------------------------------------------------------------- /public/avatars/avatar4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/avatars/avatar4.png -------------------------------------------------------------------------------- /public/avatars/avatar5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/avatars/avatar5.png -------------------------------------------------------------------------------- /public/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/background.png -------------------------------------------------------------------------------- /public/background.svg: -------------------------------------------------------------------------------- 1 | 2 | ffflux (6)-svg 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/cover/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/1.png -------------------------------------------------------------------------------- /public/cover/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/2.png -------------------------------------------------------------------------------- /public/cover/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/3.png -------------------------------------------------------------------------------- /public/cover/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/4.png -------------------------------------------------------------------------------- /public/cover/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/5.png -------------------------------------------------------------------------------- /public/cover/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/6.png -------------------------------------------------------------------------------- /public/cover/clay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/clay.png -------------------------------------------------------------------------------- /public/cover/css-formatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/css-formatter.png -------------------------------------------------------------------------------- /public/cover/scrollbar-generator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/cover/scrollbar-generator.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/Euclid Circular A Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/fonts/Euclid Circular A Bold.ttf -------------------------------------------------------------------------------- /public/fonts/Euclid Circular A Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/fonts/Euclid Circular A Medium.ttf -------------------------------------------------------------------------------- /public/fonts/Euclid Circular A Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/fonts/Euclid Circular A Regular.ttf -------------------------------------------------------------------------------- /public/fonts/Euclid Circular A SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/fonts/Euclid Circular A SemiBold.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/fonts/Gilroy-Bold.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/fonts/Gilroy-ExtraBold.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/fonts/Gilroy-Medium.ttf -------------------------------------------------------------------------------- /public/icons/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/button.png -------------------------------------------------------------------------------- /public/icons/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/copy.png -------------------------------------------------------------------------------- /public/icons/css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/css.png -------------------------------------------------------------------------------- /public/icons/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/github.png -------------------------------------------------------------------------------- /public/icons/glassmorphism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/glassmorphism.png -------------------------------------------------------------------------------- /public/icons/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/gradient.png -------------------------------------------------------------------------------- /public/icons/hex-to-hsl.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/hex-to-hsl.webp -------------------------------------------------------------------------------- /public/icons/hex-to-rgb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/hex-to-rgb.webp -------------------------------------------------------------------------------- /public/icons/instagram.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/icons/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/palette.png -------------------------------------------------------------------------------- /public/icons/rgb-to-hex.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/rgb-to-hex.webp -------------------------------------------------------------------------------- /public/icons/rgb-to-hsv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/rgb-to-hsv.webp -------------------------------------------------------------------------------- /public/icons/scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/scroll.png -------------------------------------------------------------------------------- /public/icons/stacks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/stacks.png -------------------------------------------------------------------------------- /public/icons/tailwind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/tailwind.png -------------------------------------------------------------------------------- /public/icons/text-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/text-gradient.png -------------------------------------------------------------------------------- /public/icons/underline-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/icons/underline-gradient.png -------------------------------------------------------------------------------- /public/logo-with-tailwind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/logo-with-tailwind.png -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/logo.png -------------------------------------------------------------------------------- /public/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/main.png -------------------------------------------------------------------------------- /public/mocks/300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/300.png -------------------------------------------------------------------------------- /public/mocks/banner.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/banner.webp -------------------------------------------------------------------------------- /public/mocks/banner2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/banner2.webp -------------------------------------------------------------------------------- /public/mocks/card-component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/card-component.png -------------------------------------------------------------------------------- /public/mocks/iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/iphone.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/breadcrumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/breadcrumb.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/card.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/cookies.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/features.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/footer.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/hero.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/hero2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/hero2.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/loading.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/login.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/navbar.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/pagination.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/pricing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/pricing.png -------------------------------------------------------------------------------- /public/mocks/tailwind-components/testimonials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/mocks/tailwind-components/testimonials.png -------------------------------------------------------------------------------- /public/playground-logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/playground-logo-dark.png -------------------------------------------------------------------------------- /public/playground-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ofenascimento/tools4CSS/ad1bd7e0c4d5578ae990d28f92a69cb85952de03/public/playground-logo-white.png -------------------------------------------------------------------------------- /react-syntax-highlighter.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-syntax-highlighter'; 2 | declare module 'react-syntax-highlighter/dist/cjs/styles/hljs'; -------------------------------------------------------------------------------- /src/components/ADS/AdsBanner.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useEffect } from "react"; 4 | 5 | type AdBannerTypes = { 6 | dataAdSlot: string; 7 | dataAdFormat?: string; 8 | dataFullWidthResponsive?: boolean; 9 | fixed?: boolean; 10 | customClassName?: string; 11 | mobile?: boolean; 12 | }; 13 | 14 | const AdBanner = ({ 15 | dataAdSlot, 16 | dataAdFormat, 17 | dataFullWidthResponsive, 18 | fixed, 19 | customClassName, 20 | mobile 21 | }: AdBannerTypes) => { 22 | useEffect(() => { 23 | try { 24 | ((window as any).adsbygoogle = (window as any).adsbygoogle || []).push({}); 25 | } catch (error: any) { 26 | console.log(error.message); 27 | } 28 | }, []); 29 | 30 | return ( 31 |
35 | 36 | {/*

43 | Publicidade 44 |

*/} 45 | 46 | 47 | 58 |
59 | ); 60 | }; 61 | 62 | export default AdBanner; 63 | -------------------------------------------------------------------------------- /src/components/ADS/AdsBannerMobile.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useEffect } from "react"; 4 | 5 | type AdBannerTypes = { 6 | dataAdSlot: string; 7 | dataAdFormat?: string; 8 | dataFullWidthResponsive?: boolean; 9 | fixed?: boolean; 10 | }; 11 | 12 | const AdBannerMobile = ({ 13 | dataAdSlot, 14 | dataAdFormat, 15 | dataFullWidthResponsive, 16 | fixed 17 | }: AdBannerTypes) => { 18 | useEffect(() => { 19 | try { 20 | ((window as any).adsbygoogle = (window as any).adsbygoogle || []).push( 21 | {} 22 | ); 23 | } catch (error: any) { 24 | console.log(error.message); 25 | } 26 | }, []); 27 | 28 | return ( 29 |
32 | 40 |
41 | 42 | ); 43 | }; 44 | 45 | export default AdBannerMobile; -------------------------------------------------------------------------------- /src/components/AnglePicker/AnglePicker.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect, MouseEvent, TouchEvent, ChangeEvent } from 'react'; 2 | 3 | interface AnglePickerProps { 4 | initialAngle?: number; 5 | onAngleChange?: (angle: number) => void; 6 | } 7 | 8 | const AnglePicker: React.FC = ({ initialAngle = 0, onAngleChange }) => { 9 | const [angle, setAngle] = useState(initialAngle); 10 | const knobRef = useRef(null); 11 | 12 | const calculateAngle = (x: number, y: number, centerX: number, centerY: number): number => { 13 | const dx = x - centerX; 14 | const dy = y - centerY; 15 | let radians = Math.atan2(dy, dx); 16 | let degrees = (radians * 180) / Math.PI; 17 | return (degrees + 90 + 360) % 360; 18 | }; 19 | 20 | const handleMouseMove = (e: MouseEvent | TouchEvent) => { 21 | if (knobRef.current) { 22 | const rect = knobRef.current.getBoundingClientRect(); 23 | const centerX = rect.left + rect.width / 2; 24 | const centerY = rect.top + rect.height / 2; 25 | const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; 26 | const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; 27 | const newAngle = Math.round(calculateAngle(clientX, clientY, centerX, centerY)); 28 | setAngle(newAngle); 29 | if (onAngleChange) { 30 | onAngleChange(newAngle); 31 | } 32 | } 33 | }; 34 | 35 | const handleMouseDown = (e: MouseEvent | TouchEvent) => { 36 | document.addEventListener('mousemove', handleMouseMove as any); 37 | document.addEventListener('touchmove', handleMouseMove as any); 38 | document.addEventListener('mouseup', handleMouseUp); 39 | document.addEventListener('touchend', handleMouseUp); 40 | }; 41 | 42 | const handleMouseUp = () => { 43 | document.removeEventListener('mousemove', handleMouseMove as any); 44 | document.removeEventListener('touchmove', handleMouseMove as any); 45 | document.removeEventListener('mouseup', handleMouseUp); 46 | document.removeEventListener('touchend', handleMouseUp); 47 | }; 48 | 49 | const handleInputChange = (e: ChangeEvent) => { 50 | const newAngle = Number(e.target.value); 51 | setAngle(newAngle); 52 | if (onAngleChange) { 53 | onAngleChange(newAngle); 54 | } 55 | }; 56 | 57 | useEffect(() => { 58 | return () => { 59 | document.removeEventListener('mousemove', handleMouseMove as any); 60 | document.removeEventListener('touchmove', handleMouseMove as any); 61 | document.removeEventListener('mouseup', handleMouseUp); 62 | document.removeEventListener('touchend', handleMouseUp); 63 | }; 64 | }, []); 65 | 66 | return ( 67 |
68 |
75 |
76 |
77 |
78 | 84 | ° 85 |
86 |
87 | ); 88 | }; 89 | 90 | export default AnglePicker; 91 | -------------------------------------------------------------------------------- /src/components/Banner/Banner.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "next/link"; 3 | import ColorGrid from "../ColorGrid/ColorGrid"; 4 | import { BsPalette2 } from "react-icons/bs"; 5 | import { LuPaintbrush2 } from "react-icons/lu"; 6 | 7 | export default function Banner() { 8 | return ( 9 | <> 10 |
11 |
12 | 13 |
14 |
15 |

16 | Unlock Your Creativity with Our Ultimate CSS Gradients Collection 17 |

18 | 19 | 27 | 28 | 29 | 37 | 38 |
39 |
40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/Breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { TBreadcrumb } from './types'; 3 | 4 | export const Breadcrumb = ({ links }: TBreadcrumb): React.ReactElement => { 5 | return ( 6 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/types.ts: -------------------------------------------------------------------------------- 1 | export interface TBreadcrumb { 2 | links: Array<{ 3 | label: string 4 | href?: string 5 | }> 6 | } -------------------------------------------------------------------------------- /src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { IButton } from './types' 3 | import { twMerge } from 'tailwind-merge' 4 | import clsx from 'clsx' 5 | 6 | const Button = (props: IButton) => { 7 | return ( 8 | 11 | ) 12 | } 13 | 14 | export default Button; -------------------------------------------------------------------------------- /src/components/Button/types.ts: -------------------------------------------------------------------------------- 1 | export interface IButton { 2 | text: string 3 | onClick(e: any) : void 4 | className?: string 5 | textClassName?: string 6 | } -------------------------------------------------------------------------------- /src/components/Card/Card.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { useState, useEffect } from "react"; 3 | import { MdFavoriteBorder, MdFavorite } from "react-icons/md"; 4 | import { ICard } from "./types"; 5 | import Image from "next/image"; 6 | import { useFavorites } from "../../context/FavoriteContext"; 7 | import styles from "./styles.module.scss"; 8 | import { useSnackbar } from "../Snackbar/SnackBar"; 9 | import { SnackBarContainer } from "../Snackbar/SnackbarContainer"; 10 | 11 | const Card = (props: ICard) => { 12 | const { favoriteTools, saveFavorite, deleteFavorite } = useFavorites(); 13 | const [isFavorited, setIsFavorited] = useState(false); 14 | const { showSnackbar, snackbarList } = useSnackbar(); 15 | 16 | useEffect(() => { 17 | setIsFavorited(favoriteTools.some((tool) => tool.name === props.title)); 18 | }, [favoriteTools, props.title]); 19 | 20 | const handleFavorite = (e: React.MouseEvent) => { 21 | e.stopPropagation(); 22 | const tool = { name: props.title, url: `/${props.slug}` }; 23 | if (isFavorited) { 24 | deleteFavorite(props.title); 25 | showSnackbar({ 26 | type: "error", 27 | title: `${props.title} removed to favorites list`, 28 | subtitle: 'It has been removed, but you can add it back anytime' 29 | }); 30 | } else { 31 | saveFavorite(tool); 32 | showSnackbar({ 33 | type: "success", 34 | title: `${props.title} added to favorites list`, 35 | subtitle: 'You can now find it in your favorites' 36 | }); 37 | } 38 | setIsFavorited(!isFavorited); 39 | }; 40 | 41 | return ( 42 | <> 43 | 44 |
45 | 46 |
49 | 50 | 51 |
52 | 53 |

56 | {props.title} 57 |

58 | 59 |

62 | {props.info} 63 |

64 | 65 | {/* */} 75 |
76 |
77 |
78 | 79 | 80 | ); 81 | }; 82 | 83 | export default Card; 84 | -------------------------------------------------------------------------------- /src/components/Card/styles.module.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | overflow: hidden; 3 | transition: transform 0.3s ease; 4 | 5 | &:hover, 6 | &:focus { 7 | transform: scale(1.05) ; 8 | z-index: 10; 9 | border: 1.7px solid #CBD6E1; 10 | } 11 | 12 | .info { 13 | position: relative; 14 | display: inline-block; 15 | background: linear-gradient(to right, #000000 0%, #000000 100%); 16 | background-size: 0 100%; 17 | background-repeat: no-repeat; 18 | transition: background-size 0.3s ease, color 0.4s ease; 19 | } 20 | 21 | &:hover .info, 22 | &:focus .info { 23 | background-size: 100% 100%; 24 | color: #fff; 25 | } 26 | 27 | .title { 28 | position: relative; 29 | display: inline-block; 30 | } 31 | 32 | .title::after { 33 | content: ""; 34 | position: absolute; 35 | left: 0; 36 | bottom: 0; 37 | width: 100%; 38 | height: 2px; 39 | background: #2563eb; 40 | transform: scaleX(0); 41 | transform-origin: bottom right; 42 | transition: transform 0.3s ease; 43 | } 44 | 45 | &:hover .title::after, 46 | &:focus .title::after { 47 | transform: scaleX(1); 48 | transform-origin: bottom left; 49 | } 50 | 51 | .imageContainer { 52 | overflow: hidden; 53 | transition: transform 0.3s ease; 54 | } 55 | 56 | &:hover .imageContainer, 57 | &:focus .imageContainer { 58 | transform: scale(1.1) rotate(5deg); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/components/Card/types.ts: -------------------------------------------------------------------------------- 1 | export interface ICard { 2 | title: string; 3 | slug: string; 4 | img?: string; 5 | info?: string; 6 | } -------------------------------------------------------------------------------- /src/components/CardToComponentsCategory/CardToComponentsCategory.tsx: -------------------------------------------------------------------------------- 1 | // components/CardToComponents.js 2 | import Image from 'next/image'; 3 | import { ICardToComponentsCategory } from './types'; 4 | import Link from 'next/link'; 5 | 6 | const CardToComponentsCategory: React.FC = ({ urlImage, title, subtitle, href }) => { 7 | return ( 8 | 9 |
10 | Image of a project 17 |
18 |
19 | {title} 20 |
21 |

22 | {subtitle} 23 |

24 |
25 |
26 | 27 | ); 28 | }; 29 | 30 | export default CardToComponentsCategory; 31 | -------------------------------------------------------------------------------- /src/components/CardToComponentsCategory/types.ts: -------------------------------------------------------------------------------- 1 | export interface ICardToComponentsCategory { 2 | href: string; 3 | urlImage: string; 4 | title: string; 5 | subtitle: string; 6 | } -------------------------------------------------------------------------------- /src/components/ColorGrid/ColorGrid.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties, FC, useEffect, useState } from "react"; 2 | import { palleteGradient } from "../../utils/gradients"; 3 | 4 | interface CardProps { 5 | color1: string; 6 | color2: string; 7 | zIndex: number; 8 | } 9 | 10 | const Card: FC = ({ color1, color2, zIndex }) => { 11 | return ( 12 |
20 | 46 |
47 | ); 48 | }; 49 | 50 | interface Palette { 51 | color1: string; 52 | color2: string; 53 | } 54 | 55 | const getRandomColors = (palette: Palette[], num: number): Palette[] => { 56 | const shuffled = palette.sort(() => 0.5 - Math.random()); 57 | return shuffled.slice(0, num); 58 | }; 59 | 60 | const ColorGrid: FC = () => { 61 | const [numCards, setNumCards] = useState(4); 62 | const selectedColors = getRandomColors(palleteGradient, numCards); 63 | 64 | useEffect(() => { 65 | const handleResize = () => { 66 | const width = window.innerWidth; 67 | 68 | if (width >= 1400) { 69 | setNumCards(6); 70 | } else if(width >= 1250 && width < 1400) { 71 | setNumCards(5); 72 | } else if (width >= 1100 && width < 1250) { 73 | setNumCards(4); 74 | } else if (width >= 1023 && width < 1100) { 75 | setNumCards(3); 76 | } else { 77 | setNumCards(5); 78 | } 79 | }; 80 | 81 | window.addEventListener("resize", handleResize); 82 | handleResize(); 83 | 84 | return () => window.removeEventListener("resize", handleResize); 85 | }, []); 86 | 87 | const gridStyles: CSSProperties = { 88 | display: "flex", 89 | justifyContent: "center", 90 | alignItems: "center", 91 | position: "relative", 92 | }; 93 | 94 | return ( 95 | <> 96 |
97 | {selectedColors.map((palette, index) => ( 98 | 104 | ))} 105 |
106 | 107 | ); 108 | }; 109 | 110 | export default ColorGrid; 111 | -------------------------------------------------------------------------------- /src/components/ColorPicker/ColorPicker.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { HexColorPicker } from "react-colorful"; 3 | import { IColorPicker } from "./types"; 4 | 5 | const ColorPicker: React.FC = ({ value, onChange }) => { 6 | const [displayColorPicker, setDisplayColorPicker] = useState(false); 7 | 8 | const handleClick = () => { 9 | setDisplayColorPicker(!displayColorPicker); 10 | }; 11 | 12 | const handleClose = () => { 13 | setDisplayColorPicker(false); 14 | }; 15 | 16 | const handleInputChange = (event: React.ChangeEvent) => { 17 | let newValue = event.target.value; 18 | if (newValue.length === 6 && !newValue.startsWith("#")) { 19 | newValue = "#" + newValue; 20 | } 21 | onChange(newValue); 22 | }; 23 | 24 | return ( 25 |
26 |
27 | 34 | 39 |
40 | 41 | {displayColorPicker && ( 42 |
43 |
47 |
48 | 49 |
50 |
51 | )} 52 |
53 | ); 54 | }; 55 | 56 | export default ColorPicker; 57 | -------------------------------------------------------------------------------- /src/components/ColorPicker/types.ts: -------------------------------------------------------------------------------- 1 | export interface IColorPicker { 2 | value: string; 3 | onChange: (color: string) => void; 4 | } -------------------------------------------------------------------------------- /src/components/ControlsContainer/ControlsContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import clsx from "clsx"; 3 | import { twMerge } from "tailwind-merge"; 4 | import { IControlsContainer } from "./types"; 5 | 6 | const ControlsContainer: React.FC = ({ children, className }) => { 7 | return ( 8 |
16 | {children} 17 |
18 | ); 19 | }; 20 | 21 | export default ControlsContainer; 22 | -------------------------------------------------------------------------------- /src/components/ControlsContainer/types.ts: -------------------------------------------------------------------------------- 1 | export interface IControlsContainer { 2 | children: React.ReactNode; 3 | className?: string; 4 | } -------------------------------------------------------------------------------- /src/components/CopyButton/CopyButton.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import React, { useState } from 'react'; 3 | import CopyToClipboard from 'react-copy-to-clipboard'; 4 | import { IconDefinition, faCheck } from '@fortawesome/free-solid-svg-icons'; 5 | import { faCopy } from '@fortawesome/free-regular-svg-icons'; 6 | import { ICopyButton } from './types'; 7 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 8 | import { twMerge } from 'tailwind-merge'; 9 | import confetti from 'canvas-confetti'; 10 | 11 | const CopyButton = (props: ICopyButton) => { 12 | const [text, setText] = useState(props.initialText ?? 'COPY'); 13 | const [icon, setIcon] = useState(faCopy); 14 | 15 | const Copy = () => { 16 | 17 | confetti({ 18 | particleCount: 50, 19 | spread: 100, 20 | origin: { y: 0.6 }, 21 | }); 22 | 23 | if (props.withIcon) { 24 | setIcon(faCheck); 25 | setTimeout(() => { 26 | setIcon(faCopy); 27 | }, 2000); 28 | } 29 | setText(props.copiedText ?? 'COPIED 🎉'); 30 | setTimeout(() => { 31 | setText(props.initialText ?? 'COPY'); 32 | }, 2500); 33 | }; 34 | 35 | return ( 36 | <> 37 | 38 | 41 | 42 | 43 | ); 44 | }; 45 | 46 | export default CopyButton; 47 | -------------------------------------------------------------------------------- /src/components/CopyButton/types.ts: -------------------------------------------------------------------------------- 1 | export interface ICopyButton { 2 | textToCopy: string 3 | withIcon?: boolean 4 | className?: string 5 | initialText?: string 6 | copiedText?: string 7 | textClassName?: string 8 | } -------------------------------------------------------------------------------- /src/components/CustomSlider/CustomSlider.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "@mui/material/Slider"; 3 | import { makeStyles } from "@mui/styles"; 4 | import { useTheme } from "../../context/ThemeContext"; 5 | import { ICustomSlider } from "./types"; 6 | 7 | const useStyles = makeStyles({ 8 | thumb: { 9 | backgroundColor: "#fff", 10 | }, 11 | rail: { 12 | backgroundColor: "#fff", 13 | }, 14 | track: { 15 | backgroundColor: "#fff", 16 | }, 17 | }); 18 | 19 | const CustomSlider = (props: ICustomSlider) => { 20 | const { theme } = useTheme(); 21 | const classes = useStyles(); 22 | 23 | return ( 24 | { 31 | if (props.onChange) { 32 | props.onChange(event, value); 33 | } 34 | }} 35 | /> 36 | ); 37 | }; 38 | 39 | export default CustomSlider; 40 | -------------------------------------------------------------------------------- /src/components/CustomSlider/types.ts: -------------------------------------------------------------------------------- 1 | import { SliderProps } from "@mui/material/Slider"; 2 | 3 | export interface ICustomSlider extends SliderProps { 4 | onChange: any; 5 | } -------------------------------------------------------------------------------- /src/components/CustomSwitch/CustomSwitch.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from '@/context/ThemeContext'; 2 | import React from 'react' 3 | import Switch from "react-switch"; 4 | 5 | 6 | function CustomSwitch(props: any) { 7 | const { theme } = useTheme(); 8 | 9 | return ( 10 | <> 11 | 26 | ) 27 | } 28 | 29 | export default CustomSwitch -------------------------------------------------------------------------------- /src/components/DirectionButton/DirectionButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useControls } from '../../hooks/useControls'; 3 | 4 | 5 | const DirectionButton = () => { 6 | const { setDirection, animated } = useControls(); 7 | return ( 8 |
9 | Direction: 10 | 11 | 12 | 13 | 14 |
15 | 16 | ); 17 | }; 18 | 19 | export default DirectionButton; 20 | -------------------------------------------------------------------------------- /src/components/Drawer/Drawer.module.scss: -------------------------------------------------------------------------------- 1 | .glass { 2 | backdrop-filter: blur(4px); 3 | --webkit-backdrop-filter: blur(4px); 4 | --moz-backdrop-filter: blur(4px); 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Drawer/Drawer.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MdClose, MdDelete } from "react-icons/md"; 3 | import { useFavorites } from "../../context/FavoriteContext"; 4 | import styles from "./Drawer.module.scss"; 5 | import { IDrawer } from "./types"; 6 | 7 | const Drawer: React.FC = ({ isOpen, toggleDrawer }) => { 8 | const { favoriteTools, deleteFavorite } = useFavorites(); 9 | 10 | const handleDelete = (toolName: string) => { 11 | deleteFavorite(toolName); 12 | }; 13 | 14 | return ( 15 |
20 | 31 |
32 |
    33 | {favoriteTools.length > 0 ? ( 34 | favoriteTools.map((tool, index) => ( 35 |
  • 39 | 45 | {tool.name} 46 | 47 | 53 |
  • 54 | )) 55 | ) : ( 56 | <> 57 | )} 58 |
59 |

60 | Your favorites are saved in your browser's cache. Clearing your 61 | browsing data will result in the deletion of your favorites as well. 62 |

63 |
64 |
65 | ); 66 | }; 67 | 68 | export default Drawer; 69 | -------------------------------------------------------------------------------- /src/components/Drawer/types.ts: -------------------------------------------------------------------------------- 1 | export interface IDrawer { 2 | isOpen: boolean; 3 | toggleDrawer: () => void; 4 | } -------------------------------------------------------------------------------- /src/components/FavoriteButton/FavoriteButton.tsx: -------------------------------------------------------------------------------- 1 | import { MdFavorite, MdFavoriteBorder } from "react-icons/md"; 2 | import { IFavoriteButton } from "./types"; 3 | import { useSnackbar } from "../Snackbar/SnackBar"; 4 | import { SnackBarContainer } from "../Snackbar/SnackbarContainer"; 5 | 6 | const FavoriteButton = ({ handleFavorite, isFavorited }: IFavoriteButton) => { 7 | const { showSnackbar, snackbarList } = useSnackbar(); 8 | return null; 9 | return ( 10 | <> 11 | 50 | 51 | ); 52 | }; 53 | 54 | export default FavoriteButton; 55 | -------------------------------------------------------------------------------- /src/components/FavoriteButton/types.ts: -------------------------------------------------------------------------------- 1 | export interface IFavoriteButton { 2 | isFavorited: boolean; 3 | handleFavorite: () => void; 4 | } -------------------------------------------------------------------------------- /src/components/GithubStargazers/GithubStargazers.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { FaGithub, FaStar } from "react-icons/fa"; 3 | import { Stargazer } from "./types"; 4 | import Link from "next/link"; 5 | 6 | const GithubStargazers: React.FC = () => { 7 | const [stargazers, setStargazers] = useState([]); 8 | 9 | useEffect(() => { 10 | 11 | const fetchStargazes = async () => { 12 | try { 13 | const response = await fetch('https://api.github.com/repos/ofenascimento/tools4CSS/stargazers?per_page=100') 14 | const data = await response.json(); 15 | setStargazers(data) 16 | } 17 | catch (err) { 18 | console.log(err) 19 | } 20 | } 21 | 22 | fetchStargazes() 23 | 24 | }, []) 25 | 26 | return ( 27 |
28 |
29 | {stargazers.map((user) => ( 30 | 36 | {user.login} 41 | 42 | ))} 43 |
44 | 45 |
46 | 47 |
48 | 49 | 50 | {stargazers.length} 51 |
52 | 53 |
54 |

55 | Add a star on GitHub and join the community! 56 |

57 |
58 | ); 59 | }; 60 | 61 | export default GithubStargazers; 62 | -------------------------------------------------------------------------------- /src/components/GithubStargazers/types.ts: -------------------------------------------------------------------------------- 1 | export interface Stargazer { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | html_url: string; 6 | } -------------------------------------------------------------------------------- /src/components/Info/Info.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { IInfo } from "./types"; 3 | 4 | function Info({ title, paragraph }: IInfo): React.ReactElement { 5 | return ( 6 |
7 |

{title}

8 |

{paragraph}

9 |
10 |
11 | ); 12 | } 13 | 14 | export default Info; 15 | -------------------------------------------------------------------------------- /src/components/Info/types.ts: -------------------------------------------------------------------------------- 1 | export interface IInfo { 2 | title: string; 3 | paragraph: string | React.ReactNode; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from "@/context/ThemeContext"; 2 | import { useRouter } from "next/router"; 3 | import { ReactNode, useEffect } from "react"; 4 | import Navbar from "./Navbar/Navbar"; 5 | import Footer from "./Sections/Footer/Footer"; 6 | import TopBanner from "./TopBanner/TopBanner"; 7 | 8 | interface LayoutProps { 9 | children: ReactNode; 10 | } 11 | 12 | const Layout = ({ children }: LayoutProps) => { 13 | const router = useRouter(); 14 | const currentPath = router.asPath; 15 | const { theme } = useTheme() 16 | 17 | if (currentPath === '/tailwind-playground' || currentPath === '/test-component') return <>{children}; 18 | 19 | 20 | return ( 21 | <> 22 | {/* {(currentPath === '/' || currentPath === '/#generators') && } */} 23 |
24 |
25 | 26 |
{children}
27 |
28 |
29 |
30 | 31 | ); 32 | }; 33 | 34 | export default Layout; 35 | -------------------------------------------------------------------------------- /src/components/Loading/Loading.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './style.module.scss'; 3 | import { useTheme } from '@/context/ThemeContext'; 4 | 5 | const Loading = () => { 6 | const {theme} = useTheme() 7 | return ( 8 |
9 | ); 10 | }; 11 | 12 | export default Loading; 13 | -------------------------------------------------------------------------------- /src/components/Loading/style.module.scss: -------------------------------------------------------------------------------- 1 | .loading { 2 | width: 30px; /* Diminuído de 45.5px */ 3 | aspect-ratio: 1; 4 | position: relative; 5 | } 6 | 7 | .loading:before, 8 | .loading:after { 9 | content: ""; 10 | position: absolute; 11 | border-radius: 50px; 12 | box-shadow: 0 0 0 1.5px inset #fff; /* Ajuste no tamanho da sombra */ 13 | animation: l4 2.5s infinite; 14 | } 15 | 16 | .loading:after { 17 | animation-delay: -1.25s; 18 | } 19 | 20 | @keyframes l4 { 21 | 0% { 22 | inset: 0 16px 16px 0; /* Ajustado proporcionalmente ao novo tamanho */ 23 | } 24 | 12.5% { 25 | inset: 0 16px 0 0; 26 | } 27 | 25% { 28 | inset: 16px 16px 0 0; 29 | } 30 | 37.5% { 31 | inset: 16px 0 0 0; 32 | } 33 | 50% { 34 | inset: 16px 0 0 16px; 35 | } 36 | 62.5% { 37 | inset: 0 0 0 16px; 38 | } 39 | 75% { 40 | inset: 0 0 16px 16px; 41 | } 42 | 87.5% { 43 | inset: 0 0 16px 0; 44 | } 45 | 100% { 46 | inset: 0 16px 16px 0; 47 | } 48 | } 49 | 50 | .loadingDark { 51 | width: 30px; /* Diminuído de 45.5px */ 52 | aspect-ratio: 1; 53 | position: relative; 54 | } 55 | 56 | .loadingDark:before, 57 | .loadingDark:after { 58 | content: ""; 59 | position: absolute; 60 | border-radius: 50px; 61 | box-shadow: 0 0 0 1.5px inset #000; /* Ajuste no tamanho da sombra */ 62 | animation: l4 2.5s infinite; 63 | } 64 | 65 | .loadingDark:after { 66 | animation-delay: -1.25s; 67 | } 68 | -------------------------------------------------------------------------------- /src/components/Navbar/styles.module.scss: -------------------------------------------------------------------------------- 1 | .link { 2 | position: relative; 3 | display: inline-block; 4 | 5 | &::after { 6 | content: ""; 7 | position: absolute; 8 | left: 0; 9 | bottom: 0; 10 | width: 100%; 11 | height: 2px; 12 | background: #2563eb; 13 | transform: scaleX(0); 14 | transform-origin: bottom right; 15 | transition: transform 0.3s ease; 16 | } 17 | 18 | &:hover::after, 19 | &:focus::after { 20 | transform: scaleX(1); 21 | transform-origin: bottom left; 22 | } 23 | } -------------------------------------------------------------------------------- /src/components/Navbar/types.ts: -------------------------------------------------------------------------------- 1 | export interface ISubmenuItem { 2 | href: string; 3 | children: React.ReactNode; 4 | onClick?: () => void; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/SEO.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import Script from "next/script"; 3 | 4 | type SEOProps = { 5 | title: string; 6 | description?: string; 7 | image?: string; 8 | shouldExcludeTitleSuffix?: boolean; 9 | shouldIndexPage?: boolean; 10 | }; 11 | 12 | export function SEO({ title, description, image, shouldExcludeTitleSuffix = false, shouldIndexPage = true }: SEOProps) { 13 | function makePageTitle(pageTitle: string, excludeTitleSuffix: boolean) { 14 | const suffix = excludeTitleSuffix ? "" : " | Tools4CSS"; 15 | 16 | return pageTitle + suffix; 17 | } 18 | 19 | const pageTitle = makePageTitle(title, shouldExcludeTitleSuffix); 20 | 21 | const pageImage = image ? `${process.env.NEXT_PUBLIC_SITE_URL}/assets/images/${image}` : null; 22 | 23 | return ( 24 | <> 25 | 37 | 38 | {pageTitle} 39 | 40 | {description && } 41 | {image && } 42 | 43 | {!shouldIndexPage && } 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /src/components/Sections/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import Link from 'next/link' 3 | import React from 'react' 4 | 5 | 6 | 7 | const Footer = () => { 8 | return ( 9 |
10 |
11 | 12 |
13 | 14 |
15 | Privacy 16 | Service Terms 17 | About 18 |
19 |

Made with ❤️ by ofenascimento

20 |
21 | ) 22 | } 23 | 24 | export default Footer -------------------------------------------------------------------------------- /src/components/Sections/Hero/Hero.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./styles.module.scss"; 3 | import Link from "next/link"; 4 | import StarButton from "../../StarButton/StarButton"; 5 | import Banner from "../../Banner/Banner"; 6 | import { MdArrowRightAlt, MdAutoFixHigh } from "react-icons/md"; 7 | 8 | const Hero = () => { 9 | return ( 10 |
14 |
15 |

16 | Let's build{" "} 17 | awesome and{" "} 18 | functional{" "} 19 | 20 | interface 21 | s 22 | 23 |

24 |

25 | Take advantage of our CSS tools and generators to speed up the styling 26 | of your components 27 |

28 | 29 | 33 | Discover All Tools 34 | 35 | 36 | 37 |
38 | {/* */} 39 |
40 | ); 41 | }; 42 | 43 | export default Hero; 44 | -------------------------------------------------------------------------------- /src/components/Sections/Hero/styles.module.scss: -------------------------------------------------------------------------------- 1 | .textGradient { 2 | background: linear-gradient(-80deg, #b23fe0, #ff00dd, #096ffe, #096ffe); 3 | background-size: 300%; 4 | -webkit-background-clip: text; 5 | -webkit-text-fill-color: transparent; 6 | animation: animated_text 8s ease-in-out infinite; 7 | -moz-animation: animated_text 8s ease-in-out infinite; 8 | -webkit-animation: animated_text 8s ease-in-out infinite; 9 | 10 | @keyframes animated_text { 11 | 0% { 12 | background-position: 0px 50%; 13 | } 14 | 15 | 50% { 16 | background-position: 100% 50%; 17 | } 18 | 19 | 100% { 20 | background-position: 0px 50%; 21 | } 22 | } 23 | } 24 | 25 | .underlineGradient { 26 | background-image: linear-gradient(-80deg, #009dff, #ff00dd 100%); 27 | background-repeat: no-repeat; 28 | background-size: 100% 80%; 29 | background-position: 100%; 30 | transition: background-size 0.25s ease-in; 31 | animation: animated 15s ease-in-out infinite; 32 | -moz-animation: animated 15s ease-in-out infinite; 33 | -webkit-animation: animated 15s ease-in-out infinite; 34 | color: #fff; 35 | } 36 | 37 | .underlineGradient::after { 38 | content: ""; 39 | width: 100%; 40 | height: 3px; 41 | bottom: -3px; 42 | left: 0; 43 | background-image: linear-gradient(-80deg, #009dff, #ff00dd 100%); 44 | background-repeat: no-repeat; 45 | background-size: 100% 100%; 46 | transition: background-size 0.25s ease-in-out; 47 | z-index: -1; 48 | } 49 | 50 | .underlineGradient:hover::after { 51 | background-size: 0 100%; 52 | transition: background-size 0.25s ease-in-out; 53 | } 54 | 55 | .wave { 56 | display: inline-block; 57 | transition: transform 0.3s ease-in-out; 58 | transform-origin: bottom left; 59 | animation: wave 8s ease-in-out infinite; 60 | } 61 | 62 | @keyframes wave { 63 | 0% { 64 | transform: rotate(0deg); 65 | } 66 | 25% { 67 | transform: rotate(15deg); 68 | } 69 | 50% { 70 | transform: rotate(-18deg); 71 | } 72 | 75% { 73 | transform: rotate(10deg); 74 | } 75 | 100% { 76 | transform: rotate(0deg); 77 | } 78 | } 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/components/Sections/Tools/CSSComponents.tsx: -------------------------------------------------------------------------------- 1 | import Card from '@/components/Card/Card' 2 | import React from 'react' 3 | 4 | function CSSComponents() { 5 | return ( 6 |
7 | 13 | 19 |
20 | ) 21 | } 22 | 23 | export default CSSComponents -------------------------------------------------------------------------------- /src/components/Sections/Tools/CSSConverters.tsx: -------------------------------------------------------------------------------- 1 | import Card from '@/components/Card/Card' 2 | import React from 'react' 3 | 4 | function CSSConverters() { 5 | return ( 6 |
7 | 13 | 19 | 25 | 31 |
32 | ) 33 | } 34 | 35 | export default CSSConverters -------------------------------------------------------------------------------- /src/components/Sections/Tools/CSSGenerators.tsx: -------------------------------------------------------------------------------- 1 | import Card from '@/components/Card/Card' 2 | import React from 'react' 3 | 4 | function CSSGenerators() { 5 | return ( 6 |
7 | 13 | 19 | 25 | 31 | 37 | 43 |
44 | ) 45 | } 46 | 47 | export default CSSGenerators -------------------------------------------------------------------------------- /src/components/Sections/Tools/CSSTools.tsx: -------------------------------------------------------------------------------- 1 | import Card from '@/components/Card/Card' 2 | import React from 'react' 3 | 4 | function CSSTools() { 5 | return ( 6 |
7 | 13 | 19 | 25 |
26 | ) 27 | } 28 | 29 | export default CSSTools -------------------------------------------------------------------------------- /src/components/Sections/Tools/Tools.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Card from "../../Card/Card"; 3 | import CSSTools from "./CSSTools"; 4 | import CSSGenerators from "./CSSGenerators"; 5 | import Link from "next/link"; 6 | import { MdArrowForward } from "react-icons/md"; 7 | import CSSComponents from "./CSSComponents"; 8 | import AdBanner from "@/components/ADS/AdsBanner"; 9 | import AdBannerMobile from "@/components/ADS/AdsBannerMobile"; 10 | import CSSConverters from "./CSSConverters"; 11 | 12 | const Tools = () => { 13 | return ( 14 |
15 | 16 | 17 |
18 |

19 | CSS Generators 20 |

21 | 22 |
23 | 24 | See All 25 | 26 |
27 | 28 |
29 |
30 | 31 |
32 | 33 | 34 | 35 | 36 |
37 |

38 | CSS Tools 39 |

40 | 41 |
42 | 43 | See All 44 | 45 |
46 | 47 |
48 |
49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 |
57 |

58 | CSS Converters 59 |

60 | 61 |
62 | 63 | See All 64 | 65 |
66 | 67 |
68 |
69 | 70 |
71 | 72 | 73 | 74 | 75 | 76 |
77 |

78 | CSS Components 79 |

80 | 81 |
82 | 83 | See All 84 | 85 |
86 | 87 |
88 |
89 | 90 |
91 | 92 | 93 |
94 | ); 95 | }; 96 | 97 | export default Tools; 98 | -------------------------------------------------------------------------------- /src/components/Sections/Tools/styles.module.scss: -------------------------------------------------------------------------------- 1 | .backgroundAnimated { 2 | background-image: linear-gradient(-80deg, #009dff, #ff00dd); 3 | background-size: 400% 400%; 4 | animation: gradient 5s ease infinite; 5 | border-radius: 8px; 6 | 7 | @keyframes gradient { 8 | 0% { 9 | background-position: 0% 50%; 10 | } 11 | 12 | 50% { 13 | background-position: 100% 50%; 14 | } 15 | 16 | 100% { 17 | background-position: 0% 50%; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import React from 'react'; 3 | import { useRouter } from 'next/router'; 4 | import { MdDescription, MdGridOn, MdNearMe } from 'react-icons/md'; 5 | 6 | type ComponentData = { 7 | name: string; 8 | count: number; 9 | }; 10 | 11 | type ComponentsData = { 12 | [category: string]: ComponentData[]; 13 | }; 14 | 15 | const componentsData: ComponentsData = { 16 | Components: [ 17 | { name: "Hero", count: 4 }, 18 | { name: "Card", count: 8 }, 19 | { name: "Testimonials", count: 6 }, 20 | { name: "Pricing", count: 6 }, 21 | { name: "Features", count: 2 }, 22 | { name: "Cookies", count: 2 }, 23 | { name: "Loading", count: 6 }, 24 | // { name: "Snackbar", count: 4 }, 25 | // { name: "Slider", count: 2 }, 26 | // { name: "Carousel", count: 2 }, 27 | ], 28 | Navigation: [ 29 | { name: "Navbar", count: 2 }, 30 | { name: "Footer", count: 2 }, 31 | { name: "Pagination", count: 2 }, 32 | { name: "Breadcrumb", count: 3 }, 33 | // { name: "Sidebar", count: 2 }, 34 | ], 35 | Forms: [ 36 | { name: "Login", count: 3 }, 37 | ] 38 | }; 39 | 40 | const Sidebar: React.FC = () => { 41 | const router = useRouter(); 42 | const currentPath = router.asPath; 43 | 44 | const renderCategory = (category: string, components: ComponentData[]) => ( 45 | <> 46 |
47 |
48 | {category === "Components" && } 49 | {category === "Navigation" && } 50 | {category === "Forms" && } 51 |
52 |

{category}

53 |
54 |
55 | {components.map((component, index) => { 56 | const linkPath = `/tailwind-components/${component.name.toLowerCase()}`; 57 | return ( 58 | 59 |
60 |

{component.name}

61 | {component.count} 62 |
63 | 64 | ); 65 | })} 66 |
67 | 68 | ); 69 | 70 | return ( 71 | 78 | ); 79 | } 80 | 81 | export default Sidebar; 82 | -------------------------------------------------------------------------------- /src/components/Snackbar/SnackBar.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import React, { useEffect, useState } from 'react'; 3 | import clsx from 'clsx'; 4 | import Image from 'next/image'; 5 | import { TSnackBar } from './types'; 6 | import { snackbarStyles } from './snackbarStyles'; 7 | 8 | 9 | const SnackBar = ({ id, type, title, subtitle, action }: TSnackBar): React.ReactElement => { 10 | const { border, icon, color } = snackbarStyles[type ?? 'info']; 11 | 12 | return ( 13 |
14 |
20 |
21 | {icon} 22 |
23 | 24 |
25 |

{title}

26 | {subtitle && {subtitle}} 27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | interface SnackbarResult { 34 | snackbarList: JSX.Element[] 35 | showSnackbar: (options: Omit, duration?: number | null) => void 36 | } 37 | 38 | const useSnackbar = (): SnackbarResult => { 39 | const [snackbarList, setSnackbarList] = useState([]); 40 | 41 | useEffect(() => { 42 | snackbarList.forEach((snackbar) => { 43 | if (snackbar.duration !== null && snackbar.duration !== 0) { 44 | const id = snackbar.id; 45 | const timer = setTimeout(() => { 46 | closeSnackbar(id); 47 | }, snackbar.duration); 48 | return () => { 49 | clearTimeout(timer); 50 | }; 51 | } 52 | }); 53 | }, [snackbarList]); 54 | 55 | const showSnackbar = (options: Omit, duration: number | null = 4000): void => { 56 | const id = Date.now().toString(); 57 | const newSnackbar = { ...options, id, duration }; 58 | setSnackbarList((prevList) => [...prevList, newSnackbar]); 59 | }; 60 | 61 | const closeSnackbar = (id: string): void => { 62 | setSnackbarList((prevList) => prevList.filter((snackbar) => snackbar.id !== id)); 63 | }; 64 | 65 | return { 66 | snackbarList: snackbarList.map((snackbar) => ( 67 | 75 | )), 76 | showSnackbar, 77 | }; 78 | }; 79 | 80 | export { SnackBar, useSnackbar }; 81 | -------------------------------------------------------------------------------- /src/components/Snackbar/SnackbarContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { type TSnackBarContainer } from './types'; 3 | 4 | export const SnackBarContainer = ({ children }: TSnackBarContainer): React.ReactElement => ( 5 |
6 | {children} 7 |
8 | ); 9 | -------------------------------------------------------------------------------- /src/components/Snackbar/snackbarStyles.tsx: -------------------------------------------------------------------------------- 1 | import { MdCheck, MdClose, MdInfoOutline, MdOutlineWarningAmber } from "react-icons/md"; 2 | 3 | export const snackbarStyles: Record = { 4 | info: { 5 | border: 'border-blue-500', 6 | icon: , 7 | color: '#3b82f6' 8 | 9 | }, 10 | error: { 11 | border: 'border-red-500', 12 | icon: , 13 | color: '#ef4444' 14 | }, 15 | alert: { 16 | border: 'border-yellow-500', 17 | icon: , 18 | color: '#eab308' 19 | }, 20 | success: { 21 | border: 'border-green-500', 22 | icon: , 23 | color: '#22c55e' 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/Snackbar/types.ts: -------------------------------------------------------------------------------- 1 | import { type ReactNode } from 'react'; 2 | 3 | export interface TSnackBarContainer { 4 | children: ReactNode 5 | } 6 | 7 | export interface TSnackBar { 8 | id: string 9 | type?: 'alert' | 'info' | 'error' | 'success' 10 | title: string 11 | subtitle?: string 12 | action?: JSX.Element 13 | duration?: number | null 14 | } 15 | -------------------------------------------------------------------------------- /src/components/StarButton/StarButton.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React from "react"; 3 | import styles from './styles.module.scss' 4 | import { useTheme } from "../../context/ThemeContext"; 5 | 6 | 7 | const StarButton = () => { 8 | const {theme} = useTheme() 9 | return ( 10 | <> 11 | 12 | 13 | ⭐ Give it a star on GitHub! 14 | 15 | 16 | 17 | ); 18 | }; 19 | 20 | export default StarButton; 21 | -------------------------------------------------------------------------------- /src/components/StarButton/styles.module.scss: -------------------------------------------------------------------------------- 1 | .gradientButton { 2 | background-image: linear-gradient(80deg, #000000, #1a4fd8); 3 | background-size: 400% 400%; 4 | animation: gradientButton 10s ease infinite; 5 | } 6 | 7 | @keyframes gradientButton { 8 | 0% { 9 | background-position: 0% 50%; 10 | } 11 | 50% { 12 | background-position: 100% 50%; 13 | } 14 | 100% { 15 | background-position: 0% 50%; 16 | } 17 | } 18 | 19 | .gradientButtonDark { 20 | background-image: linear-gradient(80deg, #1a4fd8, #000000); 21 | background-size: 400% 400%; 22 | animation: gradientButton 10s ease infinite; 23 | } 24 | 25 | @keyframes gradientButtonDarl { 26 | 0% { 27 | background-position: 0% 50%; 28 | } 29 | 50% { 30 | background-position: 100% 50%; 31 | } 32 | 100% { 33 | background-position: 0% 50%; 34 | } 35 | } -------------------------------------------------------------------------------- /src/components/Textarea/Textarea.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ITextarea } from './types' 3 | import clsx from 'clsx' 4 | import { twMerge } from 'tailwind-merge' 5 | 6 | const Textarea = (props: ITextarea) => { 7 | return ( 8 | <> 9 |