diff --git a/.storybook/main.ts b/.storybook/main.ts index ab80dbb..2987901 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -10,8 +10,7 @@ export default { '@storybook/addon-interactions', ], logLevel: 'error', - // enable when you have a public folder with assets - //staticDirs: ['../public'], + staticDirs: ['../public'], typescript: { reactDocgen: false, check: false }, core: { disableTelemetry: true, disableWhatsNewNotifications: true }, framework: { @@ -19,9 +18,13 @@ export default { options: { builder: { useSWC: true } }, }, previewBody: - '' + // Warning: this should be same as the one in `src/styles/globals.css` - '', + ` + `, + webpack: async config => ({ ...config, // Performance Hints do not make sense on Storybook as it is bloated by design diff --git a/.storybook/preview.ts b/.storybook/preview.tsx similarity index 72% rename from .storybook/preview.ts rename to .storybook/preview.tsx index f6cfcd5..140eca9 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.tsx @@ -1,4 +1,6 @@ +import React from 'react'; import { withThemeByDataAttribute } from '@storybook/addon-themes'; +import { ToastProvider } from '../providers/toast'; import type { Preview, ReactRenderer } from '@storybook/react'; import '../styles/globals.css'; @@ -19,6 +21,11 @@ const preview: Preview = { defaultTheme: 'light', attributeName: 'data-theme', }), + Story => ( + + + + ), ], }; diff --git a/SECURITY.md b/SECURITY.md index 4496ba7..6a963d5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -9,4 +9,4 @@ The project is currently under development, so we only support this version(s) o | 0.1.X | :white_check_mark: | > \[!NOTE]\ ->There is no compatibility yet with etherpad-lite +> There is no compatibility yet with etherpad-lite diff --git a/app/client/page.tsx b/app/client/page.tsx index 041a541..7ef1dea 100644 --- a/app/client/page.tsx +++ b/app/client/page.tsx @@ -2,7 +2,9 @@ import { useEffect } from 'react'; import io from 'socket.io-client'; -const Page = () => { +import type { FC } from 'react'; + +const Page: FC = () => { useEffect(() => { const socket = io(); socket.on('connect', () => { diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..94f28d9 --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,6 @@ +import Landing from '@/components/Sections/Landing'; +import type { FC } from 'react'; + +const Page: FC = () => ; + +export default Page; diff --git a/components/Common/Background/index.module.css b/components/Common/Background/index.module.css new file mode 100644 index 0000000..05ca869 --- /dev/null +++ b/components/Common/Background/index.module.css @@ -0,0 +1,23 @@ +.background { + @apply absolute + left-0 + top-0 + -z-10 + size-full + bg-[url('/static/background.svg')] + bg-center + bg-no-repeat; + + &::after { + @apply absolute + inset-0 + m-auto + aspect-square + w-[300px] + rounded-full + bg-ether-400 + blur-[120px] + content-[''] + dark:bg-ether-700; + } +} diff --git a/components/Common/Background/index.stories.tsx b/components/Common/Background/index.stories.tsx new file mode 100644 index 0000000..d164bcf --- /dev/null +++ b/components/Common/Background/index.stories.tsx @@ -0,0 +1,9 @@ +import BluredBackground from './'; +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +type Story = StoryObj; +type Meta = MetaObj; + +export const Default: Story = {}; + +export default { component: BluredBackground } as Meta; diff --git a/components/Common/Background/index.tsx b/components/Common/Background/index.tsx new file mode 100644 index 0000000..9a30612 --- /dev/null +++ b/components/Common/Background/index.tsx @@ -0,0 +1,6 @@ +import styles from './index.module.css'; +import type { FC } from 'react'; + +const BluredBackground: FC = () =>
; + +export default BluredBackground; diff --git a/components/Common/Button/index.stories.tsx b/components/Common/Button/index.stories.tsx index 833d1df..0930227 100644 --- a/components/Common/Button/index.stories.tsx +++ b/components/Common/Button/index.stories.tsx @@ -1,4 +1,4 @@ -import { HeartIcon } from '@heroicons/react/20/solid'; +import { Heart } from 'lucide-react'; import Button from './'; import type { Meta as MetaObj, StoryObj } from '@storybook/react'; @@ -9,7 +9,7 @@ const defaultStory: Story = { args: { children: ( <> - We love Etherpad + We love Etherpad ), // it's allow to have button on the storybook layout to toggle the disabled state diff --git a/components/Common/Input/index.module.css b/components/Common/Input/index.module.css index 6b9554d..668d1dd 100644 --- a/components/Common/Input/index.module.css +++ b/components/Common/Input/index.module.css @@ -1,5 +1,6 @@ .inputWrapper { @apply flex + w-full flex-col gap-1.5; diff --git a/components/Common/Select/index.tsx b/components/Common/Select/index.tsx index 83db3b4..28ff30c 100644 --- a/components/Common/Select/index.tsx +++ b/components/Common/Select/index.tsx @@ -1,6 +1,6 @@ 'use client'; import * as Primitive from '@radix-ui/react-select'; -import { ChevronDownIcon } from '@heroicons/react/24/outline'; +import { ChevronDown } from 'lucide-react'; import { useId, useMemo } from 'react'; import classNames from 'classnames'; import styles from './index.module.css'; @@ -70,7 +70,7 @@ const Select: FC = ({ id={id} > - + ; +type Meta = MetaObj; + +export const Default: Story = { + args: { + open: true, + duration: 5000, + children: 'OK, everything is fine!', + }, +}; + +export const TimedNotification: Story = { + args: { + duration: 5000, + children: 'OK, everything is fine!', + }, +}; + +export const WithJSX: Story = { + args: { + open: true, + children: ( + <> + + OK, everything is fine! + + ), + }, +}; + +export const Success: Story = { + args: { + open: true, + variant: 'success', + children: 'OK, everything is fine!', + }, +}; + +export const Error: Story = { + args: { + open: true, + variant: 'error', + children: 'Something went wrong!', + }, +}; + +export const Warning: Story = { + args: { + open: true, + variant: 'warning', + children: 'Be careful!', + }, +}; + +export default { component: Notification } as Meta; diff --git a/components/Common/Toast/index.tsx b/components/Common/Toast/index.tsx new file mode 100644 index 0000000..d5e22f1 --- /dev/null +++ b/components/Common/Toast/index.tsx @@ -0,0 +1,35 @@ +import * as ToastPrimitive from '@radix-ui/react-toast'; +import classNames from 'classnames'; +import styles from './index.module.css'; +import type { FC, PropsWithChildren } from 'react'; + +type ToastProps = PropsWithChildren<{ + variant?: 'success' | 'error' | 'warning' | 'info'; + open?: boolean; + duration?: number; + onChange?: (value: boolean) => void; + className?: string; +}>; + +const Toast: FC = ({ + variant = 'info', + open, + // 2s duration + duration = 2000, + onChange, + children, + className, +}) => ( + + + {children} + + +); + +export default Toast; diff --git a/components/Logo/Platform/Apple.tsx b/components/Logo/Platform/Apple.tsx new file mode 100644 index 0000000..fa15ee4 --- /dev/null +++ b/components/Logo/Platform/Apple.tsx @@ -0,0 +1,23 @@ +import type { FC, SVGProps } from 'react'; + +const Apple: FC> = props => ( + + + + +); + +export default Apple; diff --git a/components/Logo/Platform/Generic.tsx b/components/Logo/Platform/Generic.tsx new file mode 100644 index 0000000..bdae910 --- /dev/null +++ b/components/Logo/Platform/Generic.tsx @@ -0,0 +1,22 @@ +import type { FC, SVGProps } from 'react'; + +const Generic: FC> = props => ( + + + +); + +export default Generic; diff --git a/components/Logo/Platform/Linux.tsx b/components/Logo/Platform/Linux.tsx new file mode 100644 index 0000000..20d408e --- /dev/null +++ b/components/Logo/Platform/Linux.tsx @@ -0,0 +1,969 @@ +import type { FC, SVGProps } from 'react'; + +const Linux: FC> = props => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default Linux; diff --git a/components/Logo/Platform/Microsoft.tsx b/components/Logo/Platform/Microsoft.tsx new file mode 100644 index 0000000..4567525 --- /dev/null +++ b/components/Logo/Platform/Microsoft.tsx @@ -0,0 +1,19 @@ +import type { FC, SVGProps } from 'react'; + +const Microsoft: FC> = props => ( + + + + + + +); + +export default Microsoft; diff --git a/components/Logo/Platform/platform-logos.stories.tsx b/components/Logo/Platform/platform-logos.stories.tsx new file mode 100644 index 0000000..4e23205 --- /dev/null +++ b/components/Logo/Platform/platform-logos.stories.tsx @@ -0,0 +1,21 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import Apple from './Apple'; +import Generic from './Generic'; +import Linux from './Linux'; +import Microsoft from './Microsoft'; + +export const PlatformLogos: StoryObj = { + render: () => ( +
+
+ + + + +
+
+ ), +}; + +export default { title: 'Design System' } as MetaObj; diff --git a/components/NoteBook/ToolsBar/index.module.css b/components/NoteBook/ToolsBar/index.module.css new file mode 100644 index 0000000..347229a --- /dev/null +++ b/components/NoteBook/ToolsBar/index.module.css @@ -0,0 +1,31 @@ +.toolsBar { + @apply flex + items-center + justify-between + border-b + border-gray-200 + bg-white + p-2 + dark:border-gray-800 + dark:bg-gray-950; + + .actionGroup { + @apply flex + items-center + justify-center + gap-2; + } + + button { + @apply flex + items-center + justify-center + rounded-md + p-2 + transition + duration-200 + ease-in-out + hover:bg-gray-100 + dark:hover:bg-gray-900; + } +} diff --git a/components/NoteBook/ToolsBar/index.stories.tsx b/components/NoteBook/ToolsBar/index.stories.tsx new file mode 100644 index 0000000..ffed697 --- /dev/null +++ b/components/NoteBook/ToolsBar/index.stories.tsx @@ -0,0 +1,9 @@ +import ToolsBar from '.'; +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +type Story = StoryObj; +type Meta = MetaObj; + +export const Default: Story = {}; + +export default { component: ToolsBar } as Meta; diff --git a/components/NoteBook/ToolsBar/index.tsx b/components/NoteBook/ToolsBar/index.tsx new file mode 100644 index 0000000..2713a60 --- /dev/null +++ b/components/NoteBook/ToolsBar/index.tsx @@ -0,0 +1,122 @@ +import { + Bold, + Italic, + Underline, + Strikethrough, + ListOrdered, + List, + Indent, + AlignLeft, + AlignCenter, + AlignRight, + AlignJustify, + RotateCcw, + RotateCw, + Image as ImageIcon, +} from 'lucide-react'; +import Select from '@/components/Common/Select'; +import styles from './index.module.css'; +import type { FC } from 'react'; + +const colors = [ + { + label: 'Default', + value: 'default', + }, + { + label: 'Red', + value: 'red-500', + iconImage: , + }, + { + label: 'Green', + value: 'green-500', + iconImage: , + }, + { + label: 'Blue', + value: 'blue-500', + iconImage: , + }, +]; +const fonts = [ + { label: 'Arial', value: 'Arial' }, + { label: 'Times New Roman', value: 'Times New Roman' }, + { label: 'Courier New', value: 'Courier New' }, + { label: 'Verdana', value: 'Verdana' }, + { label: 'Georgia', value: 'Georgia' }, + { label: 'Comic Sans MS', value: 'Comic Sans MS' }, + { label: 'Trebuchet MS', value: 'Trebuchet MS' }, + { label: 'Arial Black', value: 'Arial Black' }, + { label: 'Impact', value: 'Impact' }, + { label: 'Lucida Console', value: 'Lucida Console' }, +]; + +const ToolsBar: FC = () => ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+); + +export default ToolsBar; diff --git a/components/Sections/Landing/index.module.css b/components/Sections/Landing/index.module.css new file mode 100644 index 0000000..de4db06 --- /dev/null +++ b/components/Sections/Landing/index.module.css @@ -0,0 +1,33 @@ +.landing { + @apply mx-auto + flex + size-full + max-w-md + flex-col + justify-center + gap-4 + text-center; + + p { + @apply text-2xl + font-bold; + } + + .note { + @apply text-sm + font-light + text-gray-500 + dark:text-gray-400; + } + + form { + @apply flex + w-full + flex-col + gap-4; + + button { + @apply border-gray-950 dark:border; + } + } +} diff --git a/components/Sections/Landing/index.stories.tsx b/components/Sections/Landing/index.stories.tsx new file mode 100644 index 0000000..ea247bb --- /dev/null +++ b/components/Sections/Landing/index.stories.tsx @@ -0,0 +1,9 @@ +import Landing from './'; +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +type Story = StoryObj; +type Meta = MetaObj; + +export const Default: Story = {}; + +export default { component: Landing } as Meta; diff --git a/components/Sections/Landing/index.tsx b/components/Sections/Landing/index.tsx new file mode 100644 index 0000000..6f5d0ed --- /dev/null +++ b/components/Sections/Landing/index.tsx @@ -0,0 +1,24 @@ +import Button from '@/components/Common/Button'; +import BluredBackground from '@/components/Common/Background'; +import Input from '@/components/Common/Input'; +import style from './index.module.css'; +import type { FC } from 'react'; + +const Landing: FC = () => ( + <> + +
+

Create or open a new note book named:

+

+ If you keep the note book name empty, it will create a random name for + you. +

+
+ + +
+
+ +); + +export default Landing; diff --git a/components/Sections/NotFound/index.tsx b/components/Sections/NotFound/index.tsx index c2efa81..35340de 100644 --- a/components/Sections/NotFound/index.tsx +++ b/components/Sections/NotFound/index.tsx @@ -1,4 +1,4 @@ -import { ArrowRightIcon } from '@heroicons/react/20/solid'; +import { ArrowRight } from 'lucide-react'; import Link from 'next/link'; import style from './index.module.css'; import type { FC } from 'react'; @@ -9,7 +9,7 @@ const NotFoundSection: FC = () => (

The page you requested could not be found.

Go back home - + ); diff --git a/docs/Techincal.md b/docs/Techincal.md index 35976e6..e1eeef3 100644 --- a/docs/Techincal.md +++ b/docs/Techincal.md @@ -10,7 +10,7 @@ This documentation is for Etherpad-next collaborator. - [PostCSS][] - [TypeScript][] - [Storybook][] -- [heroicons](https://heroicons.com) +- [`lucide-react`](https://www.lucide.dev) - [prettier](https://www.prettier.io) - [eslint](https://www.eslint.org) diff --git a/package-lock.json b/package-lock.json index 3c69fe2..29437a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,11 @@ "name": "etherpad-next", "version": "0.1.0", "dependencies": { - "@heroicons/react": "~2.1.1", "@radix-ui/react-avatar": "~1.0.4", "@radix-ui/react-select": "~2.0.0", + "@radix-ui/react-toast": "~1.1.5", "classnames": "~2.5.1", + "lucide-react": "~0.340.0", "next": "~14.1.0", "pino": "~8.19.0", "pino-pretty": "~10.3.1", @@ -28,7 +29,6 @@ "@storybook/addon-themes": "~7.6.17", "@storybook/nextjs": "~7.6.17", "@storybook/react": "~7.6.10", - "@types/find-root": "~1.1.4", "@types/node": "~20.11.20", "@types/react": "~18.2.58", "@types/react-dom": "~18.2.19", @@ -2887,14 +2887,6 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, - "node_modules/@heroicons/react": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.1.tgz", - "integrity": "sha512-JyyN9Lo66kirbCMuMMRPtJxtKJoIsXKS569ebHGGRKbl8s4CtUfLnyKJxteA+vIKySocO4s1SkTkGS4xtG/yEA==", - "peerDependencies": { - "react": ">= 16" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -3786,6 +3778,30 @@ } } }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-primitive": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", @@ -3926,6 +3942,40 @@ } } }, + "node_modules/@radix-ui/react-toast": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz", + "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toggle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", @@ -6225,12 +6275,6 @@ "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", "dev": true }, - "node_modules/@types/find-root": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/find-root/-/find-root-1.1.4.tgz", - "integrity": "sha512-2EXK/+gVhVgtt4JqThbEncORvpYJKzi9tQGmI3EkU2jTgMzQsrPm/hbd5xe5uPdeFzIW5gh2PRvvPjaUsI8vpg==", - "dev": true - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -13544,6 +13588,14 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.340.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.340.0.tgz", + "integrity": "sha512-mWzYhbyy2d+qKuKHh+GWElPwa+kIquTnKbmSLGWOuZy+bjfZCkYD8DQWVFlqI4mQwc4HNxcqcOvtQ7ZS2PwURg==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", diff --git a/package.json b/package.json index d2975f7..84d4d7b 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,11 @@ "test": "vitest" }, "dependencies": { - "@heroicons/react": "~2.1.1", "@radix-ui/react-avatar": "~1.0.4", "@radix-ui/react-select": "~2.0.0", + "@radix-ui/react-toast": "~1.1.5", "classnames": "~2.5.1", + "lucide-react": "~0.340.0", "next": "~14.1.0", "pino": "~8.19.0", "pino-pretty": "~10.3.1", diff --git a/providers/toast.tsx b/providers/toast.tsx new file mode 100644 index 0000000..11e438f --- /dev/null +++ b/providers/toast.tsx @@ -0,0 +1,54 @@ +'use client'; +import * as ToastPrimitive from '@radix-ui/react-toast'; +import { createContext, useState } from 'react'; +import Toast from '@/components/Common/Toast'; +import type { + Dispatch, + FC, + PropsWithChildren, + ReactNode, + SetStateAction, +} from 'react'; + +type ToastContextType = { + variant?: 'success' | 'error' | 'warning' | 'info'; + message: string | ReactNode; + duration?: number; +} | null; + +type ToastProps = { + viewportClassName?: string; +} & ToastPrimitive.ToastProviderProps; + +const ToastContext = createContext(null); + +export const ToastDispatch = createContext< + Dispatch> +>(() => {}); + +export const ToastProvider: FC> = ({ + viewportClassName, + children, + ...props +}) => { + const [notification, dispatch] = useState(null); + + return ( + + + + {children} + + {notification && ( + + {notification.message} + + )} + + + + ); +}; diff --git a/public/static/background.svg b/public/static/background.svg new file mode 100644 index 0000000..05e9d63 --- /dev/null +++ b/public/static/background.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/styles/globals.css b/styles/globals.css index 7dac0ce..adc994d 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -9,14 +9,15 @@ @tailwind utilities; @tailwind variants; -html { - @apply scroll-smooth; -} - +html, body { @apply h-screen w-screen - bg-gray-100 + scroll-smooth; +} + +body { + @apply bg-gray-100 text-gray-900 dark:bg-gray-900 dark:text-gray-100;