Prettier & Auto formatter (Bash Command)PageUI + ShadcnUI tailwind configPageUI + ShadcnUI global cssGoogle Analytics (outdated)
Prettier & Auto formatter (Bash Command)
bun install -D prettier prettier-plugin-tailwindcss @ianvs/prettier-plugin-sort-imports mkdir -p .vscode && echo '{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "prettier.configPath": "prettier.config.js", "workbench.editor.customLabels.patterns": { "**/app/**/page.tsx": "${dirname} - page.tsx" "**/app/**/layout.tsx": "${dirname} - layout.tsx" } }' > .vscode/settings.json echo '/** @type {import('prettier').Config} */ // Template from github.com/shadcn-ui/taxonomy/blob/main/prettier.config.js module.exports = { endOfLine: "lf", semi: false, singleQuote: false, tabWidth: 2, trailingComma: "es5", importOrder: [ "^(react/(.*)$)|^(react$)", "^(next/(.*)$)|^(next$)", "<THIRD_PARTY_MODULES>", "", "^types$", "^@/env(.*)$", "^@/types/(.*)$", "^@/config/(.*)$", "^@/lib/(.*)$", "^@/hooks/(.*)$", "^@/components/nextstatic/(.*)$", "^@/components/landing/(.*)$", "^@/components/ui/(.*)$", "^@/components/shared/(.*)$", "^@/components/(.*)$", "^@/styles/(.*)$", "^@/app/(.*)$", "", "^[./]" ], importOrderSeparation: false, importOrderSortSpecifiers: true, importOrderBuiltinModulesToTop: true, importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], importOrderMergeDuplicateImports: true, importOrderCombineTypeAndValueImports: true, plugins: ["@ianvs/prettier-plugin-sort-imports"] }' > prettier.config.js bun prettier --write ./
PageUI + ShadcnUI tailwind config
import type { Config } from "tailwindcss" import { fontFamily } from "tailwindcss/defaultTheme" import { colors } from "./data/config/colors" /** @type {import('tailwindcss').Config} */ const config: Config = { darkMode: ["class"], content: [ "./pages/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}", "src/app/**/*.{js,ts,jsx,tsx,mdx}", "src/components/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { container: { center: true, padding: "2rem", screens: { "2xl": "1400px", }, }, extend: { fontFamily: { sans: ["var(--font-space-default)", ...fontFamily.sans], display: ["var(--font-space-display)", ...fontFamily.sans], }, colors: { primary: { 100: colors.primary.lighter, 200: colors.primary.lighter, 300: colors.primary.light, 400: colors.primary.light, 500: colors.primary.main, 600: colors.primary.main, 700: colors.primary.dark, 800: colors.primary.dark, 900: colors.primary.darker, DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { 100: colors.secondary.lighter, 200: colors.secondary.lighter, 300: colors.secondary.light, 400: colors.secondary.light, 500: colors.secondary.main, 600: colors.secondary.main, 700: colors.secondary.dark, 800: colors.secondary.dark, 900: colors.secondary.darker, DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", // ring: colors.primary.dark, background: "hsl(var(--background))", foreground: "hsl(var(--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))", }, }, screens: { "2xl": "1400px", }, backgroundImage: { "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", }, 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"), require("@tailwindcss/forms"), require("@tailwindcss/typography"), ], } export default config
PageUI + ShadcnUI global css
@tailwind base; @tailwind components; @tailwind utilities; @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%; --radius: 0.5rem; /* pageui https://pageui.shipixen.com/docs/installation/nextjs */ --hard-shadow: 0px 29px 52px 0px rgba(0, 0, 0, 0.4), 22px 25px 16px 0px rgba(0, 0, 0, 0.2); --hard-shadow-left: 0px 29px 52px 0px rgba(0, 0, 0, 0.4), -22px 25px 16px 0px rgba(0, 0, 0, 0.2); --primary-lighter-hex: theme("colors.primary.200"); --secondary-lighter-hex: theme("colors.secondary.200"); --primary-dark-hex: theme("colors.primary.800"); --secondary-dark-hex: theme("colors.secondary.800"); } .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%; } *, ::before, ::after { @apply border-gray-100 dark:border-neutral-800; } * { @apply font-sans; } h1, h2, h3, h4, h5, h6 { @apply font-semibold font-display; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } } @layer utilities { .text-balance { text-wrap: balance; } /** * Perspective (used for images etc.) */ .perspective-none { transform: none; } .perspective-left { box-shadow: var(--hard-shadow); transform: perspective(400em) rotateY(-15deg) rotateX(6deg) skew(-8deg, 4deg) translate3d(-4%, -2%, 0) scale(0.8); } .perspective-right { box-shadow: var(--hard-shadow-left); transform: perspective(400em) rotateY(15deg) rotateX(6deg) skew(8deg, -4deg) translate3d(4%, -2%, 0) scale(0.8); } .perspective-bottom { box-shadow: var(--hard-shadow); transform: translateY(-4%) perspective(400em) rotateX(18deg) scale(0.9); } .perspective-bottom-lg { box-shadow: var(--hard-shadow); transform: perspective(400em) translate3d(0, -6%, 0) rotateX(34deg) scale(0.8); } .perspective-paper { box-shadow: var(--hard-shadow); transform: rotateX(40deg) rotate(40deg) scale(0.8); } .perspective-paper-left { box-shadow: var(--hard-shadow-left); transform: rotateX(40deg) rotate(-40deg) scale(0.8); } /** * Custom shadows */ .hard-shadow { box-shadow: var(--hard-shadow); } .hard-shadow-left { box-shadow: var(--hard-shadow-left); } /** * Container utilities */ .container-narrow { @apply max-w-4xl; } .container-wide { @apply xl:max-w-6xl; } .container-ultrawide { @apply xl:max-w-7xl; } }
Google Analytics (outdated)
//.env NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=xxx //lib/analytics "use client"; import Script from "next/script"; import { env } from "../env.mjs"; const googleAnalyticsId = env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID!; export function AnalyticsScript() { return ( <Script async src={`https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsId}`} onLoad={() => { if (typeof window === "undefined") { return; } (window as any).dataLayer = (window as any).dataLayer || []; function gtag() { (window as any).dataLayer.push(arguments); } // @ts-expect-error gtag is only improted in the browser gtag("js", new Date()); // @ts-expect-error gtag is only improted in the browser gtag("config", googleAnalyticsId); }} /> ); } export function useAnalytics() { const trackEvent = (event: string, data?: Record<string, unknown>) => { if (typeof window === "undefined" || !(window as any).gta) { return; } (window as any).gta("event", event, data); }; return { trackEvent, }; } // src/app/layout.tsx import { AnalyticsScript } from "@/lib/analytics"; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en"> <body className={inter.className}> <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange > {children} </ThemeProvider> </body> <AnalyticsScript /> </html> ); }