diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..af6ab76 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,20 @@ +.now/* +*.css +.changeset +dist +esm/* +public/* +tests/* +scripts/* +*.config.js +.DS_Store +node_modules +coverage +.next +build +!.commitlintrc.cjs +!.lintstagedrc.cjs +!jest.config.js +!plopfile.js +!react-shim.js +!tsup.config.ts \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..d3067d4 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,92 @@ +{ + "$schema": "https://json.schemastore.org/eslintrc.json", + "env": { + "browser": false, + "es2021": true, + "node": true + }, + "extends": [ + "plugin:react/recommended", + "plugin:prettier/recommended", + "plugin:react-hooks/recommended", + "plugin:jsx-a11y/recommended" + ], + "plugins": ["react", "unused-imports", "import", "@typescript-eslint", "jsx-a11y", "prettier"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "ecmaVersion": 12, + "sourceType": "module" + }, + "settings": { + "react": { + "version": "detect" + } + }, + "rules": { + "no-console": "warn", + "react/prop-types": "off", + "react/jsx-uses-react": "off", + "react/react-in-jsx-scope": "off", + "react-hooks/exhaustive-deps": "off", + "jsx-a11y/click-events-have-key-events": "warn", + "jsx-a11y/interactive-supports-focus": "warn", + "prettier/prettier": "warn", + "no-unused-vars": "off", + "unused-imports/no-unused-vars": "off", + "unused-imports/no-unused-imports": "warn", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "after-used", + "ignoreRestSiblings": false, + "argsIgnorePattern": "^_.*?$" + } + ], + "import/order": [ + "warn", + { + "groups": [ + "type", + "builtin", + "object", + "external", + "internal", + "parent", + "sibling", + "index" + ], + "pathGroups": [ + { + "pattern": "~/**", + "group": "external", + "position": "after" + } + ], + "newlines-between": "always" + } + ], + "react/self-closing-comp": "warn", + "react/jsx-sort-props": [ + "warn", + { + "callbacksLast": true, + "shorthandFirst": true, + "noSortAlphabetically": false, + "reservedFirst": true + } + ], + "padding-line-between-statements": [ + "warn", + {"blankLine": "always", "prev": "*", "next": "return"}, + {"blankLine": "always", "prev": ["const", "let", "var"], "next": "*"}, + { + "blankLine": "any", + "prev": ["const", "let", "var"], + "next": ["const", "let", "var"] + } + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f322f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3662b37 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..08df0a9 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# Next.js & NextUI Template + +This is a template for creating applications using Next.js 14 (app directory) and NextUI (v2). + +[Try it on CodeSandbox](https://githubbox.com/nextui-org/next-app-template) + +## Technologies Used + +- [Next.js 14](https://nextjs.org/docs/getting-started) +- [NextUI v2](https://nextui.org/) +- [Tailwind CSS](https://tailwindcss.com/) +- [Tailwind Variants](https://tailwind-variants.org) +- [TypeScript](https://www.typescriptlang.org/) +- [Framer Motion](https://www.framer.com/motion/) +- [next-themes](https://github.com/pacocoursey/next-themes) + +## How to Use + +### Use the template with create-next-app + +To create a new project based on this template using `create-next-app`, run the following command: + +```bash +npx create-next-app -e https://github.com/nextui-org/next-app-template +``` + +### Install dependencies + +You can use one of them `npm`, `yarn`, `pnpm`, `bun`, Example using `npm`: + +```bash +npm install +``` + +### Run the development server + +```bash +npm run dev +``` + +### Setup pnpm (optional) + +If you are using `pnpm`, you need to add the following code to your `.npmrc` file: + +```bash +public-hoist-pattern[]=*@nextui-org/* +``` + +After modifying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly. + +## License + +Licensed under the [MIT license](https://github.com/nextui-org/next-app-template/blob/main/LICENSE). diff --git a/app/about/layout.tsx b/app/about/layout.tsx new file mode 100644 index 0000000..98956a5 --- /dev/null +++ b/app/about/layout.tsx @@ -0,0 +1,13 @@ +export default function AboutLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+ {children} +
+
+ ); +} diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..c79c1e6 --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,9 @@ +import { title } from "@/components/primitives"; + +export default function AboutPage() { + return ( +
+

About

+
+ ); +} diff --git a/app/blog/layout.tsx b/app/blog/layout.tsx new file mode 100644 index 0000000..911d0bc --- /dev/null +++ b/app/blog/layout.tsx @@ -0,0 +1,13 @@ +export default function BlogLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+ {children} +
+
+ ); +} diff --git a/app/blog/page.tsx b/app/blog/page.tsx new file mode 100644 index 0000000..c6d0c65 --- /dev/null +++ b/app/blog/page.tsx @@ -0,0 +1,9 @@ +import { title } from "@/components/primitives"; + +export default function BlogPage() { + return ( +
+

Blog

+
+ ); +} diff --git a/app/docs/layout.tsx b/app/docs/layout.tsx new file mode 100644 index 0000000..eaf63f3 --- /dev/null +++ b/app/docs/layout.tsx @@ -0,0 +1,13 @@ +export default function DocsLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+ {children} +
+
+ ); +} diff --git a/app/docs/page.tsx b/app/docs/page.tsx new file mode 100644 index 0000000..4a2f19b --- /dev/null +++ b/app/docs/page.tsx @@ -0,0 +1,9 @@ +import { title } from "@/components/primitives"; + +export default function DocsPage() { + return ( +
+

Docs

+
+ ); +} diff --git a/app/error.tsx b/app/error.tsx new file mode 100644 index 0000000..9ed5104 --- /dev/null +++ b/app/error.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { useEffect } from "react"; + +export default function Error({ + error, + reset, +}: { + error: Error; + reset: () => void; +}) { + useEffect(() => { + // Log the error to an error reporting service + /* eslint-disable no-console */ + console.error(error); + }, [error]); + + return ( +
+

Something went wrong!

+ +
+ ); +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..9a42a39 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,66 @@ +import "@/styles/globals.css"; +import { Metadata, Viewport } from "next"; +import { Link } from "@nextui-org/link"; +import clsx from "clsx"; + +import { Providers } from "./providers"; + +import { siteConfig } from "@/config/site"; +import { fontSans } from "@/config/fonts"; +import { Navbar } from "@/components/navbar"; + +export const metadata: Metadata = { + title: { + default: siteConfig.name, + template: `%s - ${siteConfig.name}`, + }, + description: siteConfig.description, + icons: { + icon: "/favicon.ico", + }, +}; + +export const viewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "white" }, + { media: "(prefers-color-scheme: dark)", color: "black" }, + ], +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + +
+ +
+ {children} +
+ +
+
+ + + ); +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..21aed8b --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,56 @@ +import { Link } from "@nextui-org/link"; +import { Snippet } from "@nextui-org/snippet"; +import { Code } from "@nextui-org/code"; +import { button as buttonStyles } from "@nextui-org/theme"; + +import { siteConfig } from "@/config/site"; +import { title, subtitle } from "@/components/primitives"; +import { GithubIcon } from "@/components/icons"; + +export default function Home() { + return ( +
+
+ Make  + beautiful  +
+ + websites regardless of your design experience. + +
+ Beautiful, fast and modern React UI library. +
+
+ +
+ + Documentation + + + + GitHub + +
+ +
+ + + Get started by editing app/page.tsx + + +
+
+ ); +} diff --git a/app/pricing/layout.tsx b/app/pricing/layout.tsx new file mode 100644 index 0000000..dc3db6a --- /dev/null +++ b/app/pricing/layout.tsx @@ -0,0 +1,13 @@ +export default function PricingLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+ {children} +
+
+ ); +} diff --git a/app/pricing/page.tsx b/app/pricing/page.tsx new file mode 100644 index 0000000..42e2333 --- /dev/null +++ b/app/pricing/page.tsx @@ -0,0 +1,9 @@ +import { title } from "@/components/primitives"; + +export default function PricingPage() { + return ( +
+

Pricing

+
+ ); +} diff --git a/app/providers.tsx b/app/providers.tsx new file mode 100644 index 0000000..9a1ac92 --- /dev/null +++ b/app/providers.tsx @@ -0,0 +1,22 @@ +"use client"; + +import * as React from "react"; +import { NextUIProvider } from "@nextui-org/system"; +import { useRouter } from "next/navigation"; +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { ThemeProviderProps } from "next-themes/dist/types"; + +export interface ProvidersProps { + children: React.ReactNode; + themeProps?: ThemeProviderProps; +} + +export function Providers({ children, themeProps }: ProvidersProps) { + const router = useRouter(); + + return ( + + {children} + + ); +} diff --git a/components/counter.tsx b/components/counter.tsx new file mode 100644 index 0000000..d16f862 --- /dev/null +++ b/components/counter.tsx @@ -0,0 +1,14 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@nextui-org/button"; + +export const Counter = () => { + const [count, setCount] = useState(0); + + return ( + + ); +}; diff --git a/components/icons.tsx b/components/icons.tsx new file mode 100644 index 0000000..a3d7332 --- /dev/null +++ b/components/icons.tsx @@ -0,0 +1,215 @@ +import * as React from "react"; + +import { IconSvgProps } from "@/types"; + +export const Logo: React.FC = ({ + size = 36, + width, + height, + ...props +}) => ( + + + +); + +export const DiscordIcon: React.FC = ({ + size = 24, + width, + height, + ...props +}) => { + return ( + + + + ); +}; + +export const TwitterIcon: React.FC = ({ + size = 24, + width, + height, + ...props +}) => { + return ( + + + + ); +}; + +export const GithubIcon: React.FC = ({ + size = 24, + width, + height, + ...props +}) => { + return ( + + + + ); +}; + +export const MoonFilledIcon = ({ + size = 24, + width, + height, + ...props +}: IconSvgProps) => ( + +); + +export const SunFilledIcon = ({ + size = 24, + width, + height, + ...props +}: IconSvgProps) => ( + +); + +export const HeartFilledIcon = ({ + size = 24, + width, + height, + ...props +}: IconSvgProps) => ( + +); + +export const SearchIcon = (props: IconSvgProps) => ( + +); + +export const NextUILogo: React.FC = (props) => { + const { width, height = 40 } = props; + + return ( + + + + + + ); +}; diff --git a/components/navbar.tsx b/components/navbar.tsx new file mode 100644 index 0000000..f9c216c --- /dev/null +++ b/components/navbar.tsx @@ -0,0 +1,141 @@ +import { + Navbar as NextUINavbar, + NavbarContent, + NavbarMenu, + NavbarMenuToggle, + NavbarBrand, + NavbarItem, + NavbarMenuItem, +} from "@nextui-org/navbar"; +import { Button } from "@nextui-org/button"; +import { Kbd } from "@nextui-org/kbd"; +import { Link } from "@nextui-org/link"; +import { Input } from "@nextui-org/input"; +import { link as linkStyles } from "@nextui-org/theme"; +import NextLink from "next/link"; +import clsx from "clsx"; + +import { siteConfig } from "@/config/site"; +import { ThemeSwitch } from "@/components/theme-switch"; +import { + TwitterIcon, + GithubIcon, + DiscordIcon, + HeartFilledIcon, + SearchIcon, + Logo, +} from "@/components/icons"; + +export const Navbar = () => { + const searchInput = ( + + K + + } + labelPlacement="outside" + placeholder="Search..." + startContent={ + + } + type="search" + /> + ); + + return ( + + + + + +

ACME

+
+
+
    + {siteConfig.navItems.map((item) => ( + + + {item.label} + + + ))} +
+
+ + + + + + + + + + + + + + + {searchInput} + + + + + + + + + + + + + + + {searchInput} +
+ {siteConfig.navMenuItems.map((item, index) => ( + + + {item.label} + + + ))} +
+
+
+ ); +}; diff --git a/components/primitives.ts b/components/primitives.ts new file mode 100644 index 0000000..472973c --- /dev/null +++ b/components/primitives.ts @@ -0,0 +1,53 @@ +import { tv } from "tailwind-variants"; + +export const title = tv({ + base: "tracking-tight inline font-semibold", + variants: { + color: { + violet: "from-[#FF1CF7] to-[#b249f8]", + yellow: "from-[#FF705B] to-[#FFB457]", + blue: "from-[#5EA2EF] to-[#0072F5]", + cyan: "from-[#00b7fa] to-[#01cfea]", + green: "from-[#6FEE8D] to-[#17c964]", + pink: "from-[#FF72E1] to-[#F54C7A]", + foreground: "dark:from-[#FFFFFF] dark:to-[#4B4B4B]", + }, + size: { + sm: "text-3xl lg:text-4xl", + md: "text-[2.3rem] lg:text-5xl leading-9", + lg: "text-4xl lg:text-6xl", + }, + fullWidth: { + true: "w-full block", + }, + }, + defaultVariants: { + size: "md", + }, + compoundVariants: [ + { + color: [ + "violet", + "yellow", + "blue", + "cyan", + "green", + "pink", + "foreground", + ], + class: "bg-clip-text text-transparent bg-gradient-to-b", + }, + ], +}); + +export const subtitle = tv({ + base: "w-full md:w-1/2 my-2 text-lg lg:text-xl text-default-600 block max-w-full", + variants: { + fullWidth: { + true: "!w-full", + }, + }, + defaultVariants: { + fullWidth: true, + }, +}); diff --git a/components/theme-switch.tsx b/components/theme-switch.tsx new file mode 100644 index 0000000..55bcb0f --- /dev/null +++ b/components/theme-switch.tsx @@ -0,0 +1,81 @@ +"use client"; + +import { FC } from "react"; +import { VisuallyHidden } from "@react-aria/visually-hidden"; +import { SwitchProps, useSwitch } from "@nextui-org/switch"; +import { useTheme } from "next-themes"; +import { useIsSSR } from "@react-aria/ssr"; +import clsx from "clsx"; + +import { SunFilledIcon, MoonFilledIcon } from "@/components/icons"; + +export interface ThemeSwitchProps { + className?: string; + classNames?: SwitchProps["classNames"]; +} + +export const ThemeSwitch: FC = ({ + className, + classNames, +}) => { + const { theme, setTheme } = useTheme(); + const isSSR = useIsSSR(); + + const onChange = () => { + theme === "light" ? setTheme("dark") : setTheme("light"); + }; + + const { + Component, + slots, + isSelected, + getBaseProps, + getInputProps, + getWrapperProps, + } = useSwitch({ + isSelected: theme === "light" || isSSR, + "aria-label": `Switch to ${theme === "light" || isSSR ? "dark" : "light"} mode`, + onChange, + }); + + return ( + + + + +
+ {!isSelected || isSSR ? ( + + ) : ( + + )} +
+
+ ); +}; diff --git a/config/fonts.ts b/config/fonts.ts new file mode 100644 index 0000000..0e7d9c9 --- /dev/null +++ b/config/fonts.ts @@ -0,0 +1,11 @@ +import { Fira_Code as FontMono, Inter as FontSans } from "next/font/google"; + +export const fontSans = FontSans({ + subsets: ["latin"], + variable: "--font-sans", +}); + +export const fontMono = FontMono({ + subsets: ["latin"], + variable: "--font-mono", +}); diff --git a/config/site.ts b/config/site.ts new file mode 100644 index 0000000..75f5822 --- /dev/null +++ b/config/site.ts @@ -0,0 +1,69 @@ +export type SiteConfig = typeof siteConfig; + +export const siteConfig = { + name: "Next.js + NextUI", + description: "Make beautiful websites regardless of your design experience.", + navItems: [ + { + label: "Home", + href: "/", + }, + { + label: "Docs", + href: "/docs", + }, + { + label: "Pricing", + href: "/pricing", + }, + { + label: "Blog", + href: "/blog", + }, + { + label: "About", + href: "/about", + }, + ], + navMenuItems: [ + { + label: "Profile", + href: "/profile", + }, + { + label: "Dashboard", + href: "/dashboard", + }, + { + label: "Projects", + href: "/projects", + }, + { + label: "Team", + href: "/team", + }, + { + label: "Calendar", + href: "/calendar", + }, + { + label: "Settings", + href: "/settings", + }, + { + label: "Help & Feedback", + href: "/help-feedback", + }, + { + label: "Logout", + href: "/logout", + }, + ], + links: { + github: "https://github.com/nextui-org/nextui", + twitter: "https://twitter.com/getnextui", + docs: "https://nextui.org", + discord: "https://discord.gg/9b6yyZKmH4", + sponsor: "https://patreon.com/jrgarciadev", + }, +}; diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..767719f --- /dev/null +++ b/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/package.json b/package.json new file mode 100644 index 0000000..63a9c85 --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "next-app-template", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "next dev --turbo", + "build": "next build", + "start": "next start", + "lint": "eslint . --ext .ts,.tsx -c .eslintrc.json --fix" + }, + "dependencies": { + "@nextui-org/button": "2.0.38", + "@nextui-org/code": "2.0.33", + "@nextui-org/input": "2.2.5", + "@nextui-org/kbd": "2.0.34", + "@nextui-org/link": "2.0.35", + "@nextui-org/listbox": "2.1.27", + "@nextui-org/navbar": "2.0.37", + "@nextui-org/snippet": "2.0.43", + "@nextui-org/switch": "2.0.34", + "@nextui-org/system": "2.2.6", + "@nextui-org/theme": "2.2.11", + "@react-aria/ssr": "3.9.4", + "@react-aria/visually-hidden": "3.8.12", + "clsx": "2.1.1", + "framer-motion": "~11.1.1", + "intl-messageformat": "^10.5.0", + "next": "14.2.4", + "next-themes": "^0.2.1", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "devDependencies": { + "@types/node": "20.5.7", + "@types/react": "18.3.3", + "@types/react-dom": "18.3.0", + "@typescript-eslint/eslint-plugin": "7.2.0", + "@typescript-eslint/parser": "7.2.0", + "autoprefixer": "10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "14.2.1", + "eslint-config-prettier": "^8.2.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.23.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-unused-imports": "^3.2.0", + "postcss": "8.4.38", + "tailwind-variants": "0.1.20", + "tailwindcss": "3.4.3", + "typescript": "5.0.4" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..d2f8422 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/styles/globals.css b/styles/globals.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/styles/globals.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..2608822 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,20 @@ +import {nextui} from '@nextui-org/theme' + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', + './node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}' + ], + theme: { + extend: { + fontFamily: { + sans: ["var(--font-sans)"], + mono: ["var(--font-mono)"], + }, + }, + }, + darkMode: "class", + plugins: [nextui()], +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e06a445 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/types/index.ts b/types/index.ts new file mode 100644 index 0000000..cece4a4 --- /dev/null +++ b/types/index.ts @@ -0,0 +1,5 @@ +import { SVGProps } from "react"; + +export type IconSvgProps = SVGProps & { + size?: number; +};