21st.dev 원본
CPU Architecture · Gamma UI
섹션 · MIT
갤러리 안에서는 화면을 벗어나거나 움직임을 멈추면 예제도 닫힙니다. 다시 재생할 때는 처음부터 시작해요.
저장한 HTML에서는 갤러리의 재생 설정이 적용되지 않아요.
SOURCE FILES
원본 코드 읽기
수집한 원본 소스와 실행 안내를 함께 제공합니다. 실행 HTML에는 미리보기를 위한 실행 환경이 들어 있습니다.
baseline-example.tsx
"use client"
import CpuArchitecture from "@/components/ui/cpu-architecture"
const CpuArchitectureDemo = () => {
return (
<div className="h-full w-full rounded-xl p-4">
<CpuArchitecture />
</div>
)
}
export default CpuArchitectureDemo
LICENSE
MIT License
Copyright (c) Gamma UI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
함께 쓰는 파일 14개 보기
upstream/apps/www/content/docs/components/cpu-architecture.mdx
---
title: CPU Architecture
date: 2025-12-04
description: Animated CPU architecture SVG with dynamic glowing paths, gradients, masks, and configurable text and animations.
author: mazyar
published: true
---
import { CSSCodeBlock } from "@/components/css-code-block"
<ComponentPreview name="cpu-architecture-demo" />
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add @gammaui/cpu-architecture
```
<CSSCodeBlock
fileName="global.css"
code={`
.cpu-architecture {
offset-anchor: 10px 0px;
animation: animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.75, -0.01, 0, 0.99);
}
.cpu-line-1 {
offset-path: path("M 10 20 h 79.5 q 5 0 5 5 v 30");
animation-duration: 5s;
animation-delay: 1s;
}
.cpu-line-2 {
offset-path: path("M 180 10 h -69.7 q -5 0 -5 5 v 40");
animation-delay: 6s;
animation-duration: 2s;
}
.cpu-line-3 {
offset-path: path("M 130 20 v 21.8 q 0 5 -5 5 h -25");
animation-delay: 4s;
animation-duration: 6s;
}
.cpu-line-4 {
offset-path: path("M 170 80 v -21.8 q 0 -5 -5 -5 h -65");
animation-delay: 3s;
animation-duration: 3s;
}
.cpu-line-5 {
offset-path: path(
"M 135 65 h 15 q 5 0 5 5 v 10 q 0 5 -5 5 h -39.8 q -5 0 -5 -5 v -35"
);
animation-delay: 9s;
animation-duration: 4s;
}
.cpu-line-6 {
offset-path: path("M 94.8 95 v -46");
animation-delay: 3s;
animation-duration: 7s;
}
.cpu-line-7 {
offset-path: path(
"M 88 88 v -15 q 0 -5 -5 -5 h -10 q -5 0 -5 -5 v -5 q 0 -5 5 -5 h 28"
);
animation-delay: 4s;
animation-duration: 4s;
}
.cpu-line-8 {
offset-path: path("M 30 30 h 25 q 5 0 5 5 v 6.5 q 0 5 5 5 h 35");
animation-delay: 3s;
animation-duration: 3s;
}
@keyframes animation-path {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
`}
copyCode={true}
/>
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="cpu-architecture"
title="components/gammaui/cpu-architecture.tsx"
/>
<Step>Add the following CSS to your global CSS file.</Step>
<CSSCodeBlock
fileName="global.css"
code={`
.cpu-architecture {
offset-anchor: 10px 0px;
animation: animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.75, -0.01, 0, 0.99);
}
.cpu-line-1 {
offset-path: path("M 10 20 h 79.5 q 5 0 5 5 v 30");
animation-duration: 5s;
animation-delay: 1s;
}
.cpu-line-2 {
offset-path: path("M 180 10 h -69.7 q -5 0 -5 5 v 40");
animation-delay: 6s;
animation-duration: 2s;
}
.cpu-line-3 {
offset-path: path("M 130 20 v 21.8 q 0 5 -5 5 h -25");
animation-delay: 4s;
animation-duration: 6s;
}
.cpu-line-4 {
offset-path: path("M 170 80 v -21.8 q 0 -5 -5 -5 h -65");
animation-delay: 3s;
animation-duration: 3s;
}
.cpu-line-5 {
offset-path: path(
"M 135 65 h 15 q 5 0 5 5 v 10 q 0 5 -5 5 h -39.8 q -5 0 -5 -5 v -35"
);
animation-delay: 9s;
animation-duration: 4s;
}
.cpu-line-6 {
offset-path: path("M 94.8 95 v -46");
animation-delay: 3s;
animation-duration: 7s;
}
.cpu-line-7 {
offset-path: path(
"M 88 88 v -15 q 0 -5 -5 -5 h -10 q -5 0 -5 -5 v -5 q 0 -5 5 -5 h 28"
);
animation-delay: 4s;
animation-duration: 4s;
}
.cpu-line-8 {
offset-path: path("M 30 30 h 25 q 5 0 5 5 v 6.5 q 0 5 5 5 h 35");
animation-delay: 3s;
animation-duration: 3s;
}
@keyframes animation-path {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
`}
copyCode={true}
/>
<Step>Update import paths to match your project structure.</Step>
</Steps>
</TabsContent>
</Tabs>
---
## Usage
```tsx title="app/page.tsx" showLineNumbers
import { CpuArchitecture } from "@/components/gammaui/cpu-architecture"
export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center">
<CpuArchitecture />
</div>
)
}
```
---
## Examples
### Default CPU
```tsx title="example.tsx" showLineNumbers
<CpuArchitecture />
```
---
### Custom Text & Size
```tsx title="example.tsx" showLineNumbers {3-4}
<CpuArchitecture text="GPU" width="300" height="150" />
```
---
### Disable Animations
```tsx title="example.tsx" showLineNumbers {2-4}
<CpuArchitecture
animateLines={false}
animateMarkers={false}
animateText={false}
/>
```
---
### Hide CPU Connections
```tsx title="example.tsx" showLineNumbers {2}
<CpuArchitecture showCpuConnections={false} />
```
---
### Change Marker Size
```tsx title="example.tsx" showLineNumbers {2}
<CpuArchitecture lineMarkerSize={30} />
```
---
### Custom ClassName Support
```tsx title="example.tsx" showLineNumbers {2}
<CpuArchitecture className="text-blue-500" />
```
---
## Props
| Name | Type | Default | Description |
| -------------------- | ------- | -------- | ------------------------------------------------ |
| `text` | string | `"CPU"` | Label inside CPU box |
| `width` | string | `"100%"` | SVG width |
| `height` | string | `"100%"` | SVG height |
| `showCpuConnections` | boolean | `true` | Whether to show the small surrounding connectors |
| `animateText` | boolean | `true` | Enables text gradient animation |
| `animateLines` | boolean | `true` | Enables line drawing animation |
| `animateMarkers` | boolean | `true` | Enables pulsing markers |
| `lineMarkerSize` | number | `18` | Size of the moving markers |
| `className` | string | — | Custom CSS class |
upstream/apps/www/registry/example/cpu-architecture-demo.tsx
"use client"
import CpuArchitecture from "@/registry/gammaui/cpu-architecture"
const CpuArchitectureDemo = () => {
return (
<div className="h-full w-full rounded-xl p-4">
<CpuArchitecture />
</div>
)
}
export default CpuArchitectureDemo
author/apps/www/registry/gammaui/cpu-architecture.tsx
import React from "react"
import { cn } from "@/lib/utils"
export interface CpuArchitectureSvgProps {
className?: string
width?: string
height?: string
text?: string
showCpuConnections?: boolean
lineMarkerSize?: number
animateText?: boolean
animateLines?: boolean
animateMarkers?: boolean
}
export default function CpuArchitecture({
className,
width = "100%",
height = "100%",
text = "CPU",
showCpuConnections = true,
animateText = true,
lineMarkerSize = 18,
animateLines = true,
animateMarkers = true,
}: CpuArchitectureSvgProps) {
return (
<svg
className={cn("text-muted", className)}
width={width}
height={height}
viewBox="0 0 200 100"
>
{/* Paths */}
<g
stroke="currentColor"
fill="none"
strokeWidth="0.3"
strokeDasharray="100 100"
pathLength="100"
markerStart="url(#cpu-circle-marker)"
>
{/* 1st */}
<path
strokeDasharray="100 100"
pathLength="100"
d="M 10 20 h 79.5 q 5 0 5 5 v 30"
/>
{/* 2nd */}
<path
strokeDasharray="100 100"
pathLength="100"
d="M 180 10 h -69.7 q -5 0 -5 5 v 30"
/>
{/* 3rd */}
<path d="M 130 20 v 21.8 q 0 5 -5 5 h -10" />
{/* 4th */}
<path d="M 170 80 v -21.8 q 0 -5 -5 -5 h -50" />
{/* 5th */}
<path
strokeDasharray="100 100"
pathLength="100"
d="M 135 65 h 15 q 5 0 5 5 v 10 q 0 5 -5 5 h -39.8 q -5 0 -5 -5 v -20"
/>
{/* 6th */}
<path d="M 94.8 95 v -36" />
{/* 7th */}
<path d="M 88 88 v -15 q 0 -5 -5 -5 h -10 q -5 0 -5 -5 v -5 q 0 -5 5 -5 h 14" />
{/* 8th */}
<path d="M 30 30 h 25 q 5 0 5 5 v 6.5 q 0 5 5 5 h 20" />
{/* Animation For Path Starting */}
{animateLines && (
<animate
attributeName="stroke-dashoffset"
from="100"
to="0"
dur="1s"
fill="freeze"
calcMode="spline"
keySplines="0.25,0.1,0.5,1"
keyTimes="0; 1"
/>
)}
</g>
{/* 1. Blue Light */}
<g mask="url(#cpu-mask-1)">
<circle
className="cpu-architecture cpu-line-1"
cx="0"
cy="0"
r="8"
fill="url(#cpu-blue-grad)"
/>
</g>
{/* 2. Yellow Light */}
<g mask="url(#cpu-mask-2)">
<circle
className="cpu-architecture cpu-line-2"
cx="0"
cy="0"
r="8"
fill="url(#cpu-yellow-grad)"
/>
</g>
{/* 3. Pinkish Light */}
<g mask="url(#cpu-mask-3)">
<circle
className="cpu-architecture cpu-line-3"
cx="0"
cy="0"
r="8"
fill="url(#cpu-pinkish-grad)"
/>
</g>
{/* 4. White Light */}
<g mask="url(#cpu-mask-4)">
<circle
className="cpu-architecture cpu-line-4"
cx="0"
cy="0"
r="8"
fill="url(#cpu-white-grad)"
/>
</g>
{/* 5. Green Light */}
<g mask="url(#cpu-mask-5)">
<circle
className="cpu-architecture cpu-line-5"
cx="0"
cy="0"
r="8"
fill="url(#cpu-green-grad)"
/>
</g>
{/* 6. Orange Light */}
<g mask="url(#cpu-mask-6)">
<circle
className="cpu-architecture cpu-line-6"
cx="0"
cy="0"
r="8"
fill="url(#cpu-orange-grad)"
/>
</g>
{/* 7. Cyan Light */}
<g mask="url(#cpu-mask-7)">
<circle
className="cpu-architecture cpu-line-7"
cx="0"
cy="0"
r="8"
fill="url(#cpu-cyan-grad)"
/>
</g>
{/* 8. Rose Light */}
<g mask="url(#cpu-mask-8)">
<circle
className="cpu-architecture cpu-line-8"
cx="0"
cy="0"
r="8"
fill="url(#cpu-rose-grad)"
/>
</g>
{/* CPU Box */}
<g>
{/* Cpu connections */}
{showCpuConnections && (
<g fill="url(#cpu-connection-gradient)">
<rect x="93" y="37" width="2.5" height="5" rx="0.7" />
<rect x="104" y="37" width="2.5" height="5" rx="0.7" />
<rect
x="116.3"
y="44"
width="2.5"
height="5"
rx="0.7"
transform="rotate(90 116.25 45.5)"
/>
<rect
x="122.8"
y="44"
width="2.5"
height="5"
rx="0.7"
transform="rotate(90 116.25 45.5)"
/>
<rect
x="104"
y="16"
width="2.5"
height="5"
rx="0.7"
transform="rotate(180 105.25 39.5)"
/>
<rect
x="114.5"
y="16"
width="2.5"
height="5"
rx="0.7"
transform="rotate(180 105.25 39.5)"
/>
<rect
x="80"
y="-13.6"
width="2.5"
height="5"
rx="0.7"
transform="rotate(270 115.25 19.5)"
/>
<rect
x="87"
y="-13.6"
width="2.5"
height="5"
rx="0.7"
transform="rotate(270 115.25 19.5)"
/>
</g>
)}
{/* Main CPU Rectangle */}
<rect
x="85"
y="40"
width="30"
height="20"
rx="2"
fill="#181818"
filter="url(#cpu-light-shadow)"
/>
{/* CPU Text */}
<text
x="92"
y="52.5"
fontSize="7"
fill={animateText ? "url(#cpu-text-gradient)" : "white"}
fontWeight="600"
letterSpacing="0.05em"
>
{text}
</text>
</g>
{/* Masks */}
<defs>
<mask id="cpu-mask-1">
<path
d="M 10 20 h 79.5 q 5 0 5 5 v 24"
strokeWidth="0.5"
stroke="white"
/>
</mask>
<mask id="cpu-mask-2">
<path
d="M 180 10 h -69.7 q -5 0 -5 5 v 24"
strokeWidth="0.5"
stroke="white"
/>
</mask>
<mask id="cpu-mask-3">
<path
d="M 130 20 v 21.8 q 0 5 -5 5 h -10"
strokeWidth="0.5"
stroke="white"
/>
</mask>
<mask id="cpu-mask-4">
<path
d="M 170 80 v -21.8 q 0 -5 -5 -5 h -50"
strokeWidth="0.5"
stroke="white"
/>
</mask>
<mask id="cpu-mask-5">
<path
d="M 135 65 h 15 q 5 0 5 5 v 10 q 0 5 -5 5 h -39.8 q -5 0 -5 -5 v -20"
strokeWidth="0.5"
stroke="white"
/>
</mask>
<mask id="cpu-mask-6">
<path d="M 94.8 95 v -36" strokeWidth="0.5" stroke="white" />
</mask>
<mask id="cpu-mask-7">
<path
d="M 88 88 v -15 q 0 -5 -5 -5 h -10 q -5 0 -5 -5 v -5 q 0 -5 5 -5 h 14"
strokeWidth="0.5"
stroke="white"
/>
</mask>
<mask id="cpu-mask-8">
<path
d="M 30 30 h 25 q 5 0 5 5 v 6.5 q 0 5 5 5 h 20"
strokeWidth="0.5"
stroke="white"
/>
</mask>
{/* Gradients */}
<radialGradient id="cpu-blue-grad" fx="1">
<stop offset="0%" stopColor="#00E8ED" />
<stop offset="50%" stopColor="#08F" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<radialGradient id="cpu-yellow-grad" fx="1">
<stop offset="0%" stopColor="#FFD800" />
<stop offset="50%" stopColor="#FFD800" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<radialGradient id="cpu-pinkish-grad" fx="1">
<stop offset="0%" stopColor="#830CD1" />
<stop offset="50%" stopColor="#FF008B" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<radialGradient id="cpu-white-grad" fx="1">
<stop offset="0%" stopColor="white" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<radialGradient id="cpu-green-grad" fx="1">
<stop offset="0%" stopColor="#22c55e" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<radialGradient id="cpu-orange-grad" fx="1">
<stop offset="0%" stopColor="#f97316" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<radialGradient id="cpu-cyan-grad" fx="1">
<stop offset="0%" stopColor="#06b6d4" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<radialGradient id="cpu-rose-grad" fx="1">
<stop offset="0%" stopColor="#f43f5e" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
<filter
id="cpu-light-shadow"
x="-50%"
y="-50%"
width="200%"
height="200%"
>
<feDropShadow
dx="1.5"
dy="1.5"
stdDeviation="1"
floodColor="black"
floodOpacity="0.1"
/>
</filter>
<marker
id="cpu-circle-marker"
viewBox="0 0 10 10"
refX="5"
refY="5"
markerWidth={lineMarkerSize}
markerHeight={lineMarkerSize}
>
<circle
id="innerMarkerCircle"
cx="5"
cy="5"
r="2"
fill="black"
stroke="#232323"
strokeWidth="0.5"
>
{animateMarkers && (
<animate attributeName="r" values="0; 3; 2" dur="0.5s" />
)}
</circle>
</marker>
{/* Cpu connection gradient */}
<linearGradient
id="cpu-connection-gradient"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop offset="0%" stopColor="#4F4F4F" />
<stop offset="60%" stopColor="#121214" />
</linearGradient>
{/* Add CPU Text Gradient */}
<linearGradient id="cpu-text-gradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="#666666">
<animate
attributeName="offset"
values="-2; -1; 0"
dur="5s"
repeatCount="indefinite"
calcMode="spline"
keyTimes="0; 0.5; 1"
keySplines="0.4 0 0.2 1; 0.4 0 0.2 1"
/>
</stop>
<stop offset="25%" stopColor="white">
<animate
attributeName="offset"
values="-1; 0; 1"
dur="5s"
repeatCount="indefinite"
calcMode="spline"
keyTimes="0; 0.5; 1"
keySplines="0.4 0 0.2 1; 0.4 0 0.2 1"
/>
</stop>
<stop offset="50%" stopColor="#666666">
<animate
attributeName="offset"
values="0; 1; 2;"
dur="5s"
repeatCount="indefinite"
calcMode="spline"
keyTimes="0; 0.5; 1"
keySplines="0.4 0 0.2 1; 0.4 0 0.2 1"
/>
</stop>
</linearGradient>
</defs>
</svg>
)
}
author/apps/www/lib/utils.ts
import process from "process"
import { Metadata } from "next"
import clsx, { ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export const pluralize = (count: number, word: string) =>
`${count} ${word}${count === 1 ? "" : "s"}`
export function humanize(name: string): string {
return name
.replace(/-/g, " ")
.replace(/([A-Z])/g, " $1")
.trim()
.split(/\s+/)
.map((word) => word[0].toUpperCase() + word.substring(1).toLowerCase())
.join(" ")
}
export const truncate = (str: string | null, length: number) => {
if (!str || str.length <= length) return str
return `${str.slice(0, length - 3)}...`
}
export const fetcher = (...args: Parameters<typeof fetch>) =>
fetch(...args).then((res) => res.json())
/**
* Capitalizes first letters of words in string.
* @param {string} str String to be modified
* @param {boolean=false} lower Whether all other letters should be lowercased
* @return {string}
* @see https://stackoverflow.com/questions/2332811/capitalize-words-in-string/7592235#7592235
* @usage
* capitalize('fix this string'); // -> 'Fix This String'
* capitalize('javaSCrIPT'); // -> 'JavaSCrIPT'
* capitalize('javaSCrIPT', true); // -> 'Javascript'
*/
export const capitalize = (str: string, lower = false) =>
(lower ? str.toLowerCase() : str).replace(/(?:^|\s|["'([{])+\S/g, (match) =>
match.toUpperCase()
)
export function formatDate(input: string | number): string {
const date = new Date(input)
return date.toLocaleDateString("en-US", {
month: "long",
day: "numeric",
year: "numeric",
})
}
export const calculateReadingTime = (content: string): number => {
const words = content?.trim().split(/\s+/).filter(Boolean).length
return Math.max(1, Math.ceil(words / 200))
}
export const normalizeTag = (tag: unknown): string[] => {
if (!tag) return []
return Array.isArray(tag)
? tag.filter((t): t is string => typeof t === "string")
: [String(tag)]
}
export function absoluteUrl(path: string) {
return `${process.env.NEXT_PUBLIC_APP_URL}${path}`
}
export function constructMetadata({
title = "Gamma UI - Modern React + Tailwind CSS components & Templates",
description = "Beautifully designed landing page components built with React & Tailwind CSS & Motion.",
image = absoluteUrl("/og"),
...props
}: {
title?: string
description?: string
image?: string
[key: string]: Metadata[keyof Metadata]
}): Metadata {
return {
title,
description,
keywords: [
"GammaUI",
"UI Library",
"Component Library",
"React Components",
"Next.js",
"Tailwind CSS",
"Open Source",
"Frontend",
"Design System",
"Mazyar Kawa",
"GammaUI by Aman",
"Copy and Paste Components",
"Developer Tools",
"Frontend Engineer",
"Beautiful UI",
"React UI Kit",
"Free React Components",
"Open Source UI Kit",
"Tailwind UI Components",
"Headless UI",
"Reusable React Components",
"UI Templates",
"Accessible React Components",
"Copy-Paste UI",
"Next.js Component Library",
"Open Source Developer Tools",
"Frontend Design System",
"Minimal UI Kit",
"Clean React UI",
],
openGraph: {
title,
description,
type: "website",
images: [
{
url: image,
width: 1200,
height: 630,
},
],
},
twitter: {
card: "summary_large_image",
title,
description,
images: [image],
creator: "@mazyar_kawa",
},
icons: "/favicon.ico",
metadataBase: new URL("https://gammaui.com"),
authors: [
{
name: "mazyar_kawa",
url: "https://twitter.com/mazyar_kawa",
},
],
creator: "mazyar_kawa",
...props,
}
}
author/apps/www/app/globals.css
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: Open Sans, sans-serif;
--font-mono: Menlo, monospace;
--font-serif: Georgia, serif;
--radius: 1.3rem;
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
--tracking-normal: var(--tracking-normal);
--shadow-2xl: var(--shadow-2xl);
--shadow-xl: var(--shadow-xl);
--shadow-lg: var(--shadow-lg);
--shadow-md: var(--shadow-md);
--shadow: var(--shadow);
--shadow-sm: var(--shadow-sm);
--shadow-xs: var(--shadow-xs);
--shadow-2xs: var(--shadow-2xs);
--spacing: var(--spacing);
--letter-spacing: var(--letter-spacing);
--shadow-offset-y: var(--shadow-offset-y);
--shadow-offset-x: var(--shadow-offset-x);
--shadow-spread: var(--shadow-spread);
--shadow-blur: var(--shadow-blur);
--shadow-opacity: var(--shadow-opacity);
--color-shadow-color: var(--shadow-color);
--color-destructive-foreground: var(--destructive-foreground);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--animate-shimmer-slide: shimmer-slide var(--speed) ease-in-out infinite
alternate;
--animate-spin-around: spin-around calc(var(--speed) * 2) infinite linear;
@keyframes shimmer-slide {
to {
transform: translate(calc(100cqw - 100%), 0);
}
}
@keyframes spin-around {
0% {
transform: translateZ(0) rotate(0);
}
15%,
35% {
transform: translateZ(0) rotate(90deg);
}
65%,
85% {
transform: translateZ(0) rotate(270deg);
}
100% {
transform: translateZ(0) rotate(360deg);
}
}
}
:root {
--header-height: 64px;
--radius: 1.3rem;
--card: oklch(0.9784 0.0011 197.1387);
--card-foreground: oklch(0.1884 0.0128 248.5103);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.1884 0.0128 248.5103);
--primary: oklab(67.23% -0.06787 -0.14566);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.1884 0.0128 248.5103);
--secondary-foreground: oklch(1 0 0);
--muted: oklch(0.9222 0.0013 286.3737);
--muted-foreground: oklch(0.1884 0.0128 248.5103);
--accent: oklch(0.9392 0.0166 250.8453);
--accent-foreground: oklch(0.6723 0.1606 244.9955);
--destructive: oklch(0.6188 0.2376 25.7658);
--border: oklch(0.9317 0.0118 231.6594);
--input: oklch(0.9809 0.0025 228.7836);
--ring: oklch(0.6818 0.1584 243.354);
--chart-1: oklch(0.6723 0.1606 244.9955);
--chart-2: oklch(0.6907 0.1554 160.3454);
--chart-3: oklch(0.8214 0.16 82.5337);
--chart-4: oklch(0.7064 0.1822 151.7125);
--chart-5: oklch(0.5919 0.2186 10.5826);
--sidebar: oklch(0.9784 0.0011 197.1387);
--sidebar-foreground: oklch(0.1884 0.0128 248.5103);
--sidebar-primary: oklch(0.6723 0.1606 244.9955);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(0.9392 0.0166 250.8453);
--sidebar-accent-foreground: oklch(0.6723 0.1606 244.9955);
--sidebar-border: oklch(0.9271 0.0101 238.5177);
--sidebar-ring: oklch(0.6818 0.1584 243.354);
--destructive-foreground: oklch(1 0 0);
--font-sans: Open Sans, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Menlo, monospace;
--shadow-color: rgba(29, 161, 242, 0.15);
--shadow-opacity: 0;
--shadow-blur: 0px;
--shadow-spread: 0px;
--shadow-offset-x: 0px;
--shadow-offset-y: 2px;
--letter-spacing: 0em;
--spacing: 0.25rem;
--shadow-2xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-sm:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-md:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 2px 4px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-lg:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 4px 6px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xl:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 8px 10px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-2xl: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--tracking-normal: 0em;
--background: oklch(1 0 0);
--foreground: oklch(0.1884 0.0128 248.5103);
}
.dark {
--header-height: 64px;
--background: oklch(0 0 0);
--foreground: oklch(0.9328 0.0025 228.7857);
--card: oklch(0.2097 0.008 274.5332);
--card-foreground: oklch(0.8853 0 0);
--popover: oklch(0 0 0);
--popover-foreground: oklch(0.9328 0.0025 228.7857);
--primary: oklch(0.6692 0.1607 245.011);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.9622 0.0035 219.5331);
--secondary-foreground: oklch(0.1884 0.0128 248.5103);
--muted: oklch(0.209 0 0);
--muted-foreground: oklch(0.5637 0.0078 247.9662);
--accent: oklch(0.1928 0.0331 242.5459);
--accent-foreground: oklch(0.6692 0.1607 245.011);
--destructive: oklch(0.6188 0.2376 25.7658);
--border: oklch(0.2674 0.0047 248.0045);
--input: oklch(0.302 0.0288 244.8244);
--ring: oklch(0.6818 0.1584 243.354);
--chart-1: oklch(0.6723 0.1606 244.9955);
--chart-2: oklch(0.6907 0.1554 160.3454);
--chart-3: oklch(0.8214 0.16 82.5337);
--chart-4: oklch(0.7064 0.1822 151.7125);
--chart-5: oklch(0.5919 0.2186 10.5826);
--sidebar: oklch(0.2097 0.008 274.5332);
--sidebar-foreground: oklch(0.8853 0 0);
--sidebar-primary: oklch(0.6818 0.1584 243.354);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(0.1928 0.0331 242.5459);
--sidebar-accent-foreground: oklch(0.6692 0.1607 245.011);
--sidebar-border: oklch(0.3795 0.022 240.5943);
--sidebar-ring: oklch(0.6818 0.1584 243.354);
--destructive-foreground: oklch(1 0 0);
--radius: 1.3rem;
--font-sans: Open Sans, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Menlo, monospace;
--shadow-color: rgba(29, 161, 242, 0.25);
--shadow-opacity: 0;
--shadow-blur: 0px;
--shadow-spread: 0px;
--shadow-offset-x: 0px;
--shadow-offset-y: 2px;
--letter-spacing: 0em;
--spacing: 0.25rem;
--shadow-2xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-sm:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-md:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 2px 4px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-lg:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 4px 6px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xl:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 8px 10px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-2xl: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
letter-spacing: var(--tracking-normal);
}
}
.no-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
.no-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
.radial-gradient {
background: radial-gradient(
circle at 50% 0%,
rgba(250, 250, 250, 0.05) 0%,
transparent 60%
)
rgba(15, 15, 15, 1);
transition: background 0.3s ease-in-out;
width: fit-content;
}
.radial-gradient:hover {
background: radial-gradient(
circle at 50% 0%,
rgba(250, 250, 250, 0.05) 0%,
transparent 60%
)
rgba(15, 15, 15, 0.5);
}
.radial-gradient-border-button {
background: radial-gradient(
circle at 50% 0%,
rgba(250, 250, 250, 0.05) 0%,
transparent 60%
)
rgba(15, 15, 15, 1);
transition: background 0.3s ease-in-out;
width: fit-content;
}
.linear-mask {
mask-image: linear-gradient(
-75deg,
white calc(var(--x) + 20%),
transparent calc(var(--x) + 30%),
white calc(var(--x) + 100%)
);
-webkit-mask-image: linear-gradient(
-75deg,
white calc(var(--x) + 20%),
transparent calc(var(--x) + 30%),
white calc(var(--x) + 100%)
);
}
.linear-overlay {
background-image: linear-gradient(
-75deg,
rgba(255, 255, 255, 0.1) calc(var(--x) + 20%),
rgba(255, 255, 255, 0.5) calc(var(--x) + 25%),
rgba(255, 255, 255, 0.1) calc(var(--x) + 100%)
);
mask:
linear-gradient(black, black) content-box,
linear-gradient(black, black);
-webkit-mask:
linear-gradient(black, black) content-box,
linear-gradient(black, black);
mask-composite: exclude;
-webkit-mask-composite: xor;
padding: 1px;
}
@keyframes move-x {
from {
transform: translateX(var(--move-x-from));
}
to {
transform: translateX(var(--move-x-to));
}
}
.cpu-architecture {
offset-anchor: 10px 0px;
animation: animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.75, -0.01, 0, 0.99);
}
.cpu-line-1 {
offset-path: path("M 10 20 h 79.5 q 5 0 5 5 v 30");
animation-duration: 5s;
animation-delay: 1s;
}
.cpu-line-2 {
offset-path: path("M 180 10 h -69.7 q -5 0 -5 5 v 40");
animation-delay: 6s;
animation-duration: 2s;
}
.cpu-line-3 {
offset-path: path("M 130 20 v 21.8 q 0 5 -5 5 h -25");
animation-delay: 4s;
animation-duration: 6s;
}
.cpu-line-4 {
offset-path: path("M 170 80 v -21.8 q 0 -5 -5 -5 h -65");
animation-delay: 3s;
animation-duration: 3s;
}
.cpu-line-5 {
offset-path: path(
"M 135 65 h 15 q 5 0 5 5 v 10 q 0 5 -5 5 h -39.8 q -5 0 -5 -5 v -35"
);
animation-delay: 9s;
animation-duration: 4s;
}
.cpu-line-6 {
offset-path: path("M 94.8 95 v -46");
animation-delay: 3s;
animation-duration: 7s;
}
.cpu-line-7 {
offset-path: path(
"M 88 88 v -15 q 0 -5 -5 -5 h -10 q -5 0 -5 -5 v -5 q 0 -5 5 -5 h 28"
);
animation-delay: 4s;
animation-duration: 4s;
}
.cpu-line-8 {
offset-path: path("M 30 30 h 25 q 5 0 5 5 v 6.5 q 0 5 5 5 h 35");
animation-delay: 3s;
animation-duration: 3s;
}
@keyframes animation-path {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
.shadcn {
offset-anchor: 10px 0px;
animation: shadcn-animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.75, -0.01, 0, 0.99);
animation-duration: 4s;
animation-delay: 1.5s;
}
.shadcn-line-1 {
offset-path: path("M 35 14 h 25 q 5 0 5 5 v 23 q 0 5 5 5 h 25");
}
.shadcn-line-2 {
offset-path: path("M 30 50 h 65");
}
.shadcn-line-3 {
offset-path: path("M 36.3 87 h 24 q 5 0 5 -5 v -24 q 0 -5 5 -5 h 25");
}
.compBox {
animation: shadncn-animation-path-backwards;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.57, 0.02, 0, 1);
offset-path: path("M 167 36 h -17 q -5 0 -5 5 v 4 q 0 5 -5 5 h -32");
animation-duration: 4s;
opacity: 0;
animation-delay: 5s;
}
@keyframes shadncn-animation-path-backwards {
0% {
offset-distance: 100%;
opacity: 0;
}
90% {
opacity: 1;
offset-distance: 0%;
}
100% {
opacity: 0;
}
}
@keyframes shadcn-animation-path {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
@keyframes landing-marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
.animate-landing-marquee {
animation: landing-marquee var(--marquee-duration, 45s) linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.animate-landing-marquee {
animation: none;
}
}
author/apps/www/package.json
{
"name": "www",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"preinstall": "npx only-allow bun",
"postinstall": "fumadocs-mdx",
"dev": "next dev",
"prebuild": "bun run build:registry",
"build": "next build",
"build:registry": "npx tsx ./scripts/build-registry.mts && prettier --log-level silent --write \"registry/**/*.{ts,tsx,mdx}\" --cache || true",
"shadcn:build": "npx shadcn build registry.json --output ../www/public/r",
"typecheck": "tsc --noEmit",
"start": "next start",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"format:fix": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache",
"format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache",
"check": "bun run lint && bun run typecheck && bun run format:check"
},
"dependencies": {
"@number-flow/react": "^0.5.10",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@react-three/fiber": "^9.6.1",
"@react-three/postprocessing": "^3.0.4",
"@tabler/icons-react": "^3.35.0",
"@types/react-syntax-highlighter": "^15.5.13",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"front-matter": "^4.0.2",
"fumadocs-core": "^16.2.5",
"fumadocs-mdx": "^13.0.5",
"jotai": "^2.15.1",
"lucide-react": "^0.552.0",
"motion": "^12.23.24",
"next": "15.2.6",
"next-themes": "^0.4.6",
"postprocessing": "^6.39.2",
"prettier-plugin-tailwindcss": "^0.7.1",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-syntax-highlighter": "^16.1.0",
"rehype-pretty-code": "^0.14.1",
"schema-dts": "^1.1.5",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"three": "^0.185.1",
"ts-morph": "^27.0.2",
"zod": "^4.1.13"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.39.1",
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
"@shikijs/transformers": "^3.20.0",
"@tailwindcss/postcss": "^4.0.0",
"@types/node": "^20.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/three": "^0.185.0",
"@typescript-eslint/eslint-plugin": "^8.47.0",
"@typescript-eslint/parser": "^8.47.0",
"eslint": "^9.39.2",
"eslint-config-next": "^16.0.10",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"lefthook": "^2.0.4",
"prettier": "^3.6.2",
"rimraf": "^6.1.2",
"shiki": "^3.20.0",
"tailwindcss": "^4.0.0",
"tw-animate-css": "^1.4.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.47.0"
},
"engines": {
"bun": ">=1.1.43"
},
"prettier": {
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"importOrder": [
"^(react/(.*)$)|^(react$)",
"^(next/(.*)$)|^(next$)",
"<THIRD_PARTY_MODULES>",
"",
"^@workspace/(.*)$",
"",
"^types$",
"^@/types/(.*)$",
"^@/config/(.*)$",
"^@/lib/(.*)$",
"^@/hooks/(.*)$",
"^@/components/ui/(.*)$",
"^@/components/(.*)$",
"^@/registry/(.*)$",
"^@/styles/(.*)$",
"^@/app/(.*)$",
"^@/www/(.*)$",
"",
"^[./]"
],
"importOrderParserPlugins": [
"typescript",
"jsx",
"decorators-legacy"
],
"plugins": [
"@ianvs/prettier-plugin-sort-imports",
"prettier-plugin-tailwindcss"
]
}
}
Usage.tsx실행 안내·자료
// Local host for the unchanged exact baseline demonstration.
import OriginalDemo from "./baseline-example.tsx";
export default function Demo() { return <><OriginalDemo /></>; }
runtime/author-mount.tsx실행 안내·자료
/** Local sandbox host. Ready is a mount observation, never a verification result. */
import React, { Component, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
declare global {
interface Window {
__STYLEGALLERY_PREVIEW__: { id: string; status: string; errors: string[] };
}
}
export function mount(Demo: React.ComponentType, id: string) {
const state = window.__STYLEGALLERY_PREVIEW__ = { id, status: 'loading', errors: [] as string[] };
const send = (message: object) => parent.postMessage({ ...message, id }, '*');
const report = (error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
if (!state.errors.includes(message)) state.errors.push(message);
state.status = 'error';
document.body.dataset.previewStatus = 'error';
send({ type: 'sg-preview-error', message });
};
window.addEventListener('error', (event) => report(event.error ?? event.message));
window.addEventListener('unhandledrejection', (event) => report(event.reason));
window.addEventListener('securitypolicyviolation', (event) => report(`Blocked by preview policy: ${event.violatedDirective}`));
const theme = new URLSearchParams(location.search).get('theme') === 'dark' ? 'dark' : 'light';
document.documentElement.classList.toggle('dark', theme === 'dark');
document.documentElement.dataset.theme = theme;
// Keep original source bytes while ensuring imported attribution/navigation remains local text.
const removeDestinations = () => document.querySelectorAll('a[href]').forEach((link) => {
link.removeAttribute('href');
link.removeAttribute('target');
});
document.addEventListener('click', (event) => {
if ((event.target as Element)?.closest?.('a')) event.preventDefault();
}, true);
document.addEventListener('submit', (event) => event.preventDefault());
new MutationObserver(removeDestinations).observe(document.getElementById('root')!, { childList: true, subtree: true });
const observe = () => {
const root = document.getElementById('root')!;
const elements = Array.from(root.querySelectorAll('*'));
const rect = root.getBoundingClientRect();
const diagnostics = {
textLength: (root.textContent ?? '').trim().length,
elementCount: elements.length,
visibleElementCount: elements.filter((element) => {
const bounds = element.getBoundingClientRect();
const style = getComputedStyle(element);
return bounds.width > 0 && bounds.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0;
}).length,
images: [],
canvases: [],
rootRect: { width: rect.width, height: rect.height },
documentWidth: document.documentElement.scrollWidth,
viewportWidth: innerWidth,
};
send({ type: 'sg-preview-observation', diagnostics });
};
class Boundary extends Component<{ children: React.ReactNode }, { error: string | null }> {
state = { error: null as string | null };
static getDerivedStateFromError(error: Error) { return { error: error.message }; }
componentDidCatch(error: Error) { report(error); }
render() { return this.state.error ? <p role="alert">This preview could not render: {this.state.error}</p> : this.props.children; }
}
function Ready() {
useEffect(() => {
const preference = matchMedia('(prefers-reduced-motion: reduce)');
const syncSVG = () => document.querySelectorAll('svg').forEach((svg) => {
if (preference.matches) svg.pauseAnimations?.();
else svg.unpauseAnimations?.();
});
syncSVG();
preference.addEventListener('change', syncSVG);
let second = 0;
let delayed = 0;
const first = requestAnimationFrame(() => { second = requestAnimationFrame(() => {
if (state.status !== 'error') {
state.status = 'mounted';
document.body.dataset.previewStatus = 'mounted';
send({ type: 'sg-preview-ready' });
observe();
delayed = window.setTimeout(observe, 700);
}
}); });
return () => {
cancelAnimationFrame(first);
cancelAnimationFrame(second);
clearTimeout(delayed);
preference.removeEventListener('change', syncSVG);
};
}, []);
return <Demo />;
}
createRoot(document.getElementById('root')!).render(<Boundary><Ready /></Boundary>);
}
runtime/author-styles.css실행 안내·자료
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--background);
--color-card-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--muted);
--color-secondary-foreground: var(--foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--muted);
--color-accent-foreground: var(--foreground);
--color-popover: var(--background);
--color-popover-foreground: var(--foreground);
--color-destructive: #dc2626;
--color-destructive-foreground: #ffffff;
--color-input: var(--border);
--color-border: var(--border);
--color-ring: var(--foreground);
--radius-lg: 0.5rem;
--radius-md: 0.375rem;
--radius-sm: 0.25rem;
}
:root {
--background: #ffffff;
--border: #d4d4d8;
--foreground: #18181b;
--muted: #f4f4f5;
--muted-foreground: #71717a;
--primary: #18181b;
--primary-foreground: #fafafa;
background: var(--background);
color: var(--foreground);
color-scheme: light;
font-family: Arial, sans-serif;
}
.dark {
--background: #09090b;
--border: #3f3f46;
--foreground: #fafafa;
--muted: #27272a;
--muted-foreground: #a1a1aa;
--primary: #fafafa;
--primary-foreground: #18181b;
color-scheme: dark;
}
body {
margin: 0;
min-height: 100vh;
}
#root {
align-items: center;
box-sizing: border-box;
display: flex;
justify-content: center;
min-height: 100vh;
padding: 24px;
width: 100%;
}
#root > * {
max-width: 100%;
}
noscript {
display: block;
padding: 24px;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-play-state: paused !important;
}
}
runtime/applied-theme.css실행 안내·자료
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: Open Sans, sans-serif;
--font-mono: Menlo, monospace;
--font-serif: Georgia, serif;
--radius: 1.3rem;
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
--tracking-normal: var(--tracking-normal);
--shadow-2xl: var(--shadow-2xl);
--shadow-xl: var(--shadow-xl);
--shadow-lg: var(--shadow-lg);
--shadow-md: var(--shadow-md);
--shadow: var(--shadow);
--shadow-sm: var(--shadow-sm);
--shadow-xs: var(--shadow-xs);
--shadow-2xs: var(--shadow-2xs);
--spacing: var(--spacing);
--letter-spacing: var(--letter-spacing);
--shadow-offset-y: var(--shadow-offset-y);
--shadow-offset-x: var(--shadow-offset-x);
--shadow-spread: var(--shadow-spread);
--shadow-blur: var(--shadow-blur);
--shadow-opacity: var(--shadow-opacity);
--color-shadow-color: var(--shadow-color);
--color-destructive-foreground: var(--destructive-foreground);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--animate-shimmer-slide: shimmer-slide var(--speed) ease-in-out infinite
alternate;
--animate-spin-around: spin-around calc(var(--speed) * 2) infinite linear;
@keyframes shimmer-slide {
to {
transform: translate(calc(100cqw - 100%), 0);
}
}
@keyframes spin-around {
0% {
transform: translateZ(0) rotate(0);
}
15%,
35% {
transform: translateZ(0) rotate(90deg);
}
65%,
85% {
transform: translateZ(0) rotate(270deg);
}
100% {
transform: translateZ(0) rotate(360deg);
}
}
}
:root {
--header-height: 64px;
--radius: 1.3rem;
--card: oklch(0.9784 0.0011 197.1387);
--card-foreground: oklch(0.1884 0.0128 248.5103);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.1884 0.0128 248.5103);
--primary: oklab(67.23% -0.06787 -0.14566);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.1884 0.0128 248.5103);
--secondary-foreground: oklch(1 0 0);
--muted: oklch(0.9222 0.0013 286.3737);
--muted-foreground: oklch(0.1884 0.0128 248.5103);
--accent: oklch(0.9392 0.0166 250.8453);
--accent-foreground: oklch(0.6723 0.1606 244.9955);
--destructive: oklch(0.6188 0.2376 25.7658);
--border: oklch(0.9317 0.0118 231.6594);
--input: oklch(0.9809 0.0025 228.7836);
--ring: oklch(0.6818 0.1584 243.354);
--chart-1: oklch(0.6723 0.1606 244.9955);
--chart-2: oklch(0.6907 0.1554 160.3454);
--chart-3: oklch(0.8214 0.16 82.5337);
--chart-4: oklch(0.7064 0.1822 151.7125);
--chart-5: oklch(0.5919 0.2186 10.5826);
--sidebar: oklch(0.9784 0.0011 197.1387);
--sidebar-foreground: oklch(0.1884 0.0128 248.5103);
--sidebar-primary: oklch(0.6723 0.1606 244.9955);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(0.9392 0.0166 250.8453);
--sidebar-accent-foreground: oklch(0.6723 0.1606 244.9955);
--sidebar-border: oklch(0.9271 0.0101 238.5177);
--sidebar-ring: oklch(0.6818 0.1584 243.354);
--destructive-foreground: oklch(1 0 0);
--font-sans: Open Sans, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Menlo, monospace;
--shadow-color: rgba(29, 161, 242, 0.15);
--shadow-opacity: 0;
--shadow-blur: 0px;
--shadow-spread: 0px;
--shadow-offset-x: 0px;
--shadow-offset-y: 2px;
--letter-spacing: 0em;
--spacing: 0.25rem;
--shadow-2xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-sm:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-md:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 2px 4px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-lg:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 4px 6px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xl:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 8px 10px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-2xl: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--tracking-normal: 0em;
--background: oklch(1 0 0);
--foreground: oklch(0.1884 0.0128 248.5103);
}
.dark {
--header-height: 64px;
--background: oklch(0 0 0);
--foreground: oklch(0.9328 0.0025 228.7857);
--card: oklch(0.2097 0.008 274.5332);
--card-foreground: oklch(0.8853 0 0);
--popover: oklch(0 0 0);
--popover-foreground: oklch(0.9328 0.0025 228.7857);
--primary: oklch(0.6692 0.1607 245.011);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.9622 0.0035 219.5331);
--secondary-foreground: oklch(0.1884 0.0128 248.5103);
--muted: oklch(0.209 0 0);
--muted-foreground: oklch(0.5637 0.0078 247.9662);
--accent: oklch(0.1928 0.0331 242.5459);
--accent-foreground: oklch(0.6692 0.1607 245.011);
--destructive: oklch(0.6188 0.2376 25.7658);
--border: oklch(0.2674 0.0047 248.0045);
--input: oklch(0.302 0.0288 244.8244);
--ring: oklch(0.6818 0.1584 243.354);
--chart-1: oklch(0.6723 0.1606 244.9955);
--chart-2: oklch(0.6907 0.1554 160.3454);
--chart-3: oklch(0.8214 0.16 82.5337);
--chart-4: oklch(0.7064 0.1822 151.7125);
--chart-5: oklch(0.5919 0.2186 10.5826);
--sidebar: oklch(0.2097 0.008 274.5332);
--sidebar-foreground: oklch(0.8853 0 0);
--sidebar-primary: oklch(0.6818 0.1584 243.354);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(0.1928 0.0331 242.5459);
--sidebar-accent-foreground: oklch(0.6692 0.1607 245.011);
--sidebar-border: oklch(0.3795 0.022 240.5943);
--sidebar-ring: oklch(0.6818 0.1584 243.354);
--destructive-foreground: oklch(1 0 0);
--radius: 1.3rem;
--font-sans: Open Sans, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Menlo, monospace;
--shadow-color: rgba(29, 161, 242, 0.25);
--shadow-opacity: 0;
--shadow-blur: 0px;
--shadow-spread: 0px;
--shadow-offset-x: 0px;
--shadow-offset-y: 2px;
--letter-spacing: 0em;
--spacing: 0.25rem;
--shadow-2xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xs: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-sm:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 1px 2px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-md:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 2px 4px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-lg:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 4px 6px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-xl:
0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0),
0px 8px 10px -1px hsl(202.8169 89.1213% 53.1373% / 0);
--shadow-2xl: 0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
letter-spacing: var(--tracking-normal);
}
}
.no-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
.no-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
.radial-gradient {
background: radial-gradient(
circle at 50% 0%,
rgba(250, 250, 250, 0.05) 0%,
transparent 60%
)
rgba(15, 15, 15, 1);
transition: background 0.3s ease-in-out;
width: fit-content;
}
.radial-gradient:hover {
background: radial-gradient(
circle at 50% 0%,
rgba(250, 250, 250, 0.05) 0%,
transparent 60%
)
rgba(15, 15, 15, 0.5);
}
.radial-gradient-border-button {
background: radial-gradient(
circle at 50% 0%,
rgba(250, 250, 250, 0.05) 0%,
transparent 60%
)
rgba(15, 15, 15, 1);
transition: background 0.3s ease-in-out;
width: fit-content;
}
.linear-mask {
mask-image: linear-gradient(
-75deg,
white calc(var(--x) + 20%),
transparent calc(var(--x) + 30%),
white calc(var(--x) + 100%)
);
-webkit-mask-image: linear-gradient(
-75deg,
white calc(var(--x) + 20%),
transparent calc(var(--x) + 30%),
white calc(var(--x) + 100%)
);
}
.linear-overlay {
background-image: linear-gradient(
-75deg,
rgba(255, 255, 255, 0.1) calc(var(--x) + 20%),
rgba(255, 255, 255, 0.5) calc(var(--x) + 25%),
rgba(255, 255, 255, 0.1) calc(var(--x) + 100%)
);
mask:
linear-gradient(black, black) content-box,
linear-gradient(black, black);
-webkit-mask:
linear-gradient(black, black) content-box,
linear-gradient(black, black);
mask-composite: exclude;
-webkit-mask-composite: xor;
padding: 1px;
}
@keyframes move-x {
from {
transform: translateX(var(--move-x-from));
}
to {
transform: translateX(var(--move-x-to));
}
}
.cpu-architecture {
offset-anchor: 10px 0px;
animation: animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.75, -0.01, 0, 0.99);
}
.cpu-line-1 {
offset-path: path("M 10 20 h 79.5 q 5 0 5 5 v 30");
animation-duration: 5s;
animation-delay: 1s;
}
.cpu-line-2 {
offset-path: path("M 180 10 h -69.7 q -5 0 -5 5 v 40");
animation-delay: 6s;
animation-duration: 2s;
}
.cpu-line-3 {
offset-path: path("M 130 20 v 21.8 q 0 5 -5 5 h -25");
animation-delay: 4s;
animation-duration: 6s;
}
.cpu-line-4 {
offset-path: path("M 170 80 v -21.8 q 0 -5 -5 -5 h -65");
animation-delay: 3s;
animation-duration: 3s;
}
.cpu-line-5 {
offset-path: path(
"M 135 65 h 15 q 5 0 5 5 v 10 q 0 5 -5 5 h -39.8 q -5 0 -5 -5 v -35"
);
animation-delay: 9s;
animation-duration: 4s;
}
.cpu-line-6 {
offset-path: path("M 94.8 95 v -46");
animation-delay: 3s;
animation-duration: 7s;
}
.cpu-line-7 {
offset-path: path(
"M 88 88 v -15 q 0 -5 -5 -5 h -10 q -5 0 -5 -5 v -5 q 0 -5 5 -5 h 28"
);
animation-delay: 4s;
animation-duration: 4s;
}
.cpu-line-8 {
offset-path: path("M 30 30 h 25 q 5 0 5 5 v 6.5 q 0 5 5 5 h 35");
animation-delay: 3s;
animation-duration: 3s;
}
@keyframes animation-path {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
.shadcn {
offset-anchor: 10px 0px;
animation: shadcn-animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.75, -0.01, 0, 0.99);
animation-duration: 4s;
animation-delay: 1.5s;
}
.shadcn-line-1 {
offset-path: path("M 35 14 h 25 q 5 0 5 5 v 23 q 0 5 5 5 h 25");
}
.shadcn-line-2 {
offset-path: path("M 30 50 h 65");
}
.shadcn-line-3 {
offset-path: path("M 36.3 87 h 24 q 5 0 5 -5 v -24 q 0 -5 5 -5 h 25");
}
.compBox {
animation: shadncn-animation-path-backwards;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.57, 0.02, 0, 1);
offset-path: path("M 167 36 h -17 q -5 0 -5 5 v 4 q 0 5 -5 5 h -32");
animation-duration: 4s;
opacity: 0;
animation-delay: 5s;
}
@keyframes shadncn-animation-path-backwards {
0% {
offset-distance: 100%;
opacity: 0;
}
90% {
opacity: 1;
offset-distance: 0%;
}
100% {
opacity: 0;
}
}
@keyframes shadcn-animation-path {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
@keyframes landing-marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
.animate-landing-marquee {
animation: landing-marquee var(--marquee-duration, 45s) linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.animate-landing-marquee {
animation: none;
}
}
runtime/process.ts실행 안내·자료
// Browser host for a Node-only import in unused upstream documentation helpers.
// Actual components consume the unchanged cn export; no provider environment is exposed.
export default { env: {} };
THIRD-PARTY-LICENSES.txt실행 안내·자료
clsx 2.1.1 — license
MIT License
Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
tailwind-merge 3.3.1 — LICENSE.md
MIT License
Copyright (c) 2021 Dany Castillo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
react 19.2.3 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
scheduler 0.27.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
react-dom 19.2.3 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
tailwindcss 4.1.13 — LICENSE
MIT License
Copyright (c) Tailwind Labs, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
tw-animate-css 1.3.8 — LICENSE
MIT License
Copyright (c) 2025 Wombosvideo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
provenance.json실행 안내·자료
{
"id": "21st-5274a680d285",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"demoIdentity": {
"file": "baseline-example.tsx",
"export": "default",
"source": "exact-public-demo"
},
"fidelity": {
"preserved": "21st 공개 discovery의 고유 데모 ID와 미리보기 이미지가 가리키는 원래 데모 바이트 전체",
"dependency_revision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"observed_differences": [
"원래 CDN demo의 레이아웃 및 인수를 유지하고, 별도 component는 조사 시점 고정 작성자 revision에서 연결한다."
],
"mapping": [
"21st 설치 별칭 @/components/ui/cpu-architecture → 작성자 registry/gammaui/cpu-architecture.tsx; 문서 ComponentSource의 이름과 export/props 대조"
]
},
"acquisitionLimitations": [
"CDN 데모는 SHA256으로, 작성자 원본과 의존 파일은 Git 커밋 및 blob SHA로 고정했다.",
"현재 작성자 revision의 의존 파일이 과거 21st 내부 구현과 바이트 단위로 같다는 주장은 하지 않는다.",
"다운로드한 코드를 실행하지 않았으며 브라우저·상호작용 검증은 별도 단계다."
],
"files": [
{
"file": "baseline-example.tsx",
"kind": "implementation",
"sha256": "ba9a16fb30b73c6fd3d7bab24e622b63c5a078c578157061187c5d644926c4a0",
"sourceRevision": "sha256:ba9a16fb30b73c6fd3d7bab24e622b63c5a078c578157061187c5d644926c4a0",
"license": "MIT"
},
{
"file": "LICENSE",
"kind": "license",
"sha256": "185f2cd440066381aa1fb70411e11e6c288a961a91bc5274e92a72ee245f83ed",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"license": "MIT"
},
{
"file": "upstream/apps/www/content/docs/components/cpu-architecture.mdx",
"kind": "upstream-implementation",
"sha256": "885cbe70b108751d5b756eb898429779dbb6e9a22053b205636028279094c359",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"license": "MIT"
},
{
"file": "upstream/apps/www/registry/example/cpu-architecture-demo.tsx",
"kind": "upstream-implementation",
"sha256": "f3a0926604ac1f4408892983bb3b5ccf588c4486131bc5e58f4eb43d1f28795b",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"license": "MIT"
},
{
"file": "author/apps/www/registry/gammaui/cpu-architecture.tsx",
"kind": "implementation-dependency",
"sha256": "0e49978e092e97a09343c17f6697148bab18602a6f8c7e022b4db97964029501",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"license": "MIT"
},
{
"file": "author/apps/www/lib/utils.ts",
"kind": "implementation-dependency",
"sha256": "498451813b574b01a0eac9a1d5a96dfa261218ad55fff92ffc5afbf9fe785e22",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"license": "MIT"
},
{
"file": "author/apps/www/app/globals.css",
"kind": "theme-source",
"sha256": "d48b78e343a4a866a9ccc1137ca3ac2418ebf221bf52ab79d70759426f091628",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"license": "MIT"
},
{
"file": "author/apps/www/package.json",
"kind": "dependency-manifest",
"sha256": "f3307ab08f84659d3850ff0d9aa47e10999581e3536af88acef876b1cd1099fa",
"sourceRevision": "c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd",
"license": "MIT"
}
],
"importMap": {
"@/components/ui/cpu-architecture": "author/apps/www/registry/gammaui/cpu-architecture.tsx",
"@/lib/utils": "author/apps/www/lib/utils.ts"
},
"declaredDependencies": {
"clsx": "^2.1.1",
"next": "15.2.6",
"process": null,
"react": "^19.2.3",
"tailwind-merge": "^3.3.1"
},
"runtimeDependencies": {
"clsx": "2.1.1",
"tailwind-merge": "3.3.1",
"react": "19.2.3",
"scheduler": "0.27.0",
"react-dom": "19.2.3",
"tailwindcss": "4.1.13",
"tw-animate-css": "1.3.8"
},
"assets": [],
"assetAdaptations": [],
"adaptations": [
"The exact original SVG demonstration and author CSS motion paths are unchanged.",
"An empty browser process shim is supplied only for the imported documentation utility module."
],
"runtime_verified": false
}
README.md실행 안내·자료
# CPU Architecture · Gamma UI
The acquired original files are unchanged. Source revision: c79ba85279e4ff05a85b5ea4c2a8282c3467b6bd. Each exact file hash and any demo content revision is recorded in provenance.json. Keep all included license notices.
- The exact original SVG demonstration and author CSS motion paths are unchanged.
- An empty browser process shim is supplied only for the imported documentation utility module.
- Host spacing and fallback tokens come from runtime/author-styles.css. runtime/applied-theme.css contains the selected original author theme and overrides fallback tokens.
Open the standalone HTML to run with all JavaScript and CSS bundled locally. ?theme=light and ?theme=dark select the host theme. The parent gallery must use an opaque allow-scripts sandbox. No external requests are required.
For source reuse, start with Usage.tsx and the unchanged baseline demonstration. Resolve @/ imports to their included matching author files and install the exact package versions in provenance.json. The host mount and styles are supplied for reference.
Build success and the ready message do not prove runtime or accessibility behavior. Browser validation is recorded separately.
BEHIND THE EXAMPLE
이 장면을 만드는 원리
CSS 광점과 SVG 선의 별도 시간 경로
효과를 구동하는 경로를 나누어 읽고 각 경로의 종료·정지 책임을 확인합니다.
- 이 예제에서는
- CpuArchitecture는 viewBox 0 0 200 100의 SVG입니다. 선의 시작 그리기는 SVG animate의 stroke-dashoffset이고, 여덟 광점은 작성자 globals.css의 offset-path와 animation-path를 사용합니다. CPU 문자의 그라데이션 stop에도 SVG animate가 있습니다. 전체가 하나의 애니메이션 제어기를 사용하는 구조는 아닙니다.
코드와 함께 확인하기
코드에서 찾기
attributeName="stroke-dashoffset"cpu-architecture.tsx선의 등장에는 SVG 시간 경로를 사용합니다.
@keyframes animation-path {globals.css광점 위치를 offset-distance로 이동시키는 CSS 시간 경로입니다.
attributeName="offset"cpu-architecture.tsx글자 그라데이션도 독립된 SVG 시간 경로를 가집니다.
직접 해보기
CSS 애니메이션만 정지한 경우와 SVG 시간을 정지한 경우를 별도로 비교합니다.
살펴볼 변화광점·선·글자 중 무엇이 멈추는지 구분해야 합니다. 한 경로의 정지를 전체 효과 정지로 보고하지 않습니다.
세 animate prop은 전체 정지 스위치가 아니다
모션 선호를 반영할 때는 모든 재생 경로를 대상으로 현재 정보를 보존하는 정책을 정합니다.
- 이 예제에서는
- animateLines는 선의 처음 1초 그리기만, animateMarkers는 마커 반지름 애니메이션만 조건부로 넣습니다. animateText=false는 글자를 흰색으로 바꾸지만 defs의 반복 animate 노드는 남습니다. 광점의 CSS 클래스는 세 prop과 무관하게 렌더링됩니다. 기본 데모는 세 값을 모두 기본 true로 사용합니다.
코드와 함께 확인하기
코드에서 찾기
{animateLines && (cpu-architecture.tsx조건부 제어 범위는 선의 시작 애니메이션입니다.
fill={animateText ? "url(#cpu-text-gradient)" : "white"}cpu-architecture.tsx텍스트 설정은 글자의 채움 연결을 바꿉니다.
className="cpu-architecture cpu-line-1"cpu-architecture.tsx광점의 CSS 경로는 prop 조건문 밖에 있습니다.
직접 해보기
별도 소비 코드에서 세 animate prop을 모두 false로 전달합니다.
살펴볼 변화광점 이동과 defs의 SVG 시간 경로까지 중단되는지 확인합니다. 세 prop만으로 움직임 감소 지원이 완성된다고 주장하지 않습니다.
재생 중 시스템의 움직임 감소 선호를 바꿉니다.
살펴볼 변화소비 호스트가 CSS와 SVG를 함께 다루는지 확인하고, 정지 뒤 CPU와 연결선 정보가 남는지도 기록합니다.
여러 SVG를 함께 넣을 때의 식별자 계약
컴포넌트의 내부 참조와 접근 가능한 의미는 배치되는 문서 범위에서 검증합니다.
- 이 예제에서는
- 마스크·그라데이션·필터·마커는 cpu-mask-1 같은 고정 ID와 url(#...) 참조를 사용합니다. 인스턴스별 접두사는 없고 svg에는 title·desc·접근 가능한 이름을 직접 선언하지 않습니다. 이 그림을 장식으로 둘지 설명이 필요한 CPU 구조로 둘지는 소비 제품이 정해야 합니다.
코드와 함께 확인하기
코드에서 찾기
<mask id="cpu-mask-1">cpu-architecture.tsx마스크 식별자는 모든 인스턴스에 같은 문자열입니다.
markerStart="url(#cpu-circle-marker)"cpu-architecture.tsxSVG 내부 리소스를 문서 ID로 참조합니다.
직접 해보기
같은 문서에 CPU 두 개를 다른 text·lineMarkerSize 값으로 배치합니다.
살펴볼 변화중복 ID와 각 참조가 어느 인스턴스를 가리키는지 확인합니다. iframe 한 개에서의 결과를 같은 문서 다중 배치로 확대하지 않습니다.
CPU 그림을 장식으로 쓰는 경우와 설명 자료로 쓰는 경우의 접근성 트리를 각각 검토합니다.
살펴볼 변화소비 제품이 정한 의미에 맞는 숨김 또는 이름·설명이 있어야 합니다. 현재 원본의 CPU 글자만으로 충분한 설명을 제공한다고 단정하지 않습니다.
