From c24ef594a287919c056da5a9518904d0627888e8 Mon Sep 17 00:00:00 2001 From: Tobias Geiselmann <11350542+TobiasGeiselmann@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:15:06 +0100 Subject: [PATCH 1/2] Add example for shadcn/ui with remix-themes --- shadcn-ui-themes/.eslintrc.js | 4 + shadcn-ui-themes/.gitignore | 6 + shadcn-ui-themes/README.md | 31 +++ .../app/components/mode-toggle.tsx | 34 +++ shadcn-ui-themes/app/components/ui/button.tsx | 56 +++++ .../app/components/ui/dropdown-menu.tsx | 198 ++++++++++++++++++ shadcn-ui-themes/app/lib/utils.ts | 6 + shadcn-ui-themes/app/root.tsx | 62 ++++++ shadcn-ui-themes/app/routes/_index.tsx | 11 + .../app/routes/action.set-theme.ts | 5 + shadcn-ui-themes/app/sessions.server.ts | 21 ++ shadcn-ui-themes/app/tailwind.css | 77 +++++++ shadcn-ui-themes/components.json | 16 ++ shadcn-ui-themes/package.json | 40 ++++ shadcn-ui-themes/public/favicon.ico | Bin 0 -> 16958 bytes shadcn-ui-themes/remix.config.js | 8 + shadcn-ui-themes/remix.env.d.ts | 2 + shadcn-ui-themes/sandbox.config.json | 7 + shadcn-ui-themes/tailwind.config.ts | 78 +++++++ shadcn-ui-themes/tsconfig.json | 22 ++ 20 files changed, 684 insertions(+) create mode 100644 shadcn-ui-themes/.eslintrc.js create mode 100644 shadcn-ui-themes/.gitignore create mode 100644 shadcn-ui-themes/README.md create mode 100644 shadcn-ui-themes/app/components/mode-toggle.tsx create mode 100644 shadcn-ui-themes/app/components/ui/button.tsx create mode 100644 shadcn-ui-themes/app/components/ui/dropdown-menu.tsx create mode 100644 shadcn-ui-themes/app/lib/utils.ts create mode 100644 shadcn-ui-themes/app/root.tsx create mode 100644 shadcn-ui-themes/app/routes/_index.tsx create mode 100644 shadcn-ui-themes/app/routes/action.set-theme.ts create mode 100644 shadcn-ui-themes/app/sessions.server.ts create mode 100644 shadcn-ui-themes/app/tailwind.css create mode 100644 shadcn-ui-themes/components.json create mode 100644 shadcn-ui-themes/package.json create mode 100644 shadcn-ui-themes/public/favicon.ico create mode 100644 shadcn-ui-themes/remix.config.js create mode 100644 shadcn-ui-themes/remix.env.d.ts create mode 100644 shadcn-ui-themes/sandbox.config.json create mode 100644 shadcn-ui-themes/tailwind.config.ts create mode 100644 shadcn-ui-themes/tsconfig.json diff --git a/shadcn-ui-themes/.eslintrc.js b/shadcn-ui-themes/.eslintrc.js new file mode 100644 index 00000000..2061cd22 --- /dev/null +++ b/shadcn-ui-themes/.eslintrc.js @@ -0,0 +1,4 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"], +}; diff --git a/shadcn-ui-themes/.gitignore b/shadcn-ui-themes/.gitignore new file mode 100644 index 00000000..3f7bf98d --- /dev/null +++ b/shadcn-ui-themes/.gitignore @@ -0,0 +1,6 @@ +node_modules + +/.cache +/build +/public/build +.env diff --git a/shadcn-ui-themes/README.md b/shadcn-ui-themes/README.md new file mode 100644 index 00000000..3d29a8ea --- /dev/null +++ b/shadcn-ui-themes/README.md @@ -0,0 +1,31 @@ +# shadcn/ui with Theme Switcher + +This example shows how to use [shadcn/ui](https://ui.shadcn.com/) components along with [remix-themes](https://github.com/abereghici/remix-themes) to store the preferred color scheme in the session and let the user manually switch between light and dark mode. + +> [!NOTE] +> This example uses Remix v2. + +## Preview + +Open this example on [CodeSandbox](https://codesandbox.com): + +[![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/remix-run/examples/tree/main/shadcn-ui-themes) + +## Example + +This example uses `shadcn/ui` components. They are stored in the `app/components/ui` folder. +In the root loader, we retrieve the preferred color scheme from the session. We pass it to the `ThemeProvider` from `remix-themes`. Switching the color scheme is achieved by using the `useTheme` hook from `remix-themes` and calling an action on `/action/set-theme`, which will update the session. + +`:root[class~="dark"]` is added to the [app/tailwind.css](./app/tailwind.css?plain=1#L39) to be able to use the `dark` class on the html element to apply the dark mode styles. + +The configuration for the `shadcn/ui` components is stored in `components.json`. New components can be added with: + +```bash +npx shadcn-ui@latest add +``` + +## Related Links + +- [shadcn/ui](https://ui.shadcn.com/) +- [remix-themes](https://github.com/abereghici/remix-themes) +- [Tailwind CSS](https://tailwindcss.com/) diff --git a/shadcn-ui-themes/app/components/mode-toggle.tsx b/shadcn-ui-themes/app/components/mode-toggle.tsx new file mode 100644 index 00000000..a5408f87 --- /dev/null +++ b/shadcn-ui-themes/app/components/mode-toggle.tsx @@ -0,0 +1,34 @@ +import { Moon, Sun } from "lucide-react" +import { Theme, useTheme } from "remix-themes" + +import { Button } from "./ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "./ui/dropdown-menu" + +export function ModeToggle() { + const [, setTheme] = useTheme() + + return ( + + + + + + setTheme(Theme.LIGHT)}> + Light + + setTheme(Theme.DARK)}> + Dark + + + + ) +} diff --git a/shadcn-ui-themes/app/components/ui/button.tsx b/shadcn-ui-themes/app/components/ui/button.tsx new file mode 100644 index 00000000..d754ca03 --- /dev/null +++ b/shadcn-ui-themes/app/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "~/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/shadcn-ui-themes/app/components/ui/dropdown-menu.tsx b/shadcn-ui-themes/app/components/ui/dropdown-menu.tsx new file mode 100644 index 00000000..76c796c8 --- /dev/null +++ b/shadcn-ui-themes/app/components/ui/dropdown-menu.tsx @@ -0,0 +1,198 @@ +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "~/lib/utils" + +const DropdownMenu = DropdownMenuPrimitive.Root + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger + +const DropdownMenuGroup = DropdownMenuPrimitive.Group + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal + +const DropdownMenuSub = DropdownMenuPrimitive.Sub + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +} diff --git a/shadcn-ui-themes/app/lib/utils.ts b/shadcn-ui-themes/app/lib/utils.ts new file mode 100644 index 00000000..ec79801f --- /dev/null +++ b/shadcn-ui-themes/app/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/shadcn-ui-themes/app/root.tsx b/shadcn-ui-themes/app/root.tsx new file mode 100644 index 00000000..1680d092 --- /dev/null +++ b/shadcn-ui-themes/app/root.tsx @@ -0,0 +1,62 @@ +import { cssBundleHref } from "@remix-run/css-bundle"; +import type { LinksFunction, LoaderFunctionArgs } from "@remix-run/node"; +import { + Links, + LiveReload, + Meta, + Outlet, + Scripts, + ScrollRestoration, + useLoaderData, +} from "@remix-run/react"; +import { ThemeProvider, useTheme } from "remix-themes"; +import stylesheet from "~/tailwind.css"; +import { themeSessionResolver } from "./sessions.server"; + +// Return the theme from the session storage using the loader +export async function loader({ request }: LoaderFunctionArgs) { + const { getTheme } = await themeSessionResolver(request) + return { + theme: getTheme(), + } +} + +// Wrap the app with ThemeProvider. +// `specifiedTheme` is the stored theme in the session storage. +// `themeAction` is the action name that's used to change the theme in the session storage. +export default function AppWithProviders() { + const data = useLoaderData() + return ( + + + + ) +} + +export const links: LinksFunction = () => [ + ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), + { rel: "stylesheet", href: stylesheet }, +]; + +function App() { + const [theme] = useTheme() + + return ( + // Conditionally add the `dark` class to the html element. + // See https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually + + + + + + + + + + + + + + + ); +} diff --git a/shadcn-ui-themes/app/routes/_index.tsx b/shadcn-ui-themes/app/routes/_index.tsx new file mode 100644 index 00000000..504195ce --- /dev/null +++ b/shadcn-ui-themes/app/routes/_index.tsx @@ -0,0 +1,11 @@ +import { ModeToggle } from "~/components/mode-toggle"; +import { Button } from "~/components/ui/button"; + +export default function Index() { + return ( +
+ + +
+ ); +} diff --git a/shadcn-ui-themes/app/routes/action.set-theme.ts b/shadcn-ui-themes/app/routes/action.set-theme.ts new file mode 100644 index 00000000..f4b577e2 --- /dev/null +++ b/shadcn-ui-themes/app/routes/action.set-theme.ts @@ -0,0 +1,5 @@ +import { createThemeAction } from "remix-themes" + +import { themeSessionResolver } from "~/sessions.server" + +export const action = createThemeAction(themeSessionResolver) diff --git a/shadcn-ui-themes/app/sessions.server.ts b/shadcn-ui-themes/app/sessions.server.ts new file mode 100644 index 00000000..c86814c0 --- /dev/null +++ b/shadcn-ui-themes/app/sessions.server.ts @@ -0,0 +1,21 @@ +import { createCookieSessionStorage } from "@remix-run/node" +import { createThemeSessionResolver } from "remix-themes" + +// You can default to 'development' if process.env.NODE_ENV is not set +const isProduction = process.env.NODE_ENV === "production" + +const sessionStorage = createCookieSessionStorage({ + cookie: { + name: "theme", + path: "/", + httpOnly: true, + sameSite: "lax", + secrets: ["s3cr3t"], + // Set domain and secure only if in production + ...(isProduction + ? { domain: "your-production-domain.com", secure: true } + : {}), + }, +}) + +export const themeSessionResolver = createThemeSessionResolver(sessionStorage) diff --git a/shadcn-ui-themes/app/tailwind.css b/shadcn-ui-themes/app/tailwind.css new file mode 100644 index 00000000..6c6bce5f --- /dev/null +++ b/shadcn-ui-themes/app/tailwind.css @@ -0,0 +1,77 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 0.5rem; + } + + .dark, + :root[class~="dark"] { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/shadcn-ui-themes/components.json b/shadcn-ui-themes/components.json new file mode 100644 index 00000000..6178b1d0 --- /dev/null +++ b/shadcn-ui-themes/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "app/tailwind.css", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "~/components", + "utils": "~/lib/utils" + } +} \ No newline at end of file diff --git a/shadcn-ui-themes/package.json b/shadcn-ui-themes/package.json new file mode 100644 index 00000000..52031f61 --- /dev/null +++ b/shadcn-ui-themes/package.json @@ -0,0 +1,40 @@ +{ + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "remix build", + "dev": "remix dev", + "start": "remix-serve build", + "typecheck": "tsc" + }, + "dependencies": { + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-slot": "^1.0.2", + "@remix-run/css-bundle": "^2.3.0", + "@remix-run/node": "^2.3.0", + "@remix-run/react": "^2.3.0", + "@remix-run/serve": "^2.3.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "isbot": "^3.6.5", + "lucide-react": "^0.292.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "remix-themes": "^1.2.2", + "tailwind-merge": "^2.0.0", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@remix-run/dev": "^2.3.0", + "@remix-run/eslint-config": "^2.3.0", + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.8", + "eslint": "^8.27.0", + "tailwindcss": "^3.3.5", + "typescript": "^5.2.2" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/shadcn-ui-themes/public/favicon.ico b/shadcn-ui-themes/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8830cf6821b354114848e6354889b8ecf6d2bc61 GIT binary patch literal 16958 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 EKbET!0{{R3 literal 0 HcmV?d00001 diff --git a/shadcn-ui-themes/remix.config.js b/shadcn-ui-themes/remix.config.js new file mode 100644 index 00000000..7fac2d30 --- /dev/null +++ b/shadcn-ui-themes/remix.config.js @@ -0,0 +1,8 @@ +/** @type {import('@remix-run/dev').AppConfig} */ +export default { + ignoredRouteFiles: ["**/.*"], + // appDirectory: "app", + // assetsBuildDirectory: "public/build", + // publicPath: "/build/", + // serverBuildPath: "build/index.js", +}; diff --git a/shadcn-ui-themes/remix.env.d.ts b/shadcn-ui-themes/remix.env.d.ts new file mode 100644 index 00000000..dcf8c45e --- /dev/null +++ b/shadcn-ui-themes/remix.env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/shadcn-ui-themes/sandbox.config.json b/shadcn-ui-themes/sandbox.config.json new file mode 100644 index 00000000..f92e0250 --- /dev/null +++ b/shadcn-ui-themes/sandbox.config.json @@ -0,0 +1,7 @@ +{ + "hardReloadOnChange": true, + "template": "remix", + "container": { + "port": 3000 + } +} diff --git a/shadcn-ui-themes/tailwind.config.ts b/shadcn-ui-themes/tailwind.config.ts new file mode 100644 index 00000000..a92cf5d4 --- /dev/null +++ b/shadcn-ui-themes/tailwind.config.ts @@ -0,0 +1,78 @@ +import type { Config } from 'tailwindcss' + +export default { + darkMode: ["class"], + content: [ + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} satisfies Config + diff --git a/shadcn-ui-themes/tsconfig.json b/shadcn-ui-themes/tsconfig.json new file mode 100644 index 00000000..28cce918 --- /dev/null +++ b/shadcn-ui-themes/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "target": "ES2022", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + + // Remix takes care of building everything in `remix build`. + "noEmit": true + } +} From 2e506950249a2b2706480ae0092a02152ad9c85e Mon Sep 17 00:00:00 2001 From: Tobias Geiselmann <11350542+TobiasGeiselmann@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:18:01 +0100 Subject: [PATCH 2/2] Update file paths and links in README.md --- shadcn-ui-themes/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shadcn-ui-themes/README.md b/shadcn-ui-themes/README.md index 3d29a8ea..88afe799 100644 --- a/shadcn-ui-themes/README.md +++ b/shadcn-ui-themes/README.md @@ -13,12 +13,12 @@ Open this example on [CodeSandbox](https://codesandbox.com): ## Example -This example uses `shadcn/ui` components. They are stored in the `app/components/ui` folder. -In the root loader, we retrieve the preferred color scheme from the session. We pass it to the `ThemeProvider` from `remix-themes`. Switching the color scheme is achieved by using the `useTheme` hook from `remix-themes` and calling an action on `/action/set-theme`, which will update the session. +This example uses `shadcn/ui` components. They are stored in the [app/components/ui](./app/components/ui/) folder. +In the [root loader](./app/root.tsx), we retrieve the preferred color scheme from the session. We pass it to the `ThemeProvider` from `remix-themes`. Switching the color scheme is achieved by using the `useTheme` hook from `remix-themes` and calling an action on [/action/set-theme](./app/routes/action.set-theme.ts), which will update the session. `:root[class~="dark"]` is added to the [app/tailwind.css](./app/tailwind.css?plain=1#L39) to be able to use the `dark` class on the html element to apply the dark mode styles. -The configuration for the `shadcn/ui` components is stored in `components.json`. New components can be added with: +The configuration for the `shadcn/ui` components is stored in [components.json](./components.json). New components can be added with: ```bash npx shadcn-ui@latest add