From e75e1eb2dba039f42e819fd0320facef056c80df Mon Sep 17 00:00:00 2001 From: Maximilian Liebmann Date: Thu, 8 May 2025 23:41:15 +0200 Subject: [PATCH] feat: refactor UI components and integrate Tailwind CSS for styling --- .vscode/extensions.json | 3 +- components.json | 21 ++ package.json | 8 +- src/app/globals.css | 259 ++++++++++++++++------ src/app/login/login.module.css | 30 --- src/app/login/page.tsx | 18 +- src/components/button.module.css | 60 ----- src/components/button.tsx | 47 ---- src/components/labeled-input.module.css | 37 ---- src/components/labeled-input.tsx | 12 +- src/components/ui/button.tsx | 59 +++++ src/components/user/login-form.tsx | 6 +- src/components/user/sso-login-button.tsx | 11 +- src/components/user/sso-logout-button.tsx | 6 +- src/lib/utils.ts | 6 + yarn.lock | 39 ++++ 16 files changed, 359 insertions(+), 263 deletions(-) create mode 100644 components.json delete mode 100644 src/app/login/login.module.css delete mode 100644 src/components/button.module.css delete mode 100644 src/components/button.tsx delete mode 100644 src/components/labeled-input.module.css create mode 100644 src/components/ui/button.tsx create mode 100644 src/lib/utils.ts diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 3f60dc2..f58edd3 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "esbenp.prettier-vscode", "vivaxy.vscode-conventional-commits", - "dbaeumer.vscode-eslint" + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss" ] } diff --git a/components.json b/components.json new file mode 100644 index 0000000..421c026 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "gray", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/package.json b/package.json index f87691e..e691f81 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,15 @@ "@fortawesome/free-regular-svg-icons": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@radix-ui/react-slot": "^1.2.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.508.0", "next": "15.3.2", "next-auth": "^5.0.0-beta.25", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "tailwind-merge": "^3.2.0" }, "devDependencies": { "@eslint/eslintrc": "3.3.1", @@ -33,6 +38,7 @@ "prettier": "3.5.3", "prisma": "6.7.0", "tailwindcss": "^4.1.5", + "tw-animate-css": "^1.2.9", "typescript": "5.8.3" }, "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" diff --git a/src/app/globals.css b/src/app/globals.css index 219065f..322500b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,80 +1,215 @@ @import 'tailwindcss'; +@import 'tw-animate-css'; + +@custom-variant dark (&:is(.dark *)); :root { - --background: #ffffff; - --foreground: #3a3a3a; + --radius: 0.625rem; - --neutral: #808080; + --background: oklch(1 0 0); - --primary-50: rgba(59, 130, 246, 0.5); - --primary-75: rgba(59, 130, 246, 0.75); - --primary-100: rgba(59, 130, 246, 1); + --foreground: oklch(0.13 0.028 261.692); - --warning-50: rgba(245, 158, 11, 0.5); - --warning-75: rgba(245, 158, 11, 0.75); - --warning-100: rgba(245, 158, 11, 1); + --card: oklch(1 0 0); - --success-50: rgba(22, 163, 74, 0.5); - --success-75: rgba(22, 163, 74, 0.75); - --success-100: rgba(22, 163, 74, 1); + --card-foreground: oklch(0.13 0.028 261.692); - --danger-50: rgba(220, 38, 38, 0.5); - --danger-75: rgba(220, 38, 38, 0.75); - --danger-100: rgba(220, 38, 38, 1); + --popover: oklch(1 0 0); - --button-text-size: 18px; + --popover-foreground: oklch(0.13 0.028 261.692); - --neutral-50: rgb(204, 204, 204, 0.5); - --neutral-75: rgb(204, 204, 204, 0.75); - --neutral-100: rgb(204, 204, 204, 1); + --primary: oklch(0.21 0.034 264.665); - --textbox-50: rgb(204, 204, 204, 0.5); - --textbox-75: rgb(204, 204, 204, 0.75); - --textbox-100: rgb(204, 204, 204, 1); + --primary-foreground: oklch(0.985 0.002 247.839); - --base-1: #f3f3f3; + --secondary: oklch(0.967 0.003 264.542); + + --secondary-foreground: oklch(0.21 0.034 264.665); + + --muted: oklch(0.967 0.003 264.542); + + --muted-foreground: oklch(0.551 0.027 264.364); + + --accent: oklch(0.967 0.003 264.542); + + --accent-foreground: oklch(0.21 0.034 264.665); + + --destructive: oklch(0.577 0.245 27.325); + + --border: oklch(0.928 0.006 264.531); + + --input: oklch(0.928 0.006 264.531); + + --ring: oklch(0.707 0.022 261.325); + + --chart-1: oklch(0.646 0.222 41.116); + + --chart-2: oklch(0.6 0.118 184.704); + + --chart-3: oklch(0.398 0.07 227.392); + + --chart-4: oklch(0.828 0.189 84.429); + + --chart-5: oklch(0.769 0.188 70.08); + + --sidebar: oklch(0.985 0.002 247.839); + + --sidebar-foreground: oklch(0.13 0.028 261.692); + + --sidebar-primary: oklch(0.21 0.034 264.665); + + --sidebar-primary-foreground: oklch(0.985 0.002 247.839); + + --sidebar-accent: oklch(0.967 0.003 264.542); + + --sidebar-accent-foreground: oklch(0.21 0.034 264.665); + + --sidebar-border: oklch(0.928 0.006 264.531); + + --sidebar-ring: oklch(0.707 0.022 261.325); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #e5e7eb; +@theme inline { + --radius-sm: calc(var(--radius) - 4px); - --textbox-50: rgb(75, 85, 99, 0.5); - --textbox-75: rgb(75, 85, 99, 0.75); - --textbox-100: rgb(75, 85, 99, 1); + --radius-md: calc(var(--radius) - 2px); - --base-1: #111111; - } -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -a { - color: inherit; - text-decoration: none; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; + --radius-lg: var(--radius); + + --radius-xl: calc(var(--radius) + 4px); + + --color-background: var(--background); + + --color-foreground: var(--foreground); + + --color-card: var(--card); + + --color-card-foreground: var(--card-foreground); + + --color-popover: var(--popover); + + --color-popover-foreground: var(--popover-foreground); + + --color-primary: var(--primary); + + --color-primary-foreground: var(--primary-foreground); + + --color-secondary: var(--secondary); + + --color-secondary-foreground: var(--secondary-foreground); + + --color-muted: var(--muted); + + --color-muted-foreground: var(--muted-foreground); + + --color-accent: var(--accent); + + --color-accent-foreground: var(--accent-foreground); + + --color-destructive: var(--destructive); + + --color-border: var(--border); + + --color-input: var(--input); + + --color-ring: var(--ring); + + --color-chart-1: var(--chart-1); + + --color-chart-2: var(--chart-2); + + --color-chart-3: var(--chart-3); + + --color-chart-4: var(--chart-4); + + --color-chart-5: var(--chart-5); + + --color-sidebar: var(--sidebar); + + --color-sidebar-foreground: var(--sidebar-foreground); + + --color-sidebar-primary: var(--sidebar-primary); + + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + + --color-sidebar-accent: var(--sidebar-accent); + + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + + --color-sidebar-border: var(--sidebar-border); + + --color-sidebar-ring: var(--sidebar-ring); +} + +.dark { + --background: oklch(0.13 0.028 261.692); + + --foreground: oklch(0.985 0.002 247.839); + + --card: oklch(0.21 0.034 264.665); + + --card-foreground: oklch(0.985 0.002 247.839); + + --popover: oklch(0.21 0.034 264.665); + + --popover-foreground: oklch(0.985 0.002 247.839); + + --primary: oklch(0.928 0.006 264.531); + + --primary-foreground: oklch(0.21 0.034 264.665); + + --secondary: oklch(0.278 0.033 256.848); + + --secondary-foreground: oklch(0.985 0.002 247.839); + + --muted: oklch(0.278 0.033 256.848); + + --muted-foreground: oklch(0.707 0.022 261.325); + + --accent: oklch(0.278 0.033 256.848); + + --accent-foreground: oklch(0.985 0.002 247.839); + + --destructive: oklch(0.704 0.191 22.216); + + --border: oklch(1 0 0 / 10%); + + --input: oklch(1 0 0 / 15%); + + --ring: oklch(0.551 0.027 264.364); + + --chart-1: oklch(0.488 0.243 264.376); + + --chart-2: oklch(0.696 0.17 162.48); + + --chart-3: oklch(0.769 0.188 70.08); + + --chart-4: oklch(0.627 0.265 303.9); + + --chart-5: oklch(0.645 0.246 16.439); + + --sidebar: oklch(0.21 0.034 264.665); + + --sidebar-foreground: oklch(0.985 0.002 247.839); + + --sidebar-primary: oklch(0.488 0.243 264.376); + + --sidebar-primary-foreground: oklch(0.985 0.002 247.839); + + --sidebar-accent: oklch(0.278 0.033 256.848); + + --sidebar-accent-foreground: oklch(0.985 0.002 247.839); + + --sidebar-border: oklch(1 0 0 / 10%); + + --sidebar-ring: oklch(0.551 0.027 264.364); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; } } diff --git a/src/app/login/login.module.css b/src/app/login/login.module.css deleted file mode 100644 index 73b5f1b..0000000 --- a/src/app/login/login.module.css +++ /dev/null @@ -1,30 +0,0 @@ -body:has(.loginContainer) { - display: flex; - justify-content: center; - align-items: center; - height: 100svh; -} - -.loginContainer { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 1.5rem; - padding: 2rem; - width: 300px; - - background-color: var(--base-1); - border-radius: 1rem; - border: 2px solid var(--foreground); -} - -.loginContainer h1 { - margin-bottom: 1rem; -} - -.loginContainer form { - display: flex; - flex-direction: column; - gap: 1rem; -} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 9aadba1..6532785 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -3,8 +3,6 @@ import SSOLogin from '@/components/user/sso-login-button'; import LoginForm from '@/components/user/login-form'; import { redirect } from 'next/navigation'; -import style from './login.module.css'; - import '@/app/globals.css'; export default async function LoginPage() { @@ -15,16 +13,18 @@ export default async function LoginPage() { } return ( -
-

Login

+
+
+

Login

- + -
+
- {process.env.AUTH_AUTHENTIK_ISSUER && ( - - )} + {process.env.AUTH_AUTHENTIK_ISSUER && ( + + )} +
); } diff --git a/src/components/button.module.css b/src/components/button.module.css deleted file mode 100644 index 05fb359..0000000 --- a/src/components/button.module.css +++ /dev/null @@ -1,60 +0,0 @@ -.button { - background-color: var(--color-50); - border-radius: 16px; - padding: 10px 20px; - outline: none; - border: 2px solid var(--color-100); - transition: background-color 0.3s ease; - height: 50px; -} - -.button:hover { - background-color: var(--color-75); -} - -.button:active { - background-color: var(--color-100); -} - -.button span { - color: var(--foreground); - font-size: var(--button-text-size); - font-weight: 600; -} - -.icon { - display: grid; - grid-template-columns: 25px 1fr; - gap: 10px; - align-items: center; -} - -.style_primary { - --color-50: var(--primary-50); - --color-75: var(--primary-75); - --color-100: var(--primary-100); -} - -.style_warning { - --color-50: var(--warning-50); - --color-75: var(--warning-75); - --color-100: var(--warning-100); -} - -.style_success { - --color-50: var(--success-50); - --color-75: var(--success-75); - --color-100: var(--success-100); -} - -.style_danger { - --color-50: var(--danger-50); - --color-75: var(--danger-75); - --color-100: var(--danger-100); -} - -.style_neutral { - --color-50: var(--neutral-50); - --color-75: var(--neutral-75); - --color-100: var(--neutral-100); -} diff --git a/src/components/button.tsx b/src/components/button.tsx deleted file mode 100644 index 87094ad..0000000 --- a/src/components/button.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import style from './button.module.css'; - -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -export default function Button({ - type, - children, - mode = 'primary', - icon, - width, -}: { - type?: 'button' | 'submit' | 'reset'; - children?: React.ReactNode; - mode?: 'primary' | 'warning' | 'success' | 'danger' | 'neutral'; - icon?: IconProp; - width?: number; -}) { - if (!icon) { - return ( - - ); - } else { - return ( - - ); - } -} diff --git a/src/components/labeled-input.module.css b/src/components/labeled-input.module.css deleted file mode 100644 index 85f46ed..0000000 --- a/src/components/labeled-input.module.css +++ /dev/null @@ -1,37 +0,0 @@ -.input, -.input * { - box-sizing: border-box; -} - -.input { - position: relative; - overflow: visible; - width: 250px; -} - -.input input { - width: 100%; - height: 50px; - border: 1px solid var(--textbox-75); - border-radius: 16px; - padding: 10px 20px; - background-color: var(--textbox-50); - color: var(--foreground); - font-size: 16px; - transition: - background-color 0.2s ease, - border-color 0.2s ease, - box-shadow 0.2s ease; -} - -.input input:hover { - background-color: var(--textbox-75); - border-color: var(--textbox-100); -} - -.input input:focus { - outline: none; - background-color: var(--textbox-100); - border-color: var(--primary-100); - box-shadow: 0 0 0 2px var(--primary-50); -} diff --git a/src/components/labeled-input.tsx b/src/components/labeled-input.tsx index 48f27fe..572fa01 100644 --- a/src/components/labeled-input.tsx +++ b/src/components/labeled-input.tsx @@ -1,5 +1,3 @@ -import style from './labeled-input.module.css'; - export default function LabeledInput({ type, width, @@ -17,8 +15,9 @@ export default function LabeledInput({ if (!label) { return ( -
+
- +
+ svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/src/components/user/login-form.tsx b/src/components/user/login-form.tsx index 12d1f3e..a6e6117 100644 --- a/src/components/user/login-form.tsx +++ b/src/components/user/login-form.tsx @@ -1,10 +1,10 @@ import LabeledInput from '@/components/labeled-input'; -import Button from '../button'; +import { Button } from '@/components/ui/button'; export default function LoginForm() { return (
-
+ - diff --git a/src/components/user/sso-login-button.tsx b/src/components/user/sso-login-button.tsx index 272872e..6ffa1d4 100644 --- a/src/components/user/sso-login-button.tsx +++ b/src/components/user/sso-login-button.tsx @@ -1,6 +1,6 @@ import { signIn } from '@/auth'; -import Button from '../button'; -import { faOpenid } from '@fortawesome/free-brands-svg-icons'; +import { Button } from '@/components/ui/button'; +//import { faOpenid } from '@fortawesome/free-brands-svg-icons'; export default function SSOLogin({ provider, @@ -11,12 +11,17 @@ export default function SSOLogin({ }) { return (
{ 'use server'; await signIn(provider); }} > -
diff --git a/src/components/user/sso-logout-button.tsx b/src/components/user/sso-logout-button.tsx index 9a42274..a5531bc 100644 --- a/src/components/user/sso-logout-button.tsx +++ b/src/components/user/sso-logout-button.tsx @@ -1,6 +1,6 @@ import { signOut } from '@/auth'; -import Button from '../button'; -import { faDoorOpen } from '@fortawesome/free-solid-svg-icons'; +import { Button } from '@/components/ui/button'; +//import { faDoorOpen } from '@fortawesome/free-solid-svg-icons'; export function Logout() { return ( @@ -10,7 +10,7 @@ export function Logout() { await signOut({ redirectTo: '/login' }); }} > - diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /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/yarn.lock b/yarn.lock index 1336606..bedf1c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -578,6 +578,18 @@ dependencies: "@prisma/debug" "6.7.0" +"@radix-ui/react-compose-refs@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30" + integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== + +"@radix-ui/react-slot@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.2.tgz#18e6533e778a2051edc2ad0773da8e22f03f626a" + integrity sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" @@ -1169,11 +1181,23 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +class-variance-authority@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787" + integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg== + dependencies: + clsx "^2.1.1" + client-only@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -2517,6 +2541,11 @@ loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lucide-react@^0.508.0: + version "0.508.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.508.0.tgz#9155465e2051bb42a0ebb2d806f25a8b7125225f" + integrity sha512-gcP16PnexqtOFrTtv98kVsGzTfnbPekzZiQfByi2S89xfk7E/4uKE1USZqccIp58v42LqkO7MuwpCqshwSrJCg== + math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" @@ -3316,6 +3345,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +tailwind-merge@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-3.2.0.tgz#bedcf6a67a8c982da5913afcba9c854f35abb857" + integrity sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA== + tailwindcss@4.1.5, tailwindcss@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.5.tgz#d35607f1a351051bd29cda7e59ab2c222ca8deb6" @@ -3366,6 +3400,11 @@ tslib@^2.4.0, tslib@^2.8.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +tw-animate-css@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/tw-animate-css/-/tw-animate-css-1.2.9.tgz#b25d5fb31fd3c3ec6d91c1d1842c0d7c0fdbd999" + integrity sha512-9O4k1at9pMQff9EAcCEuy1UNO43JmaPQvq+0lwza9Y0BQ6LB38NiMj+qHqjoQf40355MX+gs6wtlR6H9WsSXFg== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"