commit
740b95b79a
27 changed files with 8474 additions and 0 deletions
@ -0,0 +1,24 @@ |
|||||||
|
# Nuxt dev/build outputs |
||||||
|
.output |
||||||
|
.data |
||||||
|
.nuxt |
||||||
|
.nitro |
||||||
|
.cache |
||||||
|
dist |
||||||
|
|
||||||
|
# Node dependencies |
||||||
|
node_modules |
||||||
|
|
||||||
|
# Logs |
||||||
|
logs |
||||||
|
*.log |
||||||
|
|
||||||
|
# Misc |
||||||
|
.DS_Store |
||||||
|
.fleet |
||||||
|
.idea |
||||||
|
|
||||||
|
# Local env files |
||||||
|
.env |
||||||
|
.env.* |
||||||
|
!.env.example |
@ -0,0 +1,10 @@ |
|||||||
|
{ |
||||||
|
"$schema": "https://json.schemastore.org/prettierrc", |
||||||
|
"semi": false, |
||||||
|
"singleQuote": true, |
||||||
|
"printWidth": 100, |
||||||
|
"plugins": ["prettier-plugin-tailwindcss"], |
||||||
|
"tailwindStylesheet": "./assets/styles/main.css", |
||||||
|
"tailwindConfig": "./tailwind.config.js", |
||||||
|
"tailwindFunctions": ["clsx"] |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
# Nuxt Minimal Starter |
||||||
|
|
||||||
|
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. |
||||||
|
|
||||||
|
## Setup |
||||||
|
|
||||||
|
Make sure to install dependencies: |
||||||
|
|
||||||
|
```bash |
||||||
|
# npm |
||||||
|
npm install |
||||||
|
|
||||||
|
# pnpm |
||||||
|
pnpm install |
||||||
|
|
||||||
|
# yarn |
||||||
|
yarn install |
||||||
|
|
||||||
|
# bun |
||||||
|
bun install |
||||||
|
``` |
||||||
|
|
||||||
|
## Development Server |
||||||
|
|
||||||
|
Start the development server on `http://localhost:3000`: |
||||||
|
|
||||||
|
```bash |
||||||
|
# npm |
||||||
|
npm run dev |
||||||
|
|
||||||
|
# pnpm |
||||||
|
pnpm dev |
||||||
|
|
||||||
|
# yarn |
||||||
|
yarn dev |
||||||
|
|
||||||
|
# bun |
||||||
|
bun run dev |
||||||
|
``` |
||||||
|
|
||||||
|
## Production |
||||||
|
|
||||||
|
Build the application for production: |
||||||
|
|
||||||
|
```bash |
||||||
|
# npm |
||||||
|
npm run build |
||||||
|
|
||||||
|
# pnpm |
||||||
|
pnpm build |
||||||
|
|
||||||
|
# yarn |
||||||
|
yarn build |
||||||
|
|
||||||
|
# bun |
||||||
|
bun run build |
||||||
|
``` |
||||||
|
|
||||||
|
Locally preview production build: |
||||||
|
|
||||||
|
```bash |
||||||
|
# npm |
||||||
|
npm run preview |
||||||
|
|
||||||
|
# pnpm |
||||||
|
pnpm preview |
||||||
|
|
||||||
|
# yarn |
||||||
|
yarn preview |
||||||
|
|
||||||
|
# bun |
||||||
|
bun run preview |
||||||
|
``` |
||||||
|
|
||||||
|
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. |
@ -0,0 +1,5 @@ |
|||||||
|
<template> |
||||||
|
<NuxtLayout> |
||||||
|
<NuxtPage /> |
||||||
|
</NuxtLayout> |
||||||
|
</template> |
After Width: | Height: | Size: 2.4 MiB |
@ -0,0 +1,83 @@ |
|||||||
|
@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; |
||||||
|
--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%; |
||||||
|
} |
||||||
|
|
||||||
|
.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%; |
||||||
|
--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%; |
||||||
|
} |
||||||
|
} |
||||||
|
@layer base { |
||||||
|
* { |
||||||
|
@apply border-border; |
||||||
|
} |
||||||
|
body { |
||||||
|
@apply bg-background text-foreground; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
@import './base.css'; |
||||||
|
|
||||||
|
html { |
||||||
|
@apply w-full h-full; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
@apply w-full h-full; |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
{ |
||||||
|
"$schema": "https://shadcn-vue.com/schema.json", |
||||||
|
"style": "new-york", |
||||||
|
"typescript": true, |
||||||
|
"tailwind": { |
||||||
|
"config": "tailwind.config.js", |
||||||
|
"css": "assets/styles/base.css", |
||||||
|
"baseColor": "neutral", |
||||||
|
"cssVariables": true, |
||||||
|
"prefix": "" |
||||||
|
}, |
||||||
|
"aliases": { |
||||||
|
"components": "@/components", |
||||||
|
"composables": "@/composables", |
||||||
|
"utils": "@/lib/utils", |
||||||
|
"ui": "@/components/ui", |
||||||
|
"lib": "@/lib" |
||||||
|
}, |
||||||
|
"iconLibrary": "lucide" |
||||||
|
} |
@ -0,0 +1,139 @@ |
|||||||
|
<template> |
||||||
|
<div |
||||||
|
:class=" |
||||||
|
cn( |
||||||
|
'relative z-0 flex min-h-screen w-full flex-col items-center justify-center overflow-hidden rounded-md bg-slate-950', |
||||||
|
$props.class, |
||||||
|
) |
||||||
|
" |
||||||
|
> |
||||||
|
<div class="relative isolate z-0 flex w-full flex-1 scale-y-125 items-center justify-center"> |
||||||
|
<!-- Conic Gradient --> |
||||||
|
<div |
||||||
|
:style="{ |
||||||
|
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`, |
||||||
|
}" |
||||||
|
class="animate-conic-gradient bg-gradient-conic absolute inset-auto right-1/2 h-56 w-60 overflow-visible from-cyan-500 via-transparent to-transparent text-white opacity-50 [--conic-position:from_70deg_at_center_top]" |
||||||
|
> |
||||||
|
<div |
||||||
|
class="absolute bottom-0 left-0 z-20 h-40 w-full bg-slate-950 [mask-image:linear-gradient(to_top,white,transparent)]" |
||||||
|
/> |
||||||
|
<div |
||||||
|
class="absolute bottom-0 left-0 z-20 h-full w-40 bg-slate-950 [mask-image:linear-gradient(to_right,white,transparent)]" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div |
||||||
|
:style="{ |
||||||
|
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`, |
||||||
|
}" |
||||||
|
class="animate-conic-gradient bg-gradient-conic absolute inset-auto left-1/2 h-56 w-60 from-transparent via-transparent to-cyan-500 text-white opacity-50 [--conic-position:from_290deg_at_center_top]" |
||||||
|
> |
||||||
|
<div |
||||||
|
class="absolute bottom-0 right-0 z-20 h-full w-40 bg-slate-950 [mask-image:linear-gradient(to_left,white,transparent)]" |
||||||
|
/> |
||||||
|
<div |
||||||
|
class="absolute bottom-0 right-0 z-20 h-40 w-full bg-slate-950 [mask-image:linear-gradient(to_top,white,transparent)]" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div |
||||||
|
class="absolute top-1/2 h-48 w-full translate-y-12 scale-x-150 bg-slate-950 blur-2xl" |
||||||
|
></div> |
||||||
|
|
||||||
|
<div |
||||||
|
class="absolute top-1/2 z-50 h-48 w-full bg-transparent opacity-10 backdrop-blur-md" |
||||||
|
></div> |
||||||
|
|
||||||
|
<div |
||||||
|
class="absolute inset-auto z-50 h-36 w-[28rem] -translate-y-1/2 rounded-full bg-cyan-500 opacity-50 blur-3xl" |
||||||
|
></div> |
||||||
|
|
||||||
|
<!-- Spotlight --> |
||||||
|
<div |
||||||
|
class="animate-spotlight absolute inset-auto z-30 h-36 w-32 -translate-y-24 rounded-full bg-cyan-400 blur-2xl" |
||||||
|
></div> |
||||||
|
|
||||||
|
<!-- Glowing Line --> |
||||||
|
<div |
||||||
|
class="animate-glowing-line absolute inset-auto z-50 h-0.5 w-60 -translate-y-28 bg-cyan-400" |
||||||
|
></div> |
||||||
|
|
||||||
|
<div class="absolute inset-auto z-40 h-44 w-full translate-y-[-12.5rem] bg-slate-950"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="relative z-50 flex -translate-y-80 flex-col items-center px-5"> |
||||||
|
<slot /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { computed, type HTMLAttributes } from 'vue' |
||||||
|
import { cn } from '@/lib/utils' |
||||||
|
interface LampEffectProps { |
||||||
|
delay?: number |
||||||
|
duration?: number |
||||||
|
class?: HTMLAttributes['class'] |
||||||
|
} |
||||||
|
|
||||||
|
const props = withDefaults(defineProps<LampEffectProps>(), { |
||||||
|
delay: 0.5, |
||||||
|
duration: 0.8, |
||||||
|
}) |
||||||
|
|
||||||
|
const durationInSeconds = computed(() => `${props.duration}s`) |
||||||
|
const delayInSeconds = computed(() => `${props.delay}s`) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
/* Spotlight Animation */ |
||||||
|
.animate-spotlight { |
||||||
|
animation: spotlight-anim ease-in-out v-bind(durationInSeconds) forwards; |
||||||
|
animation-delay: v-bind(delayInSeconds); |
||||||
|
} |
||||||
|
|
||||||
|
/* Glowing Line Animation */ |
||||||
|
.animate-glowing-line { |
||||||
|
animation: glowing-line-anim ease-in-out v-bind(durationInSeconds) forwards; |
||||||
|
animation-delay: v-bind(delayInSeconds); |
||||||
|
} |
||||||
|
|
||||||
|
/* Conic Gradient Animation */ |
||||||
|
.animate-conic-gradient { |
||||||
|
animation: conic-gradient-anim ease-in-out v-bind(durationInSeconds) forwards; |
||||||
|
animation-delay: v-bind(delayInSeconds); |
||||||
|
} |
||||||
|
|
||||||
|
/* Keyframes for Spotlight */ |
||||||
|
@keyframes spotlight-anim { |
||||||
|
from { |
||||||
|
width: 8rem; |
||||||
|
} |
||||||
|
to { |
||||||
|
width: 16rem; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Keyframes for Glowing Line */ |
||||||
|
@keyframes glowing-line-anim { |
||||||
|
from { |
||||||
|
width: 15rem; |
||||||
|
} |
||||||
|
to { |
||||||
|
width: 30rem; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Keyframes for Conic Gradient */ |
||||||
|
@keyframes conic-gradient-anim { |
||||||
|
from { |
||||||
|
opacity: 0.5; |
||||||
|
width: 15rem; |
||||||
|
} |
||||||
|
to { |
||||||
|
opacity: 1; |
||||||
|
width: 30rem; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,253 @@ |
|||||||
|
<template> |
||||||
|
<div :class="cn('relative h-full w-full', props.containerClass)"> |
||||||
|
<Motion |
||||||
|
ref="containerRef" |
||||||
|
as="div" |
||||||
|
:initial="{ opacity: 0 }" |
||||||
|
:animate="{ opacity: 1 }" |
||||||
|
class="absolute inset-0 z-0 flex size-full items-center justify-center bg-transparent" |
||||||
|
> |
||||||
|
<canvas ref="canvasRef"></canvas> |
||||||
|
</Motion> |
||||||
|
|
||||||
|
<div :class="cn('relative z-10', props.class)"> |
||||||
|
<slot /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { createNoise3D } from 'simplex-noise' |
||||||
|
import { onMounted, onUnmounted } from 'vue' |
||||||
|
import { templateRef, useDebounceFn } from '@vueuse/core' |
||||||
|
import { cn } from '@/lib/utils' |
||||||
|
const TAU = 2 * Math.PI |
||||||
|
const BASE_TTL = 50 |
||||||
|
const RANGE_TTL = 150 |
||||||
|
const PARTICLE_PROP_COUNT = 9 |
||||||
|
const RANGE_HUE = 100 |
||||||
|
const NOISE_STEPS = 3 |
||||||
|
const X_OFF = 0.00125 |
||||||
|
const Y_OFF = 0.00125 |
||||||
|
const Z_OFF = 0.0005 |
||||||
|
|
||||||
|
interface VortexProps { |
||||||
|
class?: string |
||||||
|
containerClass?: string |
||||||
|
particleCount?: number |
||||||
|
rangeY?: number |
||||||
|
baseHue?: number |
||||||
|
baseSpeed?: number |
||||||
|
rangeSpeed?: number |
||||||
|
baseRadius?: number |
||||||
|
rangeRadius?: number |
||||||
|
backgroundColor?: string |
||||||
|
} |
||||||
|
|
||||||
|
const props = withDefaults(defineProps<VortexProps>(), { |
||||||
|
particleCount: 700, |
||||||
|
rangeY: 100, |
||||||
|
baseSpeed: 0.0, |
||||||
|
rangeSpeed: 1.5, |
||||||
|
baseRadius: 1, |
||||||
|
rangeRadius: 2, |
||||||
|
baseHue: 220, |
||||||
|
backgroundColor: '#000000', |
||||||
|
}) |
||||||
|
|
||||||
|
const tick = ref<number>(0) |
||||||
|
const animationFrame = ref<number | null>(null) |
||||||
|
const particleProps = shallowRef<Float32Array | null>(null) |
||||||
|
const center = ref<[number, number]>([0, 0]) |
||||||
|
const ctx = shallowRef<CanvasRenderingContext2D | null>(null) |
||||||
|
|
||||||
|
const canvasRef = templateRef<HTMLCanvasElement | null>('canvasRef') |
||||||
|
const containerRef = templateRef<HTMLElement | null>('containerRef') |
||||||
|
|
||||||
|
const particleCache = { |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
vx: 0, |
||||||
|
vy: 0, |
||||||
|
life: 0, |
||||||
|
ttl: 0, |
||||||
|
speed: 0, |
||||||
|
radius: 0, |
||||||
|
hue: 0, |
||||||
|
} |
||||||
|
|
||||||
|
const noise3D = createNoise3D() |
||||||
|
|
||||||
|
function rand(n: number) { |
||||||
|
return n * Math.random() |
||||||
|
} |
||||||
|
function randRange(n: number): number { |
||||||
|
return n - rand(2 * n) |
||||||
|
} |
||||||
|
function fadeInOut(t: number, m: number): number { |
||||||
|
const hm = 0.5 * m |
||||||
|
return Math.abs(((t + hm) % m) - hm) / hm |
||||||
|
} |
||||||
|
function lerp(n1: number, n2: number, speed: number): number { |
||||||
|
return (1 - speed) * n1 + speed * n2 |
||||||
|
} |
||||||
|
|
||||||
|
function initParticle(i: number) { |
||||||
|
if (!particleProps.value || !canvasRef.value) return |
||||||
|
|
||||||
|
const canvas = canvasRef.value |
||||||
|
particleCache.x = rand(canvas.width) |
||||||
|
particleCache.y = center.value[1] + randRange(props.rangeY) |
||||||
|
particleCache.vx = 0 |
||||||
|
particleCache.vy = 0 |
||||||
|
particleCache.life = 0 |
||||||
|
particleCache.ttl = BASE_TTL + rand(RANGE_TTL) |
||||||
|
particleCache.speed = props.baseSpeed + rand(props.rangeSpeed) |
||||||
|
particleCache.radius = props.baseRadius + rand(props.rangeRadius) |
||||||
|
particleCache.hue = props.baseHue + rand(RANGE_HUE) |
||||||
|
|
||||||
|
particleProps.value.set( |
||||||
|
[ |
||||||
|
particleCache.x, |
||||||
|
particleCache.y, |
||||||
|
particleCache.vx, |
||||||
|
particleCache.vy, |
||||||
|
particleCache.life, |
||||||
|
particleCache.ttl, |
||||||
|
particleCache.speed, |
||||||
|
particleCache.radius, |
||||||
|
particleCache.hue, |
||||||
|
], |
||||||
|
i, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
function updateParticle(i: number) { |
||||||
|
if (!particleProps.value || !canvasRef.value || !ctx.value) return |
||||||
|
|
||||||
|
const canvas = canvasRef.value |
||||||
|
const props = particleProps.value |
||||||
|
const context = ctx.value |
||||||
|
|
||||||
|
particleCache.x = props[i] |
||||||
|
particleCache.y = props[i + 1] |
||||||
|
particleCache.vx = props[i + 2] |
||||||
|
particleCache.vy = props[i + 3] |
||||||
|
particleCache.life = props[i + 4] |
||||||
|
particleCache.ttl = props[i + 5] |
||||||
|
particleCache.speed = props[i + 6] |
||||||
|
particleCache.radius = props[i + 7] |
||||||
|
particleCache.hue = props[i + 8] |
||||||
|
|
||||||
|
const n = |
||||||
|
noise3D(particleCache.x * X_OFF, particleCache.y * Y_OFF, tick.value * Z_OFF) * |
||||||
|
NOISE_STEPS * |
||||||
|
TAU |
||||||
|
|
||||||
|
const nextVx = lerp(particleCache.vx, Math.cos(n), 0.5) |
||||||
|
const nextVy = lerp(particleCache.vy, Math.sin(n), 0.5) |
||||||
|
const nextX = particleCache.x + nextVx * particleCache.speed |
||||||
|
const nextY = particleCache.y + nextVy * particleCache.speed |
||||||
|
|
||||||
|
context.save() |
||||||
|
context.lineCap = 'round' |
||||||
|
context.lineWidth = particleCache.radius |
||||||
|
context.strokeStyle = `hsla(${particleCache.hue},100%,60%,${fadeInOut( |
||||||
|
particleCache.life, |
||||||
|
particleCache.ttl, |
||||||
|
)})` |
||||||
|
context.beginPath() |
||||||
|
context.moveTo(particleCache.x, particleCache.y) |
||||||
|
context.lineTo(nextX, nextY) |
||||||
|
context.stroke() |
||||||
|
context.restore() |
||||||
|
|
||||||
|
props[i] = nextX |
||||||
|
props[i + 1] = nextY |
||||||
|
props[i + 2] = nextVx |
||||||
|
props[i + 3] = nextVy |
||||||
|
props[i + 4] = particleCache.life + 1 |
||||||
|
|
||||||
|
if ( |
||||||
|
nextX > canvas.width || |
||||||
|
nextX < 0 || |
||||||
|
nextY > canvas.height || |
||||||
|
nextY < 0 || |
||||||
|
particleCache.life > particleCache.ttl |
||||||
|
) { |
||||||
|
initParticle(i) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function draw() { |
||||||
|
if (!canvasRef.value || !ctx.value || !particleProps.value) return |
||||||
|
|
||||||
|
const canvas = canvasRef.value |
||||||
|
const context = ctx.value |
||||||
|
|
||||||
|
tick.value++ |
||||||
|
|
||||||
|
context.fillStyle = props.backgroundColor |
||||||
|
context.fillRect(0, 0, canvas.width, canvas.height) |
||||||
|
|
||||||
|
for (let i = 0; i < particleProps.value.length; i += PARTICLE_PROP_COUNT) { |
||||||
|
updateParticle(i) |
||||||
|
} |
||||||
|
|
||||||
|
context.save() |
||||||
|
context.filter = 'blur(8px) brightness(200%)' |
||||||
|
context.globalCompositeOperation = 'lighter' |
||||||
|
context.drawImage(canvas, 0, 0) |
||||||
|
context.restore() |
||||||
|
|
||||||
|
context.save() |
||||||
|
context.filter = 'blur(4px) brightness(200%)' |
||||||
|
context.globalCompositeOperation = 'lighter' |
||||||
|
context.drawImage(canvas, 0, 0) |
||||||
|
context.restore() |
||||||
|
|
||||||
|
animationFrame.value = requestAnimationFrame(draw) |
||||||
|
} |
||||||
|
|
||||||
|
const handleResize = useDebounceFn(() => { |
||||||
|
if (!canvasRef.value) return |
||||||
|
|
||||||
|
const canvas = canvasRef.value |
||||||
|
const { innerWidth, innerHeight } = window |
||||||
|
canvas.width = innerWidth |
||||||
|
canvas.height = innerHeight |
||||||
|
center.value = [0.5 * canvas.width, 0.5 * canvas.height] |
||||||
|
}, 150) |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
const canvas = canvasRef.value |
||||||
|
if (!canvas) return |
||||||
|
|
||||||
|
ctx.value = canvas.getContext('2d') |
||||||
|
if (!ctx.value) return |
||||||
|
|
||||||
|
canvas.width = window.innerWidth |
||||||
|
canvas.height = window.innerHeight |
||||||
|
center.value = [0.5 * canvas.width, 0.5 * canvas.height] |
||||||
|
|
||||||
|
const particlePropsLength = props.particleCount * PARTICLE_PROP_COUNT |
||||||
|
particleProps.value = new Float32Array(particlePropsLength) |
||||||
|
|
||||||
|
for (let i = 0; i < particlePropsLength; i += PARTICLE_PROP_COUNT) { |
||||||
|
initParticle(i) |
||||||
|
} |
||||||
|
|
||||||
|
draw() |
||||||
|
window.addEventListener('resize', handleResize) |
||||||
|
}) |
||||||
|
|
||||||
|
onUnmounted(() => { |
||||||
|
if (animationFrame.value) { |
||||||
|
cancelAnimationFrame(animationFrame.value) |
||||||
|
} |
||||||
|
window.removeEventListener('resize', handleResize) |
||||||
|
|
||||||
|
ctx.value = null |
||||||
|
particleProps.value = null |
||||||
|
}) |
||||||
|
</script> |
@ -0,0 +1,26 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
import type { HTMLAttributes } from 'vue' |
||||||
|
import { cn } from '@/lib/utils' |
||||||
|
import { Primitive, type PrimitiveProps } from 'reka-ui' |
||||||
|
import { type ButtonVariants, buttonVariants } from '.' |
||||||
|
|
||||||
|
interface Props extends PrimitiveProps { |
||||||
|
variant?: ButtonVariants['variant'] |
||||||
|
size?: ButtonVariants['size'] |
||||||
|
class?: HTMLAttributes['class'] |
||||||
|
} |
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), { |
||||||
|
as: 'button', |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<Primitive |
||||||
|
:as="as" |
||||||
|
:as-child="asChild" |
||||||
|
:class="cn(buttonVariants({ variant, size }), props.class)" |
||||||
|
> |
||||||
|
<slot /> |
||||||
|
</Primitive> |
||||||
|
</template> |
@ -0,0 +1,35 @@ |
|||||||
|
import { cva, type VariantProps } from 'class-variance-authority' |
||||||
|
|
||||||
|
export { default as Button } from './Button.vue' |
||||||
|
|
||||||
|
export const buttonVariants = cva( |
||||||
|
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', |
||||||
|
{ |
||||||
|
variants: { |
||||||
|
variant: { |
||||||
|
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90', |
||||||
|
destructive: |
||||||
|
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', |
||||||
|
outline: |
||||||
|
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground active:bg-accent/900', |
||||||
|
secondary: |
||||||
|
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', |
||||||
|
ghost: 'hover:bg-accent hover:text-accent-foreground', |
||||||
|
link: 'text-primary underline-offset-4 hover:underline', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
default: 'h-9 px-4 py-2', |
||||||
|
xs: 'h-7 rounded px-2', |
||||||
|
sm: 'h-8 rounded-md px-3 text-xs', |
||||||
|
lg: 'h-10 rounded-md px-8', |
||||||
|
icon: 'h-9 w-9', |
||||||
|
}, |
||||||
|
}, |
||||||
|
defaultVariants: { |
||||||
|
variant: 'default', |
||||||
|
size: 'default', |
||||||
|
}, |
||||||
|
}, |
||||||
|
) |
||||||
|
|
||||||
|
export type ButtonVariants = VariantProps<typeof buttonVariants> |
@ -0,0 +1,238 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<header |
||||||
|
class="z-1000 fixed left-0 right-0 top-[-1] w-full bg-transparent duration-300" |
||||||
|
:class="{ 'backdrop-blur-2xl': isScrolled || isNavbarOpen, 'h-full': isNavbarOpen }" |
||||||
|
> |
||||||
|
<div class="header-bg flex h-13 w-full items-center justify-between px-6 sm:px-4 md:px-8"> |
||||||
|
<h1 class="text-2xl font-bold text-[#52AC63]">BTDK</h1> |
||||||
|
<nav class="absolute left-0 right-0 top-0"> |
||||||
|
<ul class="flex justify-center gap-8 leading-13 text-white sm:hidden md:hidden"> |
||||||
|
<li |
||||||
|
v-for="nav in navItems" |
||||||
|
:key="nav.path" |
||||||
|
class="relative h-full text-inherit after:absolute after:bottom-2 after:left-0 after:right-0 after:h-[1px] after:w-full after:origin-top-right after:scale-x-0 after:bg-[#52AC63] after:transition-[transform] after:duration-300 after:content-[''] hover:after:origin-top-left hover:after:scale-x-100" |
||||||
|
> |
||||||
|
<NuxtLink :to="nav.path" class="text-inherit"> |
||||||
|
{{ nav.name }} |
||||||
|
</NuxtLink> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</nav> |
||||||
|
<Menu as="div" class="hidden h-full sm:block md:block" v-slot="{ open: menuOpen }"> |
||||||
|
<MenuButton class="h-full"> |
||||||
|
<div |
||||||
|
class="navbar-control" |
||||||
|
:class="{ open: isNavbarOpen }" |
||||||
|
@click="toggleNavbar(menuOpen)" |
||||||
|
> |
||||||
|
<span class="control-icon"></span> |
||||||
|
<span class="control-icon"></span> |
||||||
|
<span class="control-icon"></span> |
||||||
|
</div> |
||||||
|
</MenuButton> |
||||||
|
<transition |
||||||
|
enter-active-class="transition-all duration-300 overflow-hidden" |
||||||
|
enter-from-class="max-h-0 opacity-70" |
||||||
|
enter-to-class="h-full opacity-100" |
||||||
|
leave-active-class="transition-all duration-200 overflow-hidden" |
||||||
|
leave-from-class="h-full opacity-100" |
||||||
|
leave-to-class="max-h-0 opacity-70" |
||||||
|
> |
||||||
|
<!-- lg:ml-[-110px] flex justify-center md:justify-start md:flex-col md:text-center md:overflow-auto md:grow-[1] --> |
||||||
|
<div v-show="isNavbarOpen"> |
||||||
|
<MenuItems |
||||||
|
as="ul" |
||||||
|
class="header-bg z-1000 border-t-1 absolute inset-0 top-13 overflow-hidden border-t-[1px] border-[hsla(0,0%,100%,.06)] text-white backdrop-blur-2xl" |
||||||
|
static |
||||||
|
> |
||||||
|
<template v-for="nav in navItems" :key="nav.path"> |
||||||
|
<MenuItem |
||||||
|
as="li" |
||||||
|
v-slot="{ close }" |
||||||
|
class="cursor-pointer text-nowrap text-center" |
||||||
|
@click="onNavClick(nav, menuOpen)" |
||||||
|
> |
||||||
|
<Disclosure as="div" class="size-full" v-slot="{ open }"> |
||||||
|
<DisclosureButton as="p" class="relative py-7"> |
||||||
|
<span>{{ nav.name }} </span> |
||||||
|
<Icon |
||||||
|
name="mingcute:up-line" |
||||||
|
:class="open ? '' : 'rotate-180 transform'" |
||||||
|
class="absolute right-5 top-1/2 -translate-y-1/2 text-lg transition-transform duration-300" |
||||||
|
:ssr="true" |
||||||
|
v-if="!!nav.children?.length" |
||||||
|
/> |
||||||
|
</DisclosureButton> |
||||||
|
<transition |
||||||
|
enter-active-class="transition-all duration-700 overflow-hidden" |
||||||
|
enter-from-class="max-h-0 opacity-70" |
||||||
|
enter-to-class="max-h-[300px] opacity-100" |
||||||
|
leave-active-class="transition-all duration-200 overflow-hidden" |
||||||
|
leave-from-class="max-h-[300px] opacity-100" |
||||||
|
leave-to-class="max-h-0 opacity-70" |
||||||
|
> |
||||||
|
<DisclosurePanel as="div" v-if="!!nav.children?.length"> |
||||||
|
<ul class="flex flex-col bg-[hsla(0,0%,100%,.06)]"> |
||||||
|
<li |
||||||
|
class="text-nowrap py-7 text-center" |
||||||
|
v-for="child in nav.children" |
||||||
|
:key="child.path" |
||||||
|
@click="onNavClick(child, menuOpen)" |
||||||
|
> |
||||||
|
<span> |
||||||
|
{{ child.name }} |
||||||
|
</span> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</DisclosurePanel> |
||||||
|
</transition> |
||||||
|
</Disclosure> |
||||||
|
</MenuItem> |
||||||
|
</template> |
||||||
|
</MenuItems> |
||||||
|
</div> |
||||||
|
</transition> |
||||||
|
</Menu> |
||||||
|
</div> |
||||||
|
</header> |
||||||
|
<slot /> |
||||||
|
<footer> |
||||||
|
<p>版权所有 © 2025 比特电科</p> |
||||||
|
</footer> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<script setup lang="ts"> |
||||||
|
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' |
||||||
|
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue' |
||||||
|
|
||||||
|
const isScrolled = ref(false) |
||||||
|
const router = useRouter() |
||||||
|
const checkScroll = () => { |
||||||
|
isScrolled.value = window.scrollY > 20 |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
if (typeof window !== 'undefined') { |
||||||
|
checkScroll() |
||||||
|
window.addEventListener('scroll', checkScroll) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
onUnmounted(() => { |
||||||
|
if (typeof window !== 'undefined') { |
||||||
|
window.removeEventListener('scroll', checkScroll) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
const isNavbarOpen = ref(false) |
||||||
|
const toggleNavbar = (open: boolean) => { |
||||||
|
isNavbarOpen.value = !open |
||||||
|
} |
||||||
|
|
||||||
|
function onNavClick(nav: any, menuOpen: boolean) { |
||||||
|
if (!!nav.children?.length) { |
||||||
|
return |
||||||
|
} |
||||||
|
router.push(nav.path) |
||||||
|
isNavbarOpen.value = false |
||||||
|
} |
||||||
|
|
||||||
|
const navItems = [ |
||||||
|
{ |
||||||
|
name: '首页', |
||||||
|
path: '/', |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: '产品中心', |
||||||
|
path: '/products', |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
name: 'EM系列储能边缘智能网关', |
||||||
|
path: '/products/em-series-energy-storage-edge-intelligent-gateway', |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'EM系列储能边缘智能网关', |
||||||
|
path: '/products/em-series-energy-storage-edge-intelligent-gateway', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: '技术资料', |
||||||
|
path: '/technical-materials', |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: '在线商城', |
||||||
|
path: '/online-store', |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: '关于我们', |
||||||
|
path: '/about', |
||||||
|
}, |
||||||
|
] |
||||||
|
</script> |
||||||
|
<style scoped> |
||||||
|
.header-bg { |
||||||
|
@apply bg-[rgba(92,92,92,0.4)] bg-gradient-to-r from-black/40 to-black/40; |
||||||
|
} |
||||||
|
|
||||||
|
/* 子菜单展开收缩动画 */ |
||||||
|
.transition-all { |
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); |
||||||
|
} |
||||||
|
|
||||||
|
/* CSS */ |
||||||
|
.navbar-control { |
||||||
|
cursor: pointer; |
||||||
|
height: 100%; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
gap: 6px; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
|
||||||
|
.control-icon { |
||||||
|
transition: all 0.3s ease; |
||||||
|
} |
||||||
|
|
||||||
|
/* 每个 control-icon 的伪元素 */ |
||||||
|
.control-icon:before { |
||||||
|
background-color: #fff; |
||||||
|
border-bottom-left-radius: 1px; |
||||||
|
border-top-right-radius: 1px; |
||||||
|
content: ''; |
||||||
|
display: block; |
||||||
|
height: 1px; |
||||||
|
transition: |
||||||
|
transform 0.3s ease 0.2s, |
||||||
|
background-color 0.4s ease 0s; |
||||||
|
width: 22px; |
||||||
|
opacity: 0.9; |
||||||
|
} |
||||||
|
.navbar-control.open .control-icon:before { |
||||||
|
/* background-color: #000; */ |
||||||
|
} |
||||||
|
.navbar-control.open .control-icon:nth-child(1) { |
||||||
|
transform: translateY(7px); |
||||||
|
} |
||||||
|
.navbar-control.open .control-icon:nth-child(2) { |
||||||
|
opacity: 0; |
||||||
|
transition-duration: 0.3s; |
||||||
|
} |
||||||
|
|
||||||
|
.navbar-control .control-icon:nth-child(2) { |
||||||
|
transition-duration: 0.6s; |
||||||
|
} |
||||||
|
|
||||||
|
.navbar-control.open .control-icon:nth-child(3) { |
||||||
|
transform: translateY(-7px); |
||||||
|
} |
||||||
|
|
||||||
|
/* open 状态下的动画 */ |
||||||
|
.navbar-control.open .control-icon:nth-child(1):before { |
||||||
|
transform: rotate(45deg); |
||||||
|
} |
||||||
|
.navbar-control.open .control-icon:nth-child(3):before { |
||||||
|
transform: rotate(-45deg); |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,15 @@ |
|||||||
|
import type { Updater } from '@tanstack/vue-table' |
||||||
|
import type { Ref } from 'vue' |
||||||
|
import { type ClassValue, clsx } from 'clsx' |
||||||
|
import { twMerge } from 'tailwind-merge' |
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) { |
||||||
|
return twMerge(clsx(inputs)) |
||||||
|
} |
||||||
|
|
||||||
|
export function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) { |
||||||
|
ref.value |
||||||
|
= typeof updaterOrValue === 'function' |
||||||
|
? updaterOrValue(ref.value) |
||||||
|
: updaterOrValue |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
export default defineNuxtConfig({ |
||||||
|
compatibilityDate: '2024-11-01', |
||||||
|
modules: ['motion-v/nuxt', '@nuxtjs/tailwindcss', 'shadcn-nuxt', '@nuxt/icon'], |
||||||
|
devtools: { enabled: true }, |
||||||
|
postcss: { |
||||||
|
plugins: { |
||||||
|
tailwindcss: {}, |
||||||
|
autoprefixer: {}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
css: ['~/assets/styles/main.css'], |
||||||
|
vite: { |
||||||
|
plugins: [ |
||||||
|
], |
||||||
|
}, |
||||||
|
app: { |
||||||
|
head: { |
||||||
|
title: 'EM系列储能边缘智能网关-比特电科', |
||||||
|
meta: [ |
||||||
|
|
||||||
|
{ |
||||||
|
name: 'description', |
||||||
|
content: 'EM系列储能边缘智能网关-比特电科', |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'keywords', |
||||||
|
content: 'EM系列储能边缘智能网关-比特电科', |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'viewport', |
||||||
|
content: 'width=device-width, initial-scale=1.0', |
||||||
|
}, |
||||||
|
], |
||||||
|
link: [ |
||||||
|
{ |
||||||
|
rel: 'icon', |
||||||
|
type: 'image/x-icon', |
||||||
|
href: '/favicon.ico', |
||||||
|
}, |
||||||
|
], |
||||||
|
noscript: [ |
||||||
|
{ |
||||||
|
innerHTML: 'JavaScript is required', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
devServer: { |
||||||
|
port: 3300, // 设置开发服务器端口
|
||||||
|
host: '0.0.0.0', // 可选:允许外部访问
|
||||||
|
}, |
||||||
|
}) |
@ -0,0 +1,39 @@ |
|||||||
|
{ |
||||||
|
"name": "nuxt-app", |
||||||
|
"private": true, |
||||||
|
"type": "module", |
||||||
|
"scripts": { |
||||||
|
"build": "nuxt build", |
||||||
|
"dev": "nuxt dev", |
||||||
|
"generate": "nuxt generate", |
||||||
|
"preview": "nuxt preview", |
||||||
|
"postinstall": "nuxt prepare" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@headlessui/vue": "^1.7.23", |
||||||
|
"@vueuse/core": "^13.0.0", |
||||||
|
"class-variance-authority": "^0.7.1", |
||||||
|
"lucide-vue-next": "^0.486.0", |
||||||
|
"motion-v": "1.0.0-alpha.1", |
||||||
|
"nuxt": "^3.16.1", |
||||||
|
"reka-ui": "^2.1.1", |
||||||
|
"shadcn-nuxt": "1.0.3", |
||||||
|
"simplex-noise": "^4.0.3", |
||||||
|
"vue": "^3.5.13", |
||||||
|
"vue-router": "^4.5.0" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@iconify-json/mingcute": "^1.2.3", |
||||||
|
"@inspira-ui/plugins": "^0.0.1", |
||||||
|
"@nuxt/icon": "^1.11.0", |
||||||
|
"@nuxtjs/tailwindcss": "^6.13.2", |
||||||
|
"autoprefixer": "^10.4.21", |
||||||
|
"clsx": "^2.1.1", |
||||||
|
"postcss": "^8.5.3", |
||||||
|
"prettier": "^3.5.3", |
||||||
|
"prettier-plugin-tailwindcss": "^0.6.11", |
||||||
|
"tailwind-merge": "^3.1.0", |
||||||
|
"tailwindcss-animate": "^1.0.7" |
||||||
|
}, |
||||||
|
"packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a" |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<h1>404</h1> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"></script> |
||||||
|
|
||||||
|
<style scoped></style> |
@ -0,0 +1,12 @@ |
|||||||
|
<template> |
||||||
|
<div></div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
useSeoMeta({ |
||||||
|
title: '关于我们-比特电科', |
||||||
|
description: '关于我们-比特电科', |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped></style> |
@ -0,0 +1,11 @@ |
|||||||
|
<template> |
||||||
|
<main class="h-screen w-full"> |
||||||
|
<img src="../assets/images/bg.jpg" class="h-[32rem] w-full object-cover" /> |
||||||
|
</main> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue' |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped></style> |
@ -0,0 +1,12 @@ |
|||||||
|
<template> |
||||||
|
<div></div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
useSeoMeta({ |
||||||
|
title: '产品中心-比特电科', |
||||||
|
description: '产品中心-比特电科', |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped></style> |
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,3 @@ |
|||||||
|
{ |
||||||
|
"extends": "../.nuxt/tsconfig.server.json" |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
import animate from 'tailwindcss-animate' |
||||||
|
import { setupInspiraUI } from '@inspira-ui/plugins' |
||||||
|
|
||||||
|
/** @type {import('tailwindcss').Config} */ |
||||||
|
export default { |
||||||
|
darkMode: ['selector', 'class'], |
||||||
|
safelist: ['dark'], |
||||||
|
content: [ |
||||||
|
'./components/**/*.{js,vue,ts}', |
||||||
|
'./layouts/**/*.vue', |
||||||
|
'./pages/**/*.vue', |
||||||
|
'./plugins/**/*.{js,ts}', |
||||||
|
'./app.vue', |
||||||
|
'./error.vue', |
||||||
|
], |
||||||
|
theme: { |
||||||
|
extend: { |
||||||
|
screens: { |
||||||
|
sm: { |
||||||
|
max: '479px', |
||||||
|
}, |
||||||
|
md: { |
||||||
|
min: '480px', |
||||||
|
max: '760px', |
||||||
|
}, |
||||||
|
}, |
||||||
|
spacing: { |
||||||
|
13: '3.25rem', |
||||||
|
15: '3.75rem', |
||||||
|
17: '4.25rem', |
||||||
|
19: '4.75rem', |
||||||
|
21: '5.25rem', |
||||||
|
23: '5.75rem', |
||||||
|
25: '6.25rem', |
||||||
|
}, |
||||||
|
lineHeight: { |
||||||
|
13: '3.25rem', |
||||||
|
14: '3.5rem', |
||||||
|
15: '3.75rem', |
||||||
|
16: '4rem', |
||||||
|
}, |
||||||
|
|
||||||
|
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))', |
||||||
|
}, |
||||||
|
chart: { |
||||||
|
1: 'hsl(var(--chart-1))', |
||||||
|
2: 'hsl(var(--chart-2))', |
||||||
|
3: 'hsl(var(--chart-3))', |
||||||
|
4: 'hsl(var(--chart-4))', |
||||||
|
5: 'hsl(var(--chart-5))', |
||||||
|
}, |
||||||
|
}, |
||||||
|
borderRadius: { |
||||||
|
xl: 'calc(var(--radius) + 4px)', |
||||||
|
lg: 'var(--radius)', |
||||||
|
md: 'calc(var(--radius) - 2px)', |
||||||
|
sm: 'calc(var(--radius) - 4px)', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
plugins: [animate, setupInspiraUI, require('tailwindcss-animate')], |
||||||
|
} |
Loading…
Reference in new issue