Initial commit from Create Next App

This commit is contained in:
shinya
2025-06-17 13:15:54 +08:00
commit 2e989e5e9b
77 changed files with 13025 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
import { NextResponse } from 'next/server';
export async function GET() {
return NextResponse.json({ hello: 'Next.js' });
}

View File

@@ -0,0 +1,17 @@
import { Metadata } from 'next';
import * as React from 'react';
import '@/styles/colors.css';
export const metadata: Metadata = {
title: 'Components',
description: 'Pre-built components with awesome default',
};
export default function ComponentsLayout({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}

461
src/app/components/page.tsx Normal file
View File

@@ -0,0 +1,461 @@
'use client';
import clsx from 'clsx';
import {
ArrowRight,
CreditCard,
Laptop,
Phone,
Plus,
Shield,
} from 'lucide-react';
import React from 'react';
import Button from '@/components/buttons/Button';
import IconButton from '@/components/buttons/IconButton';
import TextButton from '@/components/buttons/TextButton';
import ArrowLink from '@/components/links/ArrowLink';
import ButtonLink from '@/components/links/ButtonLink';
import PrimaryLink from '@/components/links/PrimaryLink';
import UnderlineLink from '@/components/links/UnderlineLink';
import UnstyledLink from '@/components/links/UnstyledLink';
import NextImage from '@/components/NextImage';
import Skeleton from '@/components/Skeleton';
type Color = (typeof colorList)[number];
export default function ComponentPage() {
const [mode, setMode] = React.useState<'dark' | 'light'>('light');
const [color, setColor] = React.useState<Color>('sky');
function toggleMode() {
return mode === 'dark' ? setMode('light') : setMode('dark');
}
const textColor = mode === 'dark' ? 'text-gray-300' : 'text-gray-600';
return (
<main>
<section
className={clsx(mode === 'dark' ? 'bg-dark' : 'bg-white', color)}
>
<div
className={clsx(
'layout min-h-screen py-20',
mode === 'dark' ? 'text-white' : 'text-black'
)}
>
<h1>Built-in Components</h1>
<ArrowLink direction='left' className='mt-2' href='/'>
Back to Home
</ArrowLink>
<div className='mt-8 flex flex-wrap gap-2'>
<Button
onClick={toggleMode}
variant={mode === 'dark' ? 'light' : 'dark'}
>
Set to {mode === 'dark' ? 'light' : 'dark'}
</Button>
{/* <Button onClick={randomize}>Randomize CSS Variable</Button> */}
</div>
<ol className='mt-8 space-y-6'>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>Customize Colors</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
You can change primary color to any Tailwind CSS colors. See
globals.css to change your color.
</p>
<div className='flex flex-wrap gap-2'>
<select
name='color'
id='color'
value={color}
className={clsx(
'block max-w-xs rounded',
mode === 'dark'
? 'bg-dark border border-gray-600'
: 'border-gray-300 bg-white',
'focus:border-primary-400 focus:ring-primary-400 focus:outline-none focus:ring'
)}
onChange={(e) => setColor(e.target.value as Color)}
>
{colorList.map((c) => (
<option key={c} value={c}>
{c}
</option>
))}
</select>
<ButtonLink href='https://github.com/theodorusclarence/ts-nextjs-tailwind-starter/blob/main/src/styles/colors.css'>
Check list of colors
</ButtonLink>
</div>
<div className='flex flex-wrap gap-2 text-xs font-medium'>
<div className='bg-primary-50 flex h-10 w-10 items-center justify-center rounded text-black'>
50
</div>
<div className='bg-primary-100 flex h-10 w-10 items-center justify-center rounded text-black'>
100
</div>
<div className='bg-primary-200 flex h-10 w-10 items-center justify-center rounded text-black'>
200
</div>
<div className='bg-primary-300 flex h-10 w-10 items-center justify-center rounded text-black'>
300
</div>
<div className='bg-primary-400 flex h-10 w-10 items-center justify-center rounded text-black'>
400
</div>
<div className='bg-primary-500 flex h-10 w-10 items-center justify-center rounded text-black'>
500
</div>
<div className='bg-primary-600 flex h-10 w-10 items-center justify-center rounded text-white'>
600
</div>
<div className='bg-primary-700 flex h-10 w-10 items-center justify-center rounded text-white'>
700
</div>
<div className='bg-primary-800 flex h-10 w-10 items-center justify-center rounded text-white'>
800
</div>
<div className='bg-primary-900 flex h-10 w-10 items-center justify-center rounded text-white'>
900
</div>
<div className='bg-primary-950 flex h-10 w-10 items-center justify-center rounded text-white'>
950
</div>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>UnstyledLink</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
No style applied, differentiate internal and outside links, give
custom cursor for outside links.
</p>
<div className='space-x-2'>
<UnstyledLink href='/'>Internal Links</UnstyledLink>
<UnstyledLink href='https://theodorusclarence.com'>
Outside Links
</UnstyledLink>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>PrimaryLink</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Add styling on top of UnstyledLink, giving a primary color to
the link.
</p>
<div className='space-x-2'>
<PrimaryLink href='/'>Internal Links</PrimaryLink>
<PrimaryLink href='https://theodorusclarence.com'>
Outside Links
</PrimaryLink>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>UnderlineLink</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Add styling on top of UnstyledLink, giving a dotted and animated
underline.
</p>
<div className='space-x-2'>
<UnderlineLink href='/'>Internal Links</UnderlineLink>
<UnderlineLink href='https://theodorusclarence.com'>
Outside Links
</UnderlineLink>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>ArrowLink</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Useful for indicating navigation, I use this quite a lot, so why
not build a component with some whimsy touch?
</p>
<div className='flex flex-wrap items-center gap-4'>
<ArrowLink href='/' direction='left'>
Direction Left
</ArrowLink>
<ArrowLink href='/'>Direction Right</ArrowLink>
<ArrowLink
as={UnstyledLink}
className='inline-flex items-center'
href='/'
>
Polymorphic
</ArrowLink>
<ArrowLink
as={ButtonLink}
variant='light'
className='inline-flex items-center'
href='/'
>
Polymorphic
</ArrowLink>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>ButtonLink</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Button styled link with 3 variants.
</p>
<div className='flex flex-wrap gap-2'>
<ButtonLink
variant='primary'
href='https://theodorusclarence.com'
>
Primary Variant
</ButtonLink>
<ButtonLink
variant='outline'
isDarkBg={mode === 'dark'}
href='https://theodorusclarence.com'
>
Outline Variant
</ButtonLink>
<ButtonLink
variant='ghost'
isDarkBg={mode === 'dark'}
href='https://theodorusclarence.com'
>
Ghost Variant
</ButtonLink>
<ButtonLink variant='dark' href='https://theodorusclarence.com'>
Dark Variant
</ButtonLink>
<ButtonLink
variant='light'
href='https://theodorusclarence.com'
>
Light Variant
</ButtonLink>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>Button</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Ordinary button with style.
</p>
<div className='flex flex-wrap gap-2'>
<Button variant='primary'>Primary Variant</Button>
<Button variant='outline' isDarkBg={mode === 'dark'}>
Outline Variant
</Button>
<Button variant='ghost' isDarkBg={mode === 'dark'}>
Ghost Variant
</Button>
<Button variant='dark'>Dark Variant</Button>
<Button variant='light'>Light Variant</Button>
</div>
<div className='flex flex-wrap gap-2'>
<Button
variant='primary'
leftIcon={Plus}
rightIcon={ArrowRight}
>
Icon
</Button>
<Button
variant='outline'
leftIcon={Plus}
rightIcon={ArrowRight}
isDarkBg={mode === 'dark'}
>
Icon
</Button>
<Button
variant='ghost'
leftIcon={Plus}
rightIcon={ArrowRight}
isDarkBg={mode === 'dark'}
>
Icon
</Button>
<Button variant='dark' leftIcon={Plus} rightIcon={ArrowRight}>
Icon
</Button>
<Button variant='light' leftIcon={Plus} rightIcon={ArrowRight}>
Icon
</Button>
</div>
<div className='!mt-4 flex flex-wrap gap-2'>
<Button size='sm' variant='primary'>
Small Size
</Button>
<Button size='sm' variant='outline' isDarkBg={mode === 'dark'}>
Small Size
</Button>
<Button size='sm' variant='ghost' isDarkBg={mode === 'dark'}>
Small Size
</Button>
<Button size='sm' variant='dark'>
Small Size
</Button>
<Button size='sm' variant='light'>
Small Size
</Button>
</div>
<div className='flex flex-wrap gap-2'>
<Button
size='sm'
variant='primary'
leftIcon={Plus}
rightIcon={ArrowRight}
>
Icon
</Button>
<Button
size='sm'
variant='outline'
leftIcon={Plus}
rightIcon={ArrowRight}
isDarkBg={mode === 'dark'}
>
Icon
</Button>
<Button
size='sm'
variant='ghost'
leftIcon={Plus}
rightIcon={ArrowRight}
isDarkBg={mode === 'dark'}
>
Icon
</Button>
<Button
size='sm'
variant='dark'
leftIcon={Plus}
rightIcon={ArrowRight}
>
Icon
</Button>
<Button
size='sm'
variant='light'
leftIcon={Plus}
rightIcon={ArrowRight}
>
Icon
</Button>
</div>
<div className='!mt-4 flex flex-wrap gap-2'>
<Button disabled variant='primary'>
Disabled
</Button>
<Button disabled variant='outline' isDarkBg={mode === 'dark'}>
Disabled
</Button>
<Button disabled variant='ghost' isDarkBg={mode === 'dark'}>
Disabled
</Button>
<Button disabled variant='dark'>
Disabled
</Button>
<Button disabled variant='light'>
Disabled
</Button>
</div>
<div className='flex flex-wrap gap-2'>
<Button isLoading variant='primary'>
Disabled
</Button>
<Button isLoading variant='outline' isDarkBg={mode === 'dark'}>
Disabled
</Button>
<Button isLoading variant='ghost' isDarkBg={mode === 'dark'}>
Disabled
</Button>
<Button isLoading variant='dark'>
Disabled
</Button>
<Button isLoading variant='light'>
Disabled
</Button>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>TextButton</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Button with a text style
</p>
<div className='space-x-2'>
<TextButton>Primary Variant</TextButton>
<TextButton variant='basic'>Basic Variant</TextButton>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>IconButton</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Button with only icon inside
</p>
<div className='space-x-2'>
<IconButton icon={Plus} />
<IconButton variant='outline' icon={Laptop} />
<IconButton variant='ghost' icon={Phone} />
<IconButton variant='dark' icon={Shield} />
<IconButton variant='light' icon={CreditCard} />
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>Custom 404 Page</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Styled 404 page with some animation.
</p>
<div className='flex flex-wrap gap-2'>
<ButtonLink href='/404'>Visit the 404 page</ButtonLink>
</div>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>Next Image</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Next Image with default props and skeleton animation
</p>
<NextImage
useSkeleton
className='w-32 md:w-40'
src='/favicon/android-chrome-192x192.png'
width='180'
height='180'
alt='Icon'
/>
</li>
<li className='space-y-2'>
<h2 className='text-lg md:text-xl'>Skeleton</h2>
<p className={clsx('!mt-1 text-sm', textColor)}>
Skeleton with shimmer effect
</p>
<Skeleton className='h-72 w-72' />
</li>
</ol>
</div>
</section>
</main>
);
}
const colorList = [
'slate',
'gray',
'zinc',
'neutral',
'stone',
'red',
'orange',
'amber',
'yellow',
'lime',
'green',
'emerald',
'teal',
'cyan',
'sky',
'blue',
'indigo',
'violet',
'purple',
'fuchsia',
'pink',
'rose',
] as const;

38
src/app/error.tsx Normal file
View File

@@ -0,0 +1,38 @@
'use client'; // Error components must be Client Components
import * as React from 'react';
import { RiAlarmWarningFill } from 'react-icons/ri';
import TextButton from '@/components/buttons/TextButton';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
React.useEffect(() => {
// eslint-disable-next-line no-console
console.error(error);
}, [error]);
return (
<main>
<section className='bg-white'>
<div className='layout flex min-h-screen flex-col items-center justify-center text-center text-black'>
<RiAlarmWarningFill
size={60}
className='drop-shadow-glow animate-flicker text-red-500'
/>
<h1 className='mt-8 text-4xl md:text-6xl'>
Oops, something went wrong!
</h1>
<TextButton variant='basic' onClick={reset} className='mt-4'>
Try again
</TextButton>
</div>
</section>
</main>
);
}

62
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,62 @@
import { Metadata } from 'next';
import * as React from 'react';
import '@/styles/globals.css';
// !STARTERCONF This is for demo purposes, remove @/styles/colors.css import immediately
import '@/styles/colors.css';
import { siteConfig } from '@/constant/config';
// !STARTERCONF Change these default meta
// !STARTERCONF Look at @/constant/config to change them
export const metadata: Metadata = {
metadataBase: new URL(siteConfig.url),
title: {
default: siteConfig.title,
template: `%s | ${siteConfig.title}`,
},
description: siteConfig.description,
robots: { index: true, follow: true },
// !STARTERCONF this is the default favicon, you can generate your own from https://realfavicongenerator.net/
// ! copy to /favicon folder
icons: {
icon: '/favicon/favicon.ico',
shortcut: '/favicon/favicon-16x16.png',
apple: '/favicon/apple-touch-icon.png',
},
manifest: `/favicon/site.webmanifest`,
openGraph: {
url: siteConfig.url,
title: siteConfig.title,
description: siteConfig.description,
siteName: siteConfig.title,
images: [`${siteConfig.url}/images/og.jpg`],
type: 'website',
locale: 'en_US',
},
twitter: {
card: 'summary_large_image',
title: siteConfig.title,
description: siteConfig.description,
images: [`${siteConfig.url}/images/og.jpg`],
// creator: '@th_clarence',
},
// authors: [
// {
// name: 'Theodorus Clarence',
// url: 'https://theodorusclarence.com',
// },
// ],
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>{children}</body>
</html>
);
}

24
src/app/not-found.tsx Normal file
View File

@@ -0,0 +1,24 @@
import { Metadata } from 'next';
import * as React from 'react';
import { RiAlarmWarningFill } from 'react-icons/ri';
export const metadata: Metadata = {
title: 'Not Found',
};
export default function NotFound() {
return (
<main>
<section className='bg-white'>
<div className='layout flex min-h-screen flex-col items-center justify-center text-center text-black'>
<RiAlarmWarningFill
size={60}
className='drop-shadow-glow animate-flicker text-red-500'
/>
<h1 className='mt-8 text-4xl md:text-6xl'>Page Not Found</h1>
<a href='/'>Back to home</a>
</div>
</section>
</main>
);
}

72
src/app/page.tsx Normal file
View File

@@ -0,0 +1,72 @@
'use client';
import Head from 'next/head';
import * as React from 'react';
import '@/lib/env';
import ArrowLink from '@/components/links/ArrowLink';
import ButtonLink from '@/components/links/ButtonLink';
import UnderlineLink from '@/components/links/UnderlineLink';
import UnstyledLink from '@/components/links/UnstyledLink';
/**
* SVGR Support
* Caveat: No React Props Type.
*
* You can override the next-env if the type is important to you
* @see https://stackoverflow.com/questions/68103844/how-to-override-next-js-svg-module-declaration
*/
import Logo from '~/svg/Logo.svg';
// !STARTERCONF -> Select !STARTERCONF and CMD + SHIFT + F
// Before you begin editing, follow all comments with `STARTERCONF`,
// to customize the default configuration.
export default function HomePage() {
return (
<main>
<Head>
<title>Hi</title>
</Head>
<section className='bg-white'>
<div className='layout relative flex min-h-screen flex-col items-center justify-center py-12 text-center'>
<Logo className='w-16' />
<h1 className='mt-4'>Next.js + Tailwind CSS + TypeScript Starter</h1>
<p className='mt-2 text-sm text-gray-800'>
A starter for Next.js, Tailwind CSS, and TypeScript with Absolute
Import, Seo, Link component, pre-configured with Husky{' '}
</p>
<p className='mt-2 text-sm text-gray-700'>
<ArrowLink href='https://github.com/theodorusclarence/ts-nextjs-tailwind-starter'>
See the repository
</ArrowLink>
</p>
<ButtonLink className='mt-6' href='/components' variant='light'>
See all components
</ButtonLink>
<UnstyledLink
href='https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Ftheodorusclarence%2Fts-nextjs-tailwind-starter'
className='mt-4'
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
width='92'
height='32'
src='https://vercel.com/button'
alt='Deploy with Vercel'
/>
</UnstyledLink>
<footer className='absolute bottom-2 text-gray-700'>
© {new Date().getFullYear()} By{' '}
<UnderlineLink href='https://theodorusclarence.com?ref=tsnextstarter'>
Theodorus Clarence
</UnderlineLink>
</footer>
</div>
</section>
</main>
);
}