From e76ab988a7619ff15783e83ffb0037b8370f6dcf Mon Sep 17 00:00:00 2001 From: Jirapat Treesuwan Date: Mon, 21 Oct 2024 19:35:53 +0700 Subject: [PATCH] feat: add cn tailwind merger and gradient card compo --- .eslintrc.js | 4 +- components.json | 20 +++++++ package.json | 8 ++- pnpm-lock.yaml | 69 +++++++++++++++++++++ src/app/globals.css | 79 ++++++++++++++++++++----- src/lib/utils.ts | 6 ++ src/shared/components/gradient-card.tsx | 34 +++++++++++ src/shared/utils/.gitkeep | 0 src/shared/utils/cn.ts | 6 ++ src/shared/utils/index.ts | 1 + tailwind.config.ts | 60 ++++++++++++++++--- 11 files changed, 262 insertions(+), 25 deletions(-) create mode 100644 components.json create mode 100644 src/lib/utils.ts create mode 100644 src/shared/components/gradient-card.tsx delete mode 100644 src/shared/utils/.gitkeep create mode 100644 src/shared/utils/cn.ts create mode 100644 src/shared/utils/index.ts diff --git a/.eslintrc.js b/.eslintrc.js index 3838cd2..95ae009 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,7 +31,7 @@ module.exports = { rules: { // general 'no-alert': 0, - camelcase: 0, + // camelcase: 0, 'no-console': 0, 'no-unused-vars': 1, 'no-nested-ternary': 1, @@ -43,7 +43,7 @@ module.exports = { 'prefer-destructuring': [1, { object: true, array: false }], // typescript - '@typescript-eslint/naming-convention': 1, + '@typescript-eslint/naming-convention': 0, '@typescript-eslint/no-use-before-define': 1, '@typescript-eslint/consistent-type-exports': 1, '@typescript-eslint/consistent-type-imports': 1, diff --git a/components.json b/components.json new file mode 100644 index 0000000..42f059b --- /dev/null +++ b/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 0bd1a39..c23fcd4 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,15 @@ "prepare": "husky" }, "dependencies": { + "@radix-ui/react-icons": "^1.3.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.453.0", "next": "14.2.11", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "tailwind-merge": "^2.5.4", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@types/node": "^20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 811cdb0..c82d6ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,18 @@ importers: .: dependencies: + '@radix-ui/react-icons': + specifier: ^1.3.0 + version: 1.3.0(react@18.3.1) + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.453.0 + version: 0.453.0(react@18.3.1) next: specifier: 14.2.11 version: 14.2.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17,6 +29,12 @@ importers: react-dom: specifier: ^18 version: 18.3.1(react@18.3.1) + tailwind-merge: + specifier: ^2.5.4 + version: 2.5.4 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.14) devDependencies: '@types/node': specifier: ^20 @@ -213,6 +231,11 @@ packages: resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@radix-ui/react-icons@1.3.0': + resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -513,6 +536,9 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + class-variance-authority@0.7.0: + resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -524,6 +550,14 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1220,6 +1254,11 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lucide-react@0.453.0: + resolution: {integrity: sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1750,6 +1789,14 @@ packages: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} + tailwind-merge@2.5.4: + resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==} + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + tailwindcss@3.4.14: resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} @@ -1999,6 +2046,10 @@ snapshots: '@pkgr/core@0.1.1': {} + '@radix-ui/react-icons@1.3.0(react@18.3.1)': + dependencies: + react: 18.3.1 + '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.10.4': {} @@ -2371,6 +2422,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + class-variance-authority@0.7.0: + dependencies: + clsx: 2.0.0 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -2382,6 +2437,10 @@ snapshots: client-only@0.0.1: {} + clsx@2.0.0: {} + + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3235,6 +3294,10 @@ snapshots: lru-cache@10.4.3: {} + lucide-react@0.453.0(react@18.3.1): + dependencies: + react: 18.3.1 + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -3719,6 +3782,12 @@ snapshots: '@pkgr/core': 0.1.1 tslib: 2.8.0 + tailwind-merge@2.5.4: {} + + tailwindcss-animate@1.0.7(tailwindcss@3.4.14): + dependencies: + tailwindcss: 3.4.14 + tailwindcss@3.4.14: dependencies: '@alloc/quick-lru': 5.2.0 diff --git a/src/app/globals.css b/src/app/globals.css index 13d40b8..1dcb0fc 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,21 +2,7 @@ @tailwind components; @tailwind utilities; -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - body { - color: var(--foreground); - background: var(--background); font-family: Arial, Helvetica, sans-serif; } @@ -25,3 +11,68 @@ body { text-wrap: balance; } } + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..2819a83 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/shared/components/gradient-card.tsx b/src/shared/components/gradient-card.tsx new file mode 100644 index 0000000..2ab83f6 --- /dev/null +++ b/src/shared/components/gradient-card.tsx @@ -0,0 +1,34 @@ +import type { PropsWithChildren } from 'react'; +import { cn } from '../utils'; + +type Variant = 'primary' | 'secondary' | 'translucent'; + +type Props = { + className?: string; + variant?: Variant; +}; + +export function GradientCard({ + children, + className, + variant = 'primary', +}: PropsWithChildren) { + const variantClasses = { + primary: 'bg-linear-white before:bg-linear-gray backdrop-blur-[70.5px]', + secondary: + 'bg-linear-black-pink before:bg-linear-gray backdrop-blur-[70.5px]', + translucent: 'bg-linear-purple-gray before:bg-linear-gray', + }; + + return ( +
+ {children} +
+ ); +} diff --git a/src/shared/utils/.gitkeep b/src/shared/utils/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/shared/utils/cn.ts b/src/shared/utils/cn.ts new file mode 100644 index 0000000..2819a83 --- /dev/null +++ b/src/shared/utils/cn.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/shared/utils/index.ts b/src/shared/utils/index.ts new file mode 100644 index 0000000..9ae36a5 --- /dev/null +++ b/src/shared/utils/index.ts @@ -0,0 +1 @@ +export * from './cn'; diff --git a/tailwind.config.ts b/tailwind.config.ts index 4dcc94f..168f2f8 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,19 +1,63 @@ import type { Config } from 'tailwindcss'; const config: Config = { - content: [ + darkMode: ['class'], + content: [ './src/pages/**/*.{js,ts,jsx,tsx,mdx}', './src/components/**/*.{js,ts,jsx,tsx,mdx}', './src/app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { - extend: { - colors: { - background: 'var(--background)', - foreground: 'var(--foreground)', - }, - }, + extend: { + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + } + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + } + } }, - plugins: [], + plugins: [require("tailwindcss-animate")], }; export default config;