feat: update schema, add ProjectCard, readme

This commit is contained in:
enscribe 2024-09-13 15:18:47 -07:00
parent fbeab5a744
commit b93eddea6b
No known key found for this signature in database
GPG key ID: 9BBD5C4114E25322
24 changed files with 373 additions and 72 deletions

214
README.md
View file

@ -4,12 +4,15 @@
## astro-erudite ## astro-erudite
![Stargazers]
[![License]](LICENSE)
</div>
astro-erudite is an opinionated, no-frills static blogging template built with [Astro](https://astro.build/), [Tailwind](https://tailwindcss.com/), and [shadcn/ui](https://ui.shadcn.com/). Extraordinarily loosely based off the [Astro Micro](https://astro-micro.vercel.app/) theme by [trevortylerlee](https://github.com/trevortylerlee). astro-erudite is an opinionated, no-frills static blogging template built with [Astro](https://astro.build/), [Tailwind](https://tailwindcss.com/), and [shadcn/ui](https://ui.shadcn.com/). Extraordinarily loosely based off the [Astro Micro](https://astro-micro.vercel.app/) theme by [trevortylerlee](https://github.com/trevortylerlee).
To learn more about why this template exists, read [The State of Static Blogs in 2024](https://astro-erudite.vercel.app/blog/the-state-of-static-blogs), where I share my take on what constitutes a great blogging template and my goals while developing this one. To learn more about why this template exists, read [The State of Static Blogs in 2024](https://astro-erudite.vercel.app/blog/the-state-of-static-blogs), where I share my take on what constitutes a great blogging template and my goals while developing this one.
</div>
--- ---
## Technology Stack ## Technology Stack
@ -27,3 +30,210 @@ This is a list of the various technologies used to build this website:
| Deployment | [Vercel](https://vercel.com) | | Deployment | [Vercel](https://vercel.com) |
--- ---
## Features
- [Astro](https://astro.build/)&rsquo;s [Islands](https://docs.astro.build/en/concepts/islands/) architecture for partial/selective hydration and client-side interactivity while maintaining a fast-to-render static site.
- [shadcn/ui](https://ui.shadcn.com/)&rsquo;s [Tailwind](https://tailwindcss.com/) color convention for automatic styling across both light and dark themes. Includes accessible, theme-aware UI components for navigation, buttons, etc.
- [rehype-pretty-code](https://rehype-pretty.pages.dev/) with [Shiki](https://github.com/shikijs/shiki) for advanced code block styling, highlighting, and code block titles/captions.
- Blog post authoring using [MDX](https://mdxjs.com/) for component-style content, alongside $\LaTeX$ rendering using [KaTeX](https://katex.org/).
- Astro [View Transitions](https://docs.astro.build/en/guides/view-transitions/) in <abbr title="Single Page Application">SPA</abbr> mode for smooth, opt-in animations during route switching.
- SEO optimization with fine-grained control over metadata and [Open Graph](https://ogp.me/) tags for each post.
- [RSS](https://en.wikipedia.org/wiki/RSS) feeds and sitemap generation!
- Supports author profiles (with a dedicated authors page) and adding multiple authors per post.
- Supports project tags (with a dedicated tags page) for easy post categorization and discovery.
## Getting Started
1. Hit &ldquo;Use this template&rdquo; to create a new repository in your own GitHub account with this template.
2. Clone the repository:
```bash
git clone https://github.com/[YOUR_USERNAME]/[YOUR_REPO_NAME].git
cd [YOUR_REPO_NAME]
```
3. Install dependencies:
```bash
npm install
```
4. Start the development server:
```bash
npm run dev
```
5. Open your browser and visit `http://localhost:1234` to see your blog in action. The following commands are also available:
| Command | Description |
| ------------------ | --------------------------------------------------------------- |
| `npm run start` | Alias for `npm run dev` |
| `npm run build` | Run type checking and build the project |
| `npm run preview` | Previews the built project |
| `npm run astro` | Run Astro CLI commands |
| `npm run prettier` | Blanket format all files using [Prettier](https://prettier.io/) |
## Customization
### Site Configuration
Edit the `src/consts.ts` file to update your site's metadata, navigation links, and social links:
```typescript
export const SITE: Site = {
TITLE: 'astro-erudite',
DESCRIPTION:
'astro-erudite is a opinionated, no-frills blogging template—built with Astro, Tailwind, and shadcn/ui.',
EMAIL: 'jason@enscribe.dev',
NUM_POSTS_ON_HOMEPAGE: 2,
SITEURL: 'https://astro-erudite.vercel.app',
}
export const NAV_LINKS: Link[] = [
{ href: '/blog', label: 'blog' },
{ href: '/authors', label: 'authors' },
{ href: '/about', label: 'about' },
{ href: '/tags', label: 'tags' },
]
export const SOCIAL_LINKS: Link[] = [
{ href: 'https://github.com/jktrn', label: 'GitHub' },
{ href: 'https://twitter.com/enscry', label: 'Twitter' },
{ href: 'jason@enscribe.dev', label: 'Email' },
{ href: '/rss.xml', label: 'RSS' },
]
```
### Color Palette
Colors are defined in `src/styles/global.css` in [<abbr title="Hue, Saturation, Lightness">HSL</abbr> format](https://en.wikipedia.org/wiki/HSL_and_HSV), using the [shadcn/ui](https://ui.shadcn.com/) convention:
```css
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 80.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 80.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 80.1%;
--accent-foreground: 0 0% 9%;
--additive: 112 50% 36%; /* Unique to astro-erudite */
--additive-foreground: 0 0% 98%; /* Unique to astro-erudite */
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--ring: 0 0% 3.9%;
}
.dark {
/* ... */
}
/* ... */
}
```
## Adding Content
### Blog Posts
Add new blog posts as MDX files in the `src/content/blog/` directory. Use the following frontmatter structure:
```yml
---
title: 'Your Post Title'
description: 'A brief description of your post!'
date: 2024-01-01
tags: ['tag1', 'tag2']
image: './image.png'
authors: ['author1', 'author2']
draft: false
---
```
The blog post schema is defined as follows:
| Field | Type (Zod) | Requirements | Required |
| ------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `title` | `string` | Must be ≤60 characters. | Yes |
| `description` | `string` | Must be ≤155 characters. | Yes |
| `date` | `coerce.date()` | Must be in `YYYY-MM-DD` format. | Yes |
| `image` | `image()` | Must be exactly 1200px &times; 630px. | Optional |
| `tags` | `string[]` | Preferably use kebab-case for these. | Optional |
| `authors` | `string[]` | If the author has a profile, use the slug associated with their Markdown file in `src/content/authors/` (e.g. if their file is named `jane-doe.md`, use `jane-doe` in the array). | Optional |
| `draft` | `boolean` | Defaults to `false` if not provided. | Optional |
### Authors
Add author information in `src/content/authors/` as Markdown files:
```yml
---
name: 'enscribe'
pronouns: 'he/him'
avatar: 'https://gravatar.com/avatar/9bfdc4ec972793cf05cb91efce5f4aaaec2a0da1bf4ec34dad0913f1d845faf6.webp?size=256'
bio: 'd(-_-)b'
website: 'https://enscribe.dev'
twitter: 'https://twitter.com/enscry'
github: 'https://github.com/jktrn'
mail: 'jason@enscribe.dev'
---
```
The author schema is defined as follows:
| Field | Type (Zod) | Requirements | Required |
| ---------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `name` | `string` | n/a | Yes |
| `pronouns` | `string` | n/a | Optional |
| `avatar` | `string.url()` | Must be a valid URL. Preferably use [Gravatar](https://en.gravatar.com/site/implement/images/) with the `?size=256` size parameter. | Yes |
| `bio` | `string` | n/a | Optional |
| `mail` | `string.email()` | Must be a valid email address. | Optional |
| `website` | `string.url()` | Must be a valid URL. | Optional |
| `twitter` | `string.url()` | Must be a valid URL. | Optional |
| `github` | `string.url()` | Must be a valid URL. | Optional |
| `linkedin` | `string.url()` | Must be a valid URL. | Optional |
| `discord` | `string.url()` | Must be a valid URL. | Optional |
You can add as many social media links as you want, as long as you adjust the schema! Make sure you also support the new field in the `src/components/SocialIcons.astro` component.
### Projects
Add projects in `src/content/projects/` as Markdown files:
```yml
---
name: 'Project A'
description: 'This is an example project description! You should replace this with a description of your own project.'
tags: ['Framework A', 'Library B', 'Tool C', 'Resource D']
image: '/static/1200x630.png'
link: 'https://example.com'
---
```
The project schema is defined as follows:
| Field | Type (Zod) | Requirements | Required |
| ------------- | -------------- | ------------------------------------- | -------- |
| `name` | `string` | n/a | Yes |
| `description` | `string` | n/a | Yes |
| `tags` | `string[]` | n/a | Yes |
| `image` | `image()` | Must be exactly 1200px &times; 630px. | Yes |
| `link` | `string.url()` | Must be a valid URL. | Yes |
## License
This project is open source and available under the [MIT License](LICENSE).
---
Built with &hearts; by [enscribe](https://enscribe.dev)!
[Stargazers]: https://img.shields.io/github/stars/jktrn/astro-erudite?color=fafafa&logo=github&logoColor=fff&style=for-the-badge
[License]: https://img.shields.io/github/license/jktrn/astro-erudite?color=0a0a0a&logo=github&logoColor=fff&style=for-the-badge

View file

@ -0,0 +1,33 @@
---
import { Image } from 'astro:assets'
import { Badge } from '@/components/ui/badge'
import Link from '@components/Link.astro'
import type { CollectionEntry } from 'astro:content'
type Props = {
project: CollectionEntry<'projects'>
}
const { project } = Astro.props
---
<div class="overflow-hidden rounded-xl border transition-colors duration-300 ease-in-out hover:bg-secondary/50">
<Link href={project.data.link} class="block">
<Image
src={project.data.image}
alt={project.data.name}
width={400}
height={200}
class="w-full object-cover"
/>
<div class="p-4">
<h3 class="mb-2 text-lg font-semibold">{project.data.name}</h3>
<p class="mb-4 text-sm text-muted-foreground">{project.data.description}</p>
<div class="flex flex-wrap gap-2">
{project.data.tags.map((tag) => (
<Badge variant="secondary" showHash={false}>{tag}</Badge>
))}
</div>
</div>
</Link>
</div>

View file

@ -25,12 +25,14 @@ const badgeVariants = cva(
export interface BadgeProps export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>, extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {} VariantProps<typeof badgeVariants> {
showHash?: boolean
}
function Badge({ className, variant, ...props }: BadgeProps) { function Badge({ className, variant, showHash = true, ...props }: BadgeProps) {
return ( return (
<div className={cn(badgeVariants({ variant }), className)} {...props}> <div className={cn(badgeVariants({ variant }), className)} {...props}>
<Hash className="size-3 -translate-x-0.5" /> {showHash && <Hash className="size-3 -translate-x-0.5" />}
{props.children} {props.children}
</div> </div>
) )

View file

@ -7,7 +7,7 @@ const Card = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn('bg-card text-card-foreground rounded-xl border', className)} className={cn('bg-background rounded-xl border', className)}
{...props} {...props}
/> />
)) ))

View file

@ -23,7 +23,16 @@ export function ModeToggle() {
theme === 'dark' || theme === 'dark' ||
(theme === 'system' && (theme === 'system' &&
window.matchMedia('(prefers-color-scheme: dark)').matches) window.matchMedia('(prefers-color-scheme: dark)').matches)
document.documentElement.classList.add('disable-transitions')
document.documentElement.classList[isDark ? 'add' : 'remove']('dark') document.documentElement.classList[isDark ? 'add' : 'remove']('dark')
window.getComputedStyle(document.documentElement).getPropertyValue('opacity')
requestAnimationFrame(() => {
document.documentElement.classList.remove('disable-transitions')
})
}, [theme]) }, [theme])
return ( return (

View file

@ -24,6 +24,7 @@ export const NAV_LINKS: Link[] = [
{ href: '/blog', label: 'blog' }, { href: '/blog', label: 'blog' },
{ href: '/authors', label: 'authors' }, { href: '/authors', label: 'authors' },
{ href: '/about', label: 'about' }, { href: '/about', label: 'about' },
{ href: '/tags', label: 'tags' },
] ]
export const SOCIAL_LINKS: Link[] = [ export const SOCIAL_LINKS: Link[] = [

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View file

@ -1,9 +1,9 @@
--- ---
title: '2022 Post' title: '2022 Post'
description: 'This a dummy post written in the year 2022.' description: 'This a dummy post written in the year 2022.'
date: '2022-01-01' date: 2022-01-01
tags: ['dummy', 'placeholder'] tags: ['dummy', 'placeholder']
image: '/static/1200x630.png' image: './2022.png'
--- ---
This is a dummy post written in the year 2022. This is a dummy post written in the year 2022.

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View file

@ -1,9 +1,9 @@
--- ---
title: '2023 Post' title: '2023 Post'
description: 'This a dummy post written in the year 2023.' description: 'This a dummy post written in the year 2023.'
date: '2023-01-01' date: 2023-01-01
tags: ['dummy', 'placeholder'] tags: ['dummy', 'placeholder']
image: '/static/1200x630.png' image: './2023.png'
authors: ['enscribe'] authors: ['enscribe']
--- ---

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View file

@ -1,9 +1,9 @@
--- ---
title: '2024 Post' title: '2024 Post'
description: 'This a dummy post written in the year 2024 (with multiple authors).' description: 'This a dummy post written in the year 2024 (with multiple authors).'
date: '2024-01-01' date: 2024-01-01
tags: ['dummy', 'placeholder'] tags: ['dummy', 'placeholder']
image: '/static/1200x630.png' image: './2024.png'
authors: ['enscribe', 'jktrn'] authors: ['enscribe', 'jktrn']
--- ---

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View file

@ -1,9 +1,9 @@
--- ---
title: 'The State of Static Blogs in 2024' title: 'The State of Static Blogs in 2024'
description: 'There should not be a single reason why you would need a command palette search bar to find a blog post on your own site.' description: 'There should not be a single reason why you would need a command palette search bar to find a blog post on your own site.'
date: '2024-07-25' date: 2024-07-25
tags: ['webdev', 'opinion'] tags: ['webdev', 'opinion']
image: '/static/1200x630.png' image: './1200x630.png'
authors: ['enscribe'] authors: ['enscribe']
--- ---

View file

@ -2,14 +2,30 @@ import { defineCollection, z } from 'astro:content'
const blog = defineCollection({ const blog = defineCollection({
type: 'content', type: 'content',
schema: z.object({ schema: ({ image }) =>
title: z.string(), z.object({
description: z.string(), title: z
.string()
.max(
60,
'Title should be 60 characters or less for optimal Open Graph display.',
),
description: z
.string()
.max(
155,
'Description should be 155 characters or less for optimal Open Graph display.',
),
date: z.coerce.date(), date: z.coerce.date(),
draft: z.boolean().optional(), image: image()
image: z.string().optional(), .refine((img) => img.width === 1200 && img.height === 630, {
message:
'The image must be exactly 1200px × 630px for Open Graph requirements.',
})
.optional(),
tags: z.array(z.string()).optional(), tags: z.array(z.string()).optional(),
authors: z.array(z.string()).optional(), authors: z.array(z.string()).optional(),
draft: z.boolean().optional(),
}), }),
}) })
@ -20,13 +36,28 @@ const authors = defineCollection({
pronouns: z.string().optional(), pronouns: z.string().optional(),
avatar: z.string().url(), avatar: z.string().url(),
bio: z.string().optional(), bio: z.string().optional(),
website: z.string().url().optional(),
twitter: z.string().optional(),
github: z.string().optional(),
linkedin: z.string().optional(),
mail: z.string().email().optional(), mail: z.string().email().optional(),
discord: z.string().optional(), website: z.string().url().optional(),
twitter: z.string().url().optional(),
github: z.string().url().optional(),
linkedin: z.string().url().optional(),
discord: z.string().url().optional(),
}), }),
}) })
export const collections = { blog, authors } const projects = defineCollection({
type: 'content',
schema: ({ image }) =>
z.object({
name: z.string(),
description: z.string(),
tags: z.array(z.string()),
image: image().refine((img) => img.width === 1200 && img.height === 630, {
message:
'The image must be exactly 1200px × 630px for Open Graph requirements.',
}),
link: z.string().url(),
}),
})
export const collections = { blog, authors, projects }

View file

@ -0,0 +1,7 @@
---
name: "Project A"
description: "This is an example project description! You should replace this with a description of your own project."
tags: ["Framework A", "Library B", "Tool C", "Resource D"]
image: "../../../public/static/1200x630.png"
link: "https://example.com"
---

View file

@ -0,0 +1,7 @@
---
name: "Project B"
description: "This is an example project description! You should replace this with a description of your own project."
tags: ["Framework A", "Library B", "Tool C", "Resource D"]
image: "../../../public/static/1200x630.png"
link: "https://example.com"
---

View file

@ -0,0 +1,7 @@
---
name: "Project C"
description: "This is an example project description! You should replace this with a description of your own project."
tags: ["Framework A", "Library B", "Tool C", "Resource D"]
image: "../../../public/static/1200x630.png"
link: "https://example.com"
---

View file

@ -1,8 +1,12 @@
--- ---
import Breadcrumbs from '@/components/Breadcrumbs.astro' import Breadcrumbs from '@/components/Breadcrumbs.astro'
import Container from '@components/Container.astro' import Container from '@components/Container.astro'
import ProjectCard from '@components/ProjectCard.astro'
import { SITE } from '@consts' import { SITE } from '@consts'
import Layout from '@layouts/Layout.astro' import Layout from '@layouts/Layout.astro'
import { getCollection } from 'astro:content'
const projects = await getCollection('projects')
--- ---
<Layout title="About" description={SITE.DESCRIPTION}> <Layout title="About" description={SITE.DESCRIPTION}>
@ -12,10 +16,17 @@ import Layout from '@layouts/Layout.astro'
<section> <section>
<div class="min-w-full"> <div class="min-w-full">
<h1 class="mb-4 text-3xl font-bold">Some more about us</h1> <h1 class="mb-4 text-3xl font-bold">Some more about us</h1>
<p class="prose prose-neutral dark:prose-invert"> <p class="prose prose-neutral dark:prose-invert mb-8">
{SITE.TITLE} is an opinionated, no-frills static blogging template built {SITE.TITLE} is an opinionated, no-frills static blogging template built
with Astro. with Astro.
</p> </p>
<h2 class="mb-4 text-2xl font-semibold">Our Projects</h2>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{projects.map((project) => (
<ProjectCard project={project} />
))}
</div>
</div> </div>
</section> </section>
</Container> </Container>

View file

@ -11,6 +11,7 @@ const authors = await getCollection('authors')
<Layout title="Authors" description="A list of authors on this site."> <Layout title="Authors" description="A list of authors on this site.">
<Container class="flex flex-col gap-y-6"> <Container class="flex flex-col gap-y-6">
<Breadcrumbs items={[{ label: 'Authors' }]} /> <Breadcrumbs items={[{ label: 'Authors' }]} />
{authors.length > 0 ? (
<ul class="not-prose flex flex-col gap-4"> <ul class="not-prose flex flex-col gap-4">
{ {
authors.map((author) => ( authors.map((author) => (
@ -20,5 +21,8 @@ const authors = await getCollection('authors')
)) ))
} }
</ul> </ul>
) : (
<p class="text-center text-muted-foreground">No authors found.</p>
)}
</Container> </Container>
</Layout> </Layout>

View file

@ -57,7 +57,7 @@ const authors = await parseAuthors(post.data.authors ?? [])
<Layout <Layout
title={post.data.title} title={post.data.title}
description={post.data.description} description={post.data.description}
image={post.data.image ?? '/static/1200x630.png'} image={post.data.image?.src ?? '/static/1200x630.png'}
> >
<Container class="flex flex-col gap-y-6"> <Container class="flex flex-col gap-y-6">
<Breadcrumbs <Breadcrumbs

View file

@ -9,13 +9,8 @@ export async function GET(context: APIContext) {
(post) => !post.data.draft, (post) => !post.data.draft,
) )
// Filter posts by tag 'rss-feed'
const filteredBlogs = blog.filter(
(post) => post.data.tags && post.data.tags.includes('rss-feed'),
)
// Sort posts by date // Sort posts by date
const items = [...filteredBlogs].sort( const items = [...blog].sort(
(a, b) => (a, b) =>
new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf(), new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf(),
) )

View file

@ -38,12 +38,12 @@ export async function getStaticPaths() {
> >
<Container class="flex flex-col gap-y-6"> <Container class="flex flex-col gap-y-6">
<Breadcrumbs items={[{ href: '/tags', label: 'Tags' }, { label: tag }]} /> <Breadcrumbs items={[{ href: '/tags', label: 'Tags' }, { label: tag }]} />
<div class="flex items-center gap-2"> <div class="flex items-center flex-wrap gap-2">
<h1 class="text-3xl font-semibold">Posts tagged with</h1> <h1 class="text-3xl font-semibold">Posts tagged with</h1>
<span <span
class="flex items-center gap-x-1 rounded-full bg-secondary px-4 py-2 text-2xl font-medium" class="flex items-center gap-x-1 rounded-full bg-secondary px-4 py-2 text-2xl font-bold"
> >
<Hash className="size-6" />{tag} <Hash className="size-6 -translate-x-0.5" strokeWidth={3} />{tag}
</span> </span>
</div> </div>
<div class="flex flex-col gap-y-4"> <div class="flex flex-col gap-y-4">

View file

@ -6,10 +6,6 @@
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 0 0% 3.9%; --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: 0 0% 9%;
--primary-foreground: 0 0% 98%; --primary-foreground: 0 0% 98%;
--secondary: 0 0% 80.1%; --secondary: 0 0% 80.1%;
@ -23,22 +19,11 @@
--destructive: 0 84.2% 60.2%; --destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%; --destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%; --border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%; --ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
} }
.dark { .dark {
--background: 0 0% 3.9%; --background: 0 0% 3.9%;
--foreground: 0 0% 98%; --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: 0 0% 98%;
--primary-foreground: 0 0% 9%; --primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%; --secondary: 0 0% 14.9%;
@ -52,13 +37,7 @@
--destructive: 0 62.8% 30.6%; --destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%; --destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%; --border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%; --ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
} }
*, *,
@ -80,6 +59,11 @@
@apply bg-transparent; @apply bg-transparent;
} }
} }
.disable-transitions,
.disable-transitions * {
transition: none !important;
}
} }
@layer components { @layer components {