add live + draft
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -38,3 +38,8 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# Turborepo
|
||||
.turbo
|
||||
|
||||
.vercel
|
||||
|
20
components.json
Normal file
20
components.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src/styles/globals.scss",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": false,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
}
|
||||
}
|
3
debug.log
Normal file
3
debug.log
Normal file
@@ -0,0 +1,3 @@
|
||||
[1027/133620.512:ERROR:crashpad_client_win.cc(810)] not connected
|
||||
[1027/150423.695:ERROR:crashpad_client_win.cc(810)] not connected
|
||||
[1027/150423.912:ERROR:crashpad_client_win.cc(810)] not connected
|
@@ -3,7 +3,17 @@ import type { NextConfig } from "next";
|
||||
const nextConfig: NextConfig = {
|
||||
sassOptions: {
|
||||
silenceDeprecations: ['legacy-js-api'],
|
||||
}
|
||||
},
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
hostname: "cdn.sanity.io",
|
||||
pathname: "/images/**",
|
||||
protocol: "https",
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
15
package.json
15
package.json
@@ -8,11 +8,24 @@
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"packageManager": "bun@1.1.33",
|
||||
"dependencies": {
|
||||
"@sanity/image-url": "^1.0.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.453.0",
|
||||
"next": "15.0.1",
|
||||
"next-sanity": "^9.8.7",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "19.0.0-rc-69d4b800-20241021",
|
||||
"react-dom": "19.0.0-rc-69d4b800-20241021",
|
||||
"sass": "^1.80.4"
|
||||
"react-error-boundary": "^4.1.2",
|
||||
"sanity": "^3.62.2",
|
||||
"sass": "^1.80.4",
|
||||
"sonner": "^1.5.0",
|
||||
"styled-components": "6",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
|
88
src/app/[slug]/page.tsx
Normal file
88
src/app/[slug]/page.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { defineQuery, PortableText } from "next-sanity";
|
||||
import imageUrlBuilder from "@sanity/image-url";
|
||||
import type { SanityImageSource } from "@sanity/image-url/lib/types/types";
|
||||
import { client, sanityFetch } from "../../sanity/client";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Post, SanityImageAsset } from "@/sanity/sanity.types";
|
||||
import urlBuilder from "@sanity/image-url";
|
||||
import {getImageDimensions} from '@sanity/asset-utils'
|
||||
|
||||
const POST_QUERY = defineQuery(`*[_type == "post" && slug.current == $slug][0]`);
|
||||
const POSTS_QUERY = defineQuery(`*[_type == "post"]{slug}`)
|
||||
|
||||
type PageParams = Promise<{
|
||||
slug: string;
|
||||
}>
|
||||
|
||||
const { projectId, dataset } = client.config();
|
||||
const urlFor = (source: SanityImageSource) =>
|
||||
projectId && dataset
|
||||
? imageUrlBuilder({ projectId, dataset }).image(source)
|
||||
: null;
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const posts: Post[] = (await sanityFetch({ query: POSTS_QUERY, stega: false, perspective: "published" })).data;
|
||||
return posts.map((post) => ({ slug: post.slug?.current ?? null })).filter((post) => post.slug);
|
||||
}
|
||||
|
||||
export default async function PostPage(props: { params: PageParams }) {
|
||||
const { slug } = await props.params;
|
||||
|
||||
const post: Post = (await sanityFetch({ query: POST_QUERY, params: { slug } })).data;
|
||||
|
||||
if (!post) {
|
||||
return (
|
||||
<main>
|
||||
<p>Post not found</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
const postImageUrl = post.mainImage ? urlFor(post.mainImage)?.width(550).height(310).url() : null;
|
||||
|
||||
function PortableImage({ value, isInline }: { value: SanityImageAsset; isInline: boolean }) {
|
||||
const {width, height} = getImageDimensions(value)
|
||||
return <Image
|
||||
src={urlBuilder()
|
||||
.image(value)
|
||||
.width(isInline ? 100 : 600)
|
||||
.fit('max')
|
||||
.auto('format')
|
||||
.withOptions({dataset, projectId})
|
||||
.url()}
|
||||
width={isInline ? 100 : 600}
|
||||
height={height}
|
||||
alt={value.altText || ' '}
|
||||
loading="lazy"
|
||||
style={{
|
||||
display: isInline ? 'inline-block' : 'block',
|
||||
aspectRatio: width / height,
|
||||
borderRadius: ".6rem",
|
||||
border: "1px solid rgba(255, 255, 255, .15)",
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="container mx-auto min-h-screen max-w-3xl p-8 flex flex-col gap-4">
|
||||
<Link href="/" className="hover:underline">
|
||||
← Back to posts
|
||||
</Link>
|
||||
{postImageUrl && (
|
||||
<Image
|
||||
src={postImageUrl}
|
||||
alt={`Banner for ${post.title}` }
|
||||
className="aspect-video rounded-xl"
|
||||
width="550"
|
||||
height="310"
|
||||
/>
|
||||
)}
|
||||
<h1 className="text-4xl font-bold mb-8">{post.title}</h1>
|
||||
<div className="prose">
|
||||
<p>Published: {new Date(post.publishedAt ?? "").toISOString().substring(0, 10)}</p>
|
||||
{Array.isArray(post.body) && <PortableText value={post.body} components={ { types: { image: PortableImage } } } />}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
6
src/app/api/draft-mode/enable/route.ts
Normal file
6
src/app/api/draft-mode/enable/route.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { client } from '@/sanity/client';
|
||||
import { defineEnableDraftMode } from 'next-sanity/draft-mode';
|
||||
|
||||
export const { GET } = defineEnableDraftMode({
|
||||
client: client.withConfig({ token: process.env.SANITY_API_READ_TOKEN, stega: { studioUrl: 'https://vaporvee.sanity.studio', enabled: true } }),
|
||||
})
|
@@ -1,6 +1,12 @@
|
||||
import localFont from "next/font/local";
|
||||
import "../styles/globals.scss";
|
||||
|
||||
import { draftMode } from "next/headers";
|
||||
import { VisualEditing } from 'next-sanity'
|
||||
import { SanityLive } from '@/sanity/client'
|
||||
import { LiveErrorBoundary } from "./live-error-boundary";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
|
||||
const geistSans = localFont({
|
||||
src: "../fonts/GeistVF.woff",
|
||||
variable: "--font-geist-sans",
|
||||
@@ -12,17 +18,24 @@ const geistMono = localFont({
|
||||
weight: "100 900",
|
||||
});
|
||||
|
||||
export default function RootLayout({
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { isEnabled } = await draftMode();
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<LiveErrorBoundary>
|
||||
<SanityLive />
|
||||
</LiveErrorBoundary>
|
||||
{isEnabled && <VisualEditing />}
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
);
|
24
src/app/live-error-boundary.tsx
Normal file
24
src/app/live-error-boundary.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import {
|
||||
ErrorBoundary as ReactErrorBoundary,
|
||||
type FallbackProps,
|
||||
} from "react-error-boundary";
|
||||
|
||||
export function LiveErrorBoundary({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<ReactErrorBoundary FallbackComponent={Fallback}>
|
||||
{children}
|
||||
</ReactErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
function Fallback({ error }: FallbackProps) {
|
||||
useEffect(() => {
|
||||
const msg = "Couldn't connect to Live Content API";
|
||||
console.error(`${msg}: `, error);
|
||||
}, [error]);
|
||||
|
||||
return null;
|
||||
}
|
31
src/app/page.tsx
Normal file
31
src/app/page.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from "next/link";
|
||||
import { type SanityDocument } from "next-sanity";
|
||||
|
||||
import { client } from "@/sanity/client";
|
||||
|
||||
const POSTS_QUERY = `*[
|
||||
_type == "post"
|
||||
&& defined(slug.current)
|
||||
]|order(publishedAt desc)[0...12]{_id, title, slug, publishedAt}`;
|
||||
|
||||
const options = { next: { revalidate: 30 } };
|
||||
|
||||
export default async function IndexPage() {
|
||||
const posts = await client.fetch<SanityDocument[]>(POSTS_QUERY, {}, options);
|
||||
|
||||
return (
|
||||
<main className="container mx-auto min-h-screen max-w-3xl p-8">
|
||||
<h1 className="text-4xl font-bold mb-8">Posts</h1>
|
||||
<ul className="flex flex-col gap-y-4">
|
||||
{posts.map((post) => (
|
||||
<li className="hover:underline" key={post._id}>
|
||||
<Link href={`/${post.slug.current}`}>
|
||||
<h2 className="text-xl font-semibold">{post.title}</h2>
|
||||
<p>{new Date(post.publishedAt).toLocaleDateString()}</p>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</main>
|
||||
);
|
||||
}
|
31
src/components/ui/sonner.tsx
Normal file
31
src/components/ui/sonner.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
"use client"
|
||||
|
||||
import { useTheme } from "next-themes"
|
||||
import { Toaster as Sonner } from "sonner"
|
||||
|
||||
type ToasterProps = React.ComponentProps<typeof Sonner>
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme()
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
className="toaster group"
|
||||
toastOptions={{
|
||||
classNames: {
|
||||
toast:
|
||||
"group toast group-[.toaster]:bg-white group-[.toaster]:text-neutral-950 group-[.toaster]:border-neutral-200 group-[.toaster]:shadow-lg dark:group-[.toaster]:bg-neutral-950 dark:group-[.toaster]:text-neutral-50 dark:group-[.toaster]:border-neutral-800",
|
||||
description: "group-[.toast]:text-neutral-500 dark:group-[.toast]:text-neutral-400",
|
||||
actionButton:
|
||||
"group-[.toast]:bg-neutral-900 group-[.toast]:text-neutral-50 dark:group-[.toast]:bg-neutral-50 dark:group-[.toast]:text-neutral-900",
|
||||
cancelButton:
|
||||
"group-[.toast]:bg-neutral-100 group-[.toast]:text-neutral-500 dark:group-[.toast]:bg-neutral-800 dark:group-[.toast]:text-neutral-400",
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Toaster }
|
6
src/lib/utils.ts
Normal file
6
src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
import '../layouts/default'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>Blog</div>
|
||||
);
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
import '../layouts/default'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>Home</div>
|
||||
);
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
import '../layouts/default'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>Projects</div>
|
||||
);
|
||||
}
|
20
src/sanity/client.ts
Normal file
20
src/sanity/client.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createClient, defineLive } from "next-sanity";
|
||||
|
||||
export const client = createClient({
|
||||
projectId: "zk5oebdb",
|
||||
dataset: "production",
|
||||
apiVersion: "2024-10-27",
|
||||
useCdn: false,
|
||||
stega: { studioUrl: 'https://vaporvee.sanity.studio' },
|
||||
});
|
||||
|
||||
const token = process.env.SANITY_API_READ_TOKEN
|
||||
if (!token) {
|
||||
throw new Error('Missing SANITY_API_READ_TOKEN')
|
||||
}
|
||||
|
||||
export const { sanityFetch, SanityLive } = defineLive({
|
||||
client,
|
||||
serverToken: token,
|
||||
browserToken: token,
|
||||
});
|
278
src/sanity/sanity.types.ts
Normal file
278
src/sanity/sanity.types.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* ---------------------------------------------------------------------------------
|
||||
* This file has been generated by Sanity TypeGen.
|
||||
* Command: `sanity typegen generate`
|
||||
*
|
||||
* Any modifications made directly to this file will be overwritten the next time
|
||||
* the TypeScript definitions are generated. Please make changes to the Sanity
|
||||
* schema definitions and/or GROQ queries if you need to update these types.
|
||||
*
|
||||
* For more information on how to use Sanity TypeGen, visit the official documentation:
|
||||
* https://www.sanity.io/docs/sanity-typegen
|
||||
* ---------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// Source: schema.json
|
||||
export type SanityImagePaletteSwatch = {
|
||||
_type: 'sanity.imagePaletteSwatch'
|
||||
background?: string
|
||||
foreground?: string
|
||||
population?: number
|
||||
title?: string
|
||||
}
|
||||
|
||||
export type SanityImagePalette = {
|
||||
_type: 'sanity.imagePalette'
|
||||
darkMuted?: SanityImagePaletteSwatch
|
||||
lightVibrant?: SanityImagePaletteSwatch
|
||||
darkVibrant?: SanityImagePaletteSwatch
|
||||
vibrant?: SanityImagePaletteSwatch
|
||||
dominant?: SanityImagePaletteSwatch
|
||||
lightMuted?: SanityImagePaletteSwatch
|
||||
muted?: SanityImagePaletteSwatch
|
||||
}
|
||||
|
||||
export type SanityImageDimensions = {
|
||||
_type: 'sanity.imageDimensions'
|
||||
height?: number
|
||||
width?: number
|
||||
aspectRatio?: number
|
||||
}
|
||||
|
||||
export type SanityFileAsset = {
|
||||
_id: string
|
||||
_type: 'sanity.fileAsset'
|
||||
_createdAt: string
|
||||
_updatedAt: string
|
||||
_rev: string
|
||||
originalFilename?: string
|
||||
label?: string
|
||||
title?: string
|
||||
description?: string
|
||||
altText?: string
|
||||
sha1hash?: string
|
||||
extension?: string
|
||||
mimeType?: string
|
||||
size?: number
|
||||
assetId?: string
|
||||
uploadId?: string
|
||||
path?: string
|
||||
url?: string
|
||||
source?: SanityAssetSourceData
|
||||
}
|
||||
|
||||
export type Geopoint = {
|
||||
_type: 'geopoint'
|
||||
lat?: number
|
||||
lng?: number
|
||||
alt?: number
|
||||
}
|
||||
|
||||
export type BlockContent = Array<
|
||||
| {
|
||||
children?: Array<{
|
||||
marks?: Array<string>
|
||||
text?: string
|
||||
_type: 'span'
|
||||
_key: string
|
||||
}>
|
||||
style?: 'normal' | 'h1' | 'h2' | 'h3' | 'h4' | 'blockquote'
|
||||
listItem?: 'bullet'
|
||||
markDefs?: Array<{
|
||||
href?: string
|
||||
_type: 'link'
|
||||
_key: string
|
||||
}>
|
||||
level?: number
|
||||
_type: 'block'
|
||||
_key: string
|
||||
}
|
||||
| {
|
||||
asset?: {
|
||||
_ref: string
|
||||
_type: 'reference'
|
||||
_weak?: boolean
|
||||
[internalGroqTypeReferenceTo]?: 'sanity.imageAsset'
|
||||
}
|
||||
hotspot?: SanityImageHotspot
|
||||
crop?: SanityImageCrop
|
||||
_type: 'image'
|
||||
_key: string
|
||||
}
|
||||
>
|
||||
|
||||
export type Category = {
|
||||
_id: string
|
||||
_type: 'category'
|
||||
_createdAt: string
|
||||
_updatedAt: string
|
||||
_rev: string
|
||||
title?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export type Post = {
|
||||
_id: string
|
||||
_type: 'post'
|
||||
_createdAt: string
|
||||
_updatedAt: string
|
||||
_rev: string
|
||||
title?: string
|
||||
slug?: Slug
|
||||
author?: {
|
||||
_ref: string
|
||||
_type: 'reference'
|
||||
_weak?: boolean
|
||||
[internalGroqTypeReferenceTo]?: 'author'
|
||||
}
|
||||
mainImage?: {
|
||||
asset?: {
|
||||
_ref: string
|
||||
_type: 'reference'
|
||||
_weak?: boolean
|
||||
[internalGroqTypeReferenceTo]?: 'sanity.imageAsset'
|
||||
}
|
||||
hotspot?: SanityImageHotspot
|
||||
crop?: SanityImageCrop
|
||||
_type: 'image'
|
||||
}
|
||||
categories?: Array<{
|
||||
_ref: string
|
||||
_type: 'reference'
|
||||
_weak?: boolean
|
||||
_key: string
|
||||
[internalGroqTypeReferenceTo]?: 'category'
|
||||
}>
|
||||
publishedAt?: string
|
||||
myCodeField?: Code
|
||||
body?: BlockContent
|
||||
}
|
||||
|
||||
export type Author = {
|
||||
_id: string
|
||||
_type: 'author'
|
||||
_createdAt: string
|
||||
_updatedAt: string
|
||||
_rev: string
|
||||
name?: string
|
||||
slug?: Slug
|
||||
image?: {
|
||||
asset?: {
|
||||
_ref: string
|
||||
_type: 'reference'
|
||||
_weak?: boolean
|
||||
[internalGroqTypeReferenceTo]?: 'sanity.imageAsset'
|
||||
}
|
||||
hotspot?: SanityImageHotspot
|
||||
crop?: SanityImageCrop
|
||||
_type: 'image'
|
||||
}
|
||||
bio?: Array<{
|
||||
children?: Array<{
|
||||
marks?: Array<string>
|
||||
text?: string
|
||||
_type: 'span'
|
||||
_key: string
|
||||
}>
|
||||
style?: 'normal'
|
||||
listItem?: never
|
||||
markDefs?: Array<{
|
||||
href?: string
|
||||
_type: 'link'
|
||||
_key: string
|
||||
}>
|
||||
level?: number
|
||||
_type: 'block'
|
||||
_key: string
|
||||
}>
|
||||
}
|
||||
|
||||
export type SanityImageCrop = {
|
||||
_type: 'sanity.imageCrop'
|
||||
top?: number
|
||||
bottom?: number
|
||||
left?: number
|
||||
right?: number
|
||||
}
|
||||
|
||||
export type SanityImageHotspot = {
|
||||
_type: 'sanity.imageHotspot'
|
||||
x?: number
|
||||
y?: number
|
||||
height?: number
|
||||
width?: number
|
||||
}
|
||||
|
||||
export type SanityImageAsset = {
|
||||
_id: string
|
||||
_type: 'sanity.imageAsset'
|
||||
_createdAt: string
|
||||
_updatedAt: string
|
||||
_rev: string
|
||||
originalFilename?: string
|
||||
label?: string
|
||||
title?: string
|
||||
description?: string
|
||||
altText?: string
|
||||
sha1hash?: string
|
||||
extension?: string
|
||||
mimeType?: string
|
||||
size?: number
|
||||
assetId?: string
|
||||
uploadId?: string
|
||||
path?: string
|
||||
url?: string
|
||||
metadata?: SanityImageMetadata
|
||||
source?: SanityAssetSourceData
|
||||
}
|
||||
|
||||
export type SanityAssetSourceData = {
|
||||
_type: 'sanity.assetSourceData'
|
||||
name?: string
|
||||
id?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
export type SanityImageMetadata = {
|
||||
_type: 'sanity.imageMetadata'
|
||||
location?: Geopoint
|
||||
dimensions?: SanityImageDimensions
|
||||
palette?: SanityImagePalette
|
||||
lqip?: string
|
||||
blurHash?: string
|
||||
hasAlpha?: boolean
|
||||
isOpaque?: boolean
|
||||
}
|
||||
|
||||
export type Slug = {
|
||||
_type: 'slug'
|
||||
current?: string
|
||||
source?: string
|
||||
}
|
||||
|
||||
export type Code = {
|
||||
_type: 'code'
|
||||
language?: string
|
||||
filename?: string
|
||||
code?: string
|
||||
highlightedLines?: Array<number>
|
||||
}
|
||||
|
||||
export type AllSanitySchemaTypes =
|
||||
| SanityImagePaletteSwatch
|
||||
| SanityImagePalette
|
||||
| SanityImageDimensions
|
||||
| SanityFileAsset
|
||||
| Geopoint
|
||||
| BlockContent
|
||||
| Category
|
||||
| Post
|
||||
| Author
|
||||
| SanityImageCrop
|
||||
| SanityImageHotspot
|
||||
| SanityImageAsset
|
||||
| SanityAssetSourceData
|
||||
| SanityImageMetadata
|
||||
| Slug
|
||||
| Code
|
||||
export declare const internalGroqTypeReferenceTo: unique symbol
|
@@ -4,6 +4,7 @@
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--radius: 0.75rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
@@ -1,19 +1,25 @@
|
||||
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: 'var(--background)',
|
||||
foreground: 'var(--foreground)'
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
};
|
||||
export default config;
|
||||
|
Reference in New Issue
Block a user