@oalacea/chaosui 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +75 -13
- package/components/backgrounds/glow-orbs/index.tsx +1 -1
- package/components/buttons/chaos-button/chaos-button.module.css +3 -2
- package/components/buttons/glitch-button/glitch-button.module.css +7 -7
- package/components/chaos-vars.css +27 -0
- package/components/cyber/cyber-avatar/css/cyber-avatar.module.css +60 -0
- package/components/cyber/cyber-avatar/css/index.tsx +28 -0
- package/components/cyber/cyber-avatar/tailwind/index.tsx +46 -0
- package/components/cyber/cyber-input/css/cyber-input.module.css +87 -0
- package/components/cyber/cyber-input/css/index.tsx +49 -0
- package/components/cyber/cyber-input/tailwind/index.tsx +55 -0
- package/components/cyber/cyber-loader/css/cyber-loader.module.css +102 -0
- package/components/cyber/cyber-loader/css/index.tsx +58 -0
- package/components/cyber/cyber-loader/tailwind/index.tsx +63 -0
- package/components/cyber/cyber-modal/css/cyber-modal.module.css +124 -0
- package/components/cyber/cyber-modal/css/index.tsx +75 -0
- package/components/cyber/cyber-modal/tailwind/index.tsx +87 -0
- package/components/cyber/cyber-slider/css/cyber-slider.module.css +61 -0
- package/components/cyber/cyber-slider/css/index.tsx +41 -0
- package/components/cyber/cyber-slider/tailwind/index.tsx +51 -0
- package/components/cyber/cyber-tooltip/css/cyber-tooltip.module.css +67 -0
- package/components/cyber/cyber-tooltip/css/index.tsx +36 -0
- package/components/cyber/cyber-tooltip/tailwind/index.tsx +48 -0
- package/components/effects/glitch-image/css/glitch-image.module.css +64 -0
- package/components/effects/glitch-image/css/index.tsx +25 -0
- package/components/effects/glitch-image/tailwind/index.tsx +49 -0
- package/components/effects/glowing-border/css/glowing-border.module.css +73 -0
- package/components/effects/glowing-border/css/index.tsx +45 -0
- package/components/effects/glowing-border/tailwind/index.tsx +40 -0
- package/components/effects/screen-distortion/screen-distortion.module.css +2 -2
- package/components/effects/warning-tape/index.tsx +4 -4
- package/components/effects/warning-tape/warning-tape.module.css +2 -0
- package/components/layout/data-grid/css/data-grid.module.css +52 -0
- package/components/layout/data-grid/css/index.tsx +76 -0
- package/components/layout/data-grid/tailwind/index.tsx +74 -0
- package/components/layout/hologram-card/css/hologram-card.module.css +102 -0
- package/components/layout/hologram-card/css/index.tsx +46 -0
- package/components/layout/hologram-card/tailwind/index.tsx +61 -0
- package/components/navigation/hexagon-menu/css/hexagon-menu.module.css +55 -0
- package/components/navigation/hexagon-menu/css/index.tsx +35 -0
- package/components/navigation/hexagon-menu/tailwind/index.tsx +53 -0
- package/components/neon/neon-alert/css/index.tsx +53 -0
- package/components/neon/neon-alert/css/neon-alert.module.css +60 -0
- package/components/neon/neon-alert/tailwind/index.tsx +59 -0
- package/components/neon/neon-badge/css/index.tsx +49 -0
- package/components/neon/neon-badge/css/neon-badge.module.css +53 -0
- package/components/neon/neon-badge/tailwind/index.tsx +50 -0
- package/components/neon/neon-button/css/index.tsx +54 -0
- package/components/neon/neon-button/css/neon-button.module.css +114 -0
- package/components/neon/neon-button/tailwind/index.tsx +51 -0
- package/components/neon/neon-divider/css/index.tsx +26 -0
- package/components/neon/neon-divider/css/neon-divider.module.css +43 -0
- package/components/neon/neon-divider/tailwind/index.tsx +36 -0
- package/components/neon/neon-progress/css/index.tsx +65 -0
- package/components/neon/neon-progress/css/neon-progress.module.css +88 -0
- package/components/neon/neon-progress/tailwind/index.tsx +46 -0
- package/components/neon/neon-tabs/css/index.tsx +41 -0
- package/components/neon/neon-tabs/css/neon-tabs.module.css +45 -0
- package/components/neon/neon-tabs/tailwind/index.tsx +53 -0
- package/components/neon/neon-toggle/css/index.tsx +58 -0
- package/components/neon/neon-toggle/css/neon-toggle.module.css +79 -0
- package/components/neon/neon-toggle/tailwind/index.tsx +57 -0
- package/components/text/glitch-text/glitch-text.module.css +2 -2
- package/package.json +1 -1
- package/components/glow-orbs/glow-orbs.module.css +0 -31
- package/components/glow-orbs/index.tsx +0 -87
- package/components/light-beams/index.tsx +0 -80
- package/components/light-beams/light-beams.module.css +0 -27
- package/components/noise-canvas/index.tsx +0 -113
- package/components/noise-canvas/noise-canvas.module.css +0 -8
- package/components/package.json +0 -13
- package/components/particle-field/index.tsx +0 -81
- package/components/particle-field/particle-field.module.css +0 -31
package/bin/cli.js
CHANGED
|
@@ -39,6 +39,34 @@ const COMPONENTS = {
|
|
|
39
39
|
'warning-tape': { category: 'effects', description: 'Scrolling warning tape banner', status: 'ready' },
|
|
40
40
|
'cursor-follower': { category: 'effects', description: 'Custom cursor with trail', status: 'ready' },
|
|
41
41
|
'screen-distortion': { category: 'effects', description: 'Full screen distortion effect', status: 'ready' },
|
|
42
|
+
'glowing-border': { category: 'effects', description: 'Glowing border container with pulse', status: 'ready' },
|
|
43
|
+
|
|
44
|
+
// Neon components
|
|
45
|
+
'neon-button': { category: 'neon', description: 'Button with neon glow effect', status: 'ready' },
|
|
46
|
+
'neon-badge': { category: 'neon', description: 'Luminous status badges', status: 'ready' },
|
|
47
|
+
'neon-progress': { category: 'neon', description: 'Glowing progress bar with shimmer', status: 'ready' },
|
|
48
|
+
'neon-toggle': { category: 'neon', description: 'On/off switch with neon glow', status: 'ready' },
|
|
49
|
+
'neon-alert': { category: 'neon', description: 'Alert notifications with neon style', status: 'ready' },
|
|
50
|
+
'neon-tabs': { category: 'neon', description: 'Tab navigation with glow effect', status: 'ready' },
|
|
51
|
+
'neon-divider': { category: 'neon', description: 'Luminous section dividers', status: 'ready' },
|
|
52
|
+
|
|
53
|
+
// Cyber components
|
|
54
|
+
'cyber-input': { category: 'cyber', description: 'Input with animated border glow', status: 'ready' },
|
|
55
|
+
'cyber-loader': { category: 'cyber', description: 'Futuristic spinners and loaders', status: 'ready' },
|
|
56
|
+
'cyber-modal': { category: 'cyber', description: 'Modal with scanlines and glow', status: 'ready' },
|
|
57
|
+
'cyber-avatar': { category: 'cyber', description: 'Avatar with neon border and status', status: 'ready' },
|
|
58
|
+
'cyber-slider': { category: 'cyber', description: 'Slider with neon track and thumb', status: 'ready' },
|
|
59
|
+
'cyber-tooltip': { category: 'cyber', description: 'Terminal-style tooltips', status: 'ready' },
|
|
60
|
+
|
|
61
|
+
// Layout components
|
|
62
|
+
'hologram-card': { category: 'layout', description: 'Holographic card with scanlines', status: 'ready' },
|
|
63
|
+
'data-grid': { category: 'layout', description: 'Terminal-style data table', status: 'ready' },
|
|
64
|
+
|
|
65
|
+
// Navigation
|
|
66
|
+
'hexagon-menu': { category: 'navigation', description: 'Honeycomb hexagon menu', status: 'ready' },
|
|
67
|
+
|
|
68
|
+
// Effects (new)
|
|
69
|
+
'glitch-image': { category: 'effects', description: 'Image with RGB glitch on hover', status: 'ready' },
|
|
42
70
|
};
|
|
43
71
|
|
|
44
72
|
const program = new Command();
|
|
@@ -75,8 +103,15 @@ program
|
|
|
75
103
|
.command('add [component]')
|
|
76
104
|
.description('Add a component to your project')
|
|
77
105
|
.option('-d, --dir <path>', 'Target directory', './components/chaos')
|
|
106
|
+
.option('-v, --variant <type>', 'Styling variant: css or tailwind', 'css')
|
|
78
107
|
.option('-y, --yes', 'Skip confirmation')
|
|
79
108
|
.action(async (componentName, options) => {
|
|
109
|
+
const variant = options.variant.toLowerCase();
|
|
110
|
+
if (!['css', 'tailwind'].includes(variant)) {
|
|
111
|
+
console.log(pc.red(`\n✗ Invalid variant "${variant}". Use 'css' or 'tailwind'.\n`));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
80
115
|
// If no component specified, show interactive picker
|
|
81
116
|
if (!componentName) {
|
|
82
117
|
const choices = Object.entries(COMPONENTS).map(([name, info]) => ({
|
|
@@ -85,20 +120,33 @@ program
|
|
|
85
120
|
value: name,
|
|
86
121
|
}));
|
|
87
122
|
|
|
88
|
-
const response = await prompts(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
const response = await prompts([
|
|
124
|
+
{
|
|
125
|
+
type: 'autocomplete',
|
|
126
|
+
name: 'component',
|
|
127
|
+
message: 'Which component?',
|
|
128
|
+
choices,
|
|
129
|
+
suggest: (input, choices) =>
|
|
130
|
+
choices.filter(c => c.title.includes(input) || c.description.toLowerCase().includes(input.toLowerCase()))
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'select',
|
|
134
|
+
name: 'variant',
|
|
135
|
+
message: 'Styling variant?',
|
|
136
|
+
choices: [
|
|
137
|
+
{ title: 'CSS Modules', value: 'css' },
|
|
138
|
+
{ title: 'Tailwind', value: 'tailwind' },
|
|
139
|
+
],
|
|
140
|
+
initial: variant === 'tailwind' ? 1 : 0,
|
|
141
|
+
}
|
|
142
|
+
]);
|
|
96
143
|
|
|
97
144
|
if (!response.component) {
|
|
98
145
|
console.log(pc.dim('Cancelled.'));
|
|
99
146
|
return;
|
|
100
147
|
}
|
|
101
148
|
componentName = response.component;
|
|
149
|
+
options.variant = response.variant || variant;
|
|
102
150
|
}
|
|
103
151
|
|
|
104
152
|
// Validate component exists
|
|
@@ -109,22 +157,36 @@ program
|
|
|
109
157
|
}
|
|
110
158
|
|
|
111
159
|
const info = COMPONENTS[componentName];
|
|
112
|
-
const
|
|
160
|
+
const baseDir = path.join(COMPONENTS_DIR, info.category, componentName);
|
|
161
|
+
|
|
162
|
+
// Check if component has variant subdirs or is legacy (flat structure)
|
|
163
|
+
const variantDir = path.join(baseDir, options.variant);
|
|
164
|
+
const hasVariants = fs.existsSync(path.join(baseDir, 'css')) || fs.existsSync(path.join(baseDir, 'tailwind'));
|
|
165
|
+
const sourceDir = hasVariants ? variantDir : baseDir;
|
|
113
166
|
const targetDir = path.resolve(options.dir, info.category);
|
|
114
167
|
|
|
115
168
|
// Check source exists
|
|
116
169
|
if (!fs.existsSync(sourceDir)) {
|
|
117
|
-
|
|
118
|
-
|
|
170
|
+
if (hasVariants) {
|
|
171
|
+
console.log(pc.yellow(`\n⚠ Variant "${options.variant}" not available for "${componentName}".`));
|
|
172
|
+
const available = [];
|
|
173
|
+
if (fs.existsSync(path.join(baseDir, 'css'))) available.push('css');
|
|
174
|
+
if (fs.existsSync(path.join(baseDir, 'tailwind'))) available.push('tailwind');
|
|
175
|
+
console.log(pc.dim(` Available variants: ${available.join(', ')}\n`));
|
|
176
|
+
} else {
|
|
177
|
+
console.log(pc.yellow(`\n⚠ Component "${componentName}" is not yet implemented.`));
|
|
178
|
+
console.log(pc.dim(' Coming soon!\n'));
|
|
179
|
+
}
|
|
119
180
|
return;
|
|
120
181
|
}
|
|
121
182
|
|
|
122
183
|
// Confirm
|
|
123
184
|
if (!options.yes) {
|
|
185
|
+
const variantLabel = hasVariants ? ` (${pc.magenta(options.variant)})` : '';
|
|
124
186
|
const confirm = await prompts({
|
|
125
187
|
type: 'confirm',
|
|
126
188
|
name: 'value',
|
|
127
|
-
message: `Add ${pc.cyan(componentName)} to ${pc.dim(targetDir)}?`,
|
|
189
|
+
message: `Add ${pc.cyan(componentName)}${variantLabel} to ${pc.dim(targetDir)}?`,
|
|
128
190
|
initial: true,
|
|
129
191
|
});
|
|
130
192
|
|
|
@@ -139,7 +201,7 @@ program
|
|
|
139
201
|
await fs.ensureDir(targetDir);
|
|
140
202
|
await fs.copy(sourceDir, path.join(targetDir, componentName));
|
|
141
203
|
|
|
142
|
-
console.log(pc.green(`\n✓ Added ${componentName}`));
|
|
204
|
+
console.log(pc.green(`\n✓ Added ${componentName}`) + (hasVariants ? pc.magenta(` (${options.variant})`) : ''));
|
|
143
205
|
console.log(pc.dim(` → ${path.join(targetDir, componentName)}\n`));
|
|
144
206
|
|
|
145
207
|
// Show usage hint
|
|
@@ -21,7 +21,7 @@ export interface GlowOrbsProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
21
21
|
export const GlowOrbs = forwardRef<HTMLDivElement, GlowOrbsProps>(
|
|
22
22
|
(
|
|
23
23
|
{
|
|
24
|
-
colors = ['
|
|
24
|
+
colors = ['hsl(var(--primary))', 'hsl(var(--secondary))', 'hsl(var(--accent, 300 100% 50%))', 'hsl(var(--muted))'],
|
|
25
25
|
count = 5,
|
|
26
26
|
sizeRange = [100, 300],
|
|
27
27
|
blur = 80,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
.button {
|
|
2
|
-
--accent:
|
|
2
|
+
--accent: hsl(var(--primary, 347 100% 50%));
|
|
3
|
+
--accent-alt: hsl(var(--secondary, 180 100% 50%));
|
|
3
4
|
position: relative;
|
|
4
5
|
padding: 1rem 2.5rem;
|
|
5
6
|
font-family: inherit;
|
|
@@ -132,7 +133,7 @@
|
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
.ghost1 { color: var(--accent); }
|
|
135
|
-
.ghost2 { color:
|
|
136
|
+
.ghost2 { color: var(--accent-alt); }
|
|
136
137
|
|
|
137
138
|
.button:hover .ghost1 {
|
|
138
139
|
opacity: 0.6;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
cursor: pointer;
|
|
14
14
|
overflow: hidden;
|
|
15
15
|
transition: transform 0.1s;
|
|
16
|
-
--glitch-color:
|
|
17
|
-
--glitch-color-alt:
|
|
16
|
+
--glitch-color: hsl(var(--primary, 347 100% 50%));
|
|
17
|
+
--glitch-color-alt: hsl(var(--secondary, 180 100% 50%));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
.button:active {
|
|
@@ -23,20 +23,20 @@
|
|
|
23
23
|
|
|
24
24
|
/* Variants */
|
|
25
25
|
.default {
|
|
26
|
-
background:
|
|
27
|
-
color:
|
|
28
|
-
border: 1px solid
|
|
26
|
+
background: hsl(var(--background, 0 0% 4%));
|
|
27
|
+
color: hsl(var(--foreground, 0 0% 98%));
|
|
28
|
+
border: 1px solid hsl(var(--border, 0 0% 20%));
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
.outline {
|
|
32
32
|
background: transparent;
|
|
33
|
-
color:
|
|
33
|
+
color: hsl(var(--foreground, 0 0% 98%));
|
|
34
34
|
border: 2px solid currentColor;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.ghost {
|
|
38
38
|
background: transparent;
|
|
39
|
-
color:
|
|
39
|
+
color: hsl(var(--foreground, 0 0% 98%));
|
|
40
40
|
border: none;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* Chaos UI - CSS Variables */
|
|
2
|
+
/* These are the DEFAULT values - they will be overridden by your project's globals.css */
|
|
3
|
+
/* Only add this if you don't have these variables defined yet */
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
/* Standard variables (same as shadcn/ui) */
|
|
7
|
+
--primary: 347 100% 50%; /* #ff0040 */
|
|
8
|
+
--primary-foreground: 0 0% 100%;
|
|
9
|
+
|
|
10
|
+
--secondary: 180 100% 50%; /* #00ffff */
|
|
11
|
+
--secondary-foreground: 0 0% 0%;
|
|
12
|
+
|
|
13
|
+
--accent: 300 100% 50%; /* #ff00ff */
|
|
14
|
+
--accent-foreground: 0 0% 100%;
|
|
15
|
+
|
|
16
|
+
--background: 0 0% 4%; /* #0a0a0a */
|
|
17
|
+
--foreground: 0 0% 98%; /* #fafafa */
|
|
18
|
+
|
|
19
|
+
--muted: 0 0% 9%; /* #171717 */
|
|
20
|
+
--muted-foreground: 0 0% 63%; /* #a1a1a1 */
|
|
21
|
+
|
|
22
|
+
--border: 0 0% 20%; /* #333333 */
|
|
23
|
+
|
|
24
|
+
/* Chaos-specific (optional, for fine-tuning effects) */
|
|
25
|
+
--destructive: 0 84% 60%;
|
|
26
|
+
--destructive-foreground: 0 0% 98%;
|
|
27
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
.avatar {
|
|
2
|
+
--avatar-color: #00f0ff;
|
|
3
|
+
--avatar-size: 3rem;
|
|
4
|
+
position: relative;
|
|
5
|
+
width: var(--avatar-size);
|
|
6
|
+
height: var(--avatar-size);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.sm { --avatar-size: 2rem; }
|
|
10
|
+
.md { --avatar-size: 3rem; }
|
|
11
|
+
.lg { --avatar-size: 4rem; }
|
|
12
|
+
.xl { --avatar-size: 6rem; }
|
|
13
|
+
|
|
14
|
+
.cyan { --avatar-color: #00f0ff; }
|
|
15
|
+
.pink { --avatar-color: #ff00ff; }
|
|
16
|
+
.green { --avatar-color: #00ff88; }
|
|
17
|
+
.purple { --avatar-color: #a855f7; }
|
|
18
|
+
|
|
19
|
+
.image {
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
object-fit: cover;
|
|
23
|
+
border-radius: 50%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.border {
|
|
27
|
+
position: absolute;
|
|
28
|
+
top: -2px;
|
|
29
|
+
left: -2px;
|
|
30
|
+
width: calc(100% + 4px);
|
|
31
|
+
height: calc(100% + 4px);
|
|
32
|
+
border: 2px solid var(--avatar-color);
|
|
33
|
+
border-radius: 50%;
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.glowing .border {
|
|
38
|
+
box-shadow: 0 0 10px var(--avatar-color), 0 0 20px var(--avatar-color);
|
|
39
|
+
animation: avatar-glow 2s ease-in-out infinite;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@keyframes avatar-glow {
|
|
43
|
+
0%, 100% { box-shadow: 0 0 5px var(--avatar-color); }
|
|
44
|
+
50% { box-shadow: 0 0 15px var(--avatar-color), 0 0 30px var(--avatar-color); }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.status {
|
|
48
|
+
position: absolute;
|
|
49
|
+
bottom: 2px;
|
|
50
|
+
right: 2px;
|
|
51
|
+
width: 0.75rem;
|
|
52
|
+
height: 0.75rem;
|
|
53
|
+
border-radius: 50%;
|
|
54
|
+
border: 2px solid hsl(var(--background, 0 0% 4%));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.online { background: #00ff88; box-shadow: 0 0 6px #00ff88; }
|
|
58
|
+
.offline { background: #666; }
|
|
59
|
+
.busy { background: #ff0040; box-shadow: 0 0 6px #ff0040; }
|
|
60
|
+
.away { background: #ffaa00; box-shadow: 0 0 6px #ffaa00; }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { forwardRef, ImgHTMLAttributes } from 'react';
|
|
4
|
+
import styles from './cyber-avatar.module.css';
|
|
5
|
+
|
|
6
|
+
export interface CyberAvatarProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'> {
|
|
7
|
+
src: string;
|
|
8
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
9
|
+
variant?: 'cyan' | 'pink' | 'green' | 'purple';
|
|
10
|
+
status?: 'online' | 'offline' | 'busy' | 'away';
|
|
11
|
+
glowing?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const CyberAvatar = forwardRef<HTMLDivElement, CyberAvatarProps>(
|
|
15
|
+
({ src, alt = 'Avatar', size = 'md', variant = 'cyan', status, glowing = false, className, ...props }, ref) => {
|
|
16
|
+
const classes = [styles.avatar, styles[size], styles[variant], glowing && styles.glowing, className].filter(Boolean).join(' ');
|
|
17
|
+
return (
|
|
18
|
+
<div ref={ref} className={classes}>
|
|
19
|
+
<img src={src} alt={alt} className={styles.image} {...props} />
|
|
20
|
+
<div className={styles.border} />
|
|
21
|
+
{status && <span className={`${styles.status} ${styles[status]}`} />}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CyberAvatar.displayName = 'CyberAvatar';
|
|
28
|
+
export default CyberAvatar;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { forwardRef, ImgHTMLAttributes } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface CyberAvatarProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'> {
|
|
6
|
+
src: string;
|
|
7
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
8
|
+
variant?: 'cyan' | 'pink' | 'green' | 'purple';
|
|
9
|
+
status?: 'online' | 'offline' | 'busy' | 'away';
|
|
10
|
+
glowing?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const sizeStyles = { sm: 'w-8 h-8', md: 'w-12 h-12', lg: 'w-16 h-16', xl: 'w-24 h-24' };
|
|
14
|
+
const variantStyles = {
|
|
15
|
+
cyan: 'border-cyan-400 shadow-[0_0_10px_#00f0ff]',
|
|
16
|
+
pink: 'border-fuchsia-500 shadow-[0_0_10px_#ff00ff]',
|
|
17
|
+
green: 'border-emerald-400 shadow-[0_0_10px_#00ff88]',
|
|
18
|
+
purple: 'border-purple-500 shadow-[0_0_10px_#a855f7]',
|
|
19
|
+
};
|
|
20
|
+
const statusStyles = {
|
|
21
|
+
online: 'bg-emerald-400 shadow-[0_0_6px_#00ff88]',
|
|
22
|
+
offline: 'bg-gray-500',
|
|
23
|
+
busy: 'bg-rose-500 shadow-[0_0_6px_#ff0040]',
|
|
24
|
+
away: 'bg-amber-500 shadow-[0_0_6px_#ffaa00]',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const CyberAvatar = forwardRef<HTMLDivElement, CyberAvatarProps>(
|
|
28
|
+
({ src, alt = 'Avatar', size = 'md', variant = 'cyan', status, glowing = false, className = '', ...props }, ref) => {
|
|
29
|
+
return (
|
|
30
|
+
<div ref={ref} className={`relative ${sizeStyles[size]} ${className}`}>
|
|
31
|
+
<img
|
|
32
|
+
src={src}
|
|
33
|
+
alt={alt}
|
|
34
|
+
className={`w-full h-full object-cover rounded-full border-2 ${variantStyles[variant]} ${glowing ? 'animate-pulse' : ''}`}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
{status && (
|
|
38
|
+
<span className={`absolute bottom-0.5 right-0.5 w-3 h-3 rounded-full border-2 border-[#0a0a0f] ${statusStyles[status]}`} />
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CyberAvatar.displayName = 'CyberAvatar';
|
|
46
|
+
export default CyberAvatar;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
.wrapper {
|
|
2
|
+
--input-color: #00f0ff;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: 0.5rem;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.cyan { --input-color: #00f0ff; }
|
|
9
|
+
.pink { --input-color: #ff00ff; }
|
|
10
|
+
.green { --input-color: #00ff88; }
|
|
11
|
+
.purple { --input-color: #a855f7; }
|
|
12
|
+
.hasError { --input-color: #ff0040; }
|
|
13
|
+
|
|
14
|
+
.label {
|
|
15
|
+
font-family: var(--font-display, 'Rajdhani', sans-serif);
|
|
16
|
+
font-size: 0.75rem;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
text-transform: uppercase;
|
|
19
|
+
letter-spacing: 0.125em;
|
|
20
|
+
color: var(--input-color);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.inputWrapper {
|
|
24
|
+
position: relative;
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.input {
|
|
30
|
+
width: 100%;
|
|
31
|
+
padding: 0.75rem 1rem;
|
|
32
|
+
font-family: var(--font-mono, 'Share Tech Mono', monospace);
|
|
33
|
+
font-size: 0.875rem;
|
|
34
|
+
color: hsl(var(--foreground, 0 0% 98%));
|
|
35
|
+
background: rgba(0, 0, 0, 0.5);
|
|
36
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
37
|
+
border-radius: 0;
|
|
38
|
+
outline: none;
|
|
39
|
+
transition: all 0.3s ease;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.input::placeholder {
|
|
43
|
+
color: rgba(255, 255, 255, 0.3);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.input:focus {
|
|
47
|
+
border-color: var(--input-color);
|
|
48
|
+
box-shadow: 0 0 10px rgba(0, 240, 255, 0.3);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.border {
|
|
52
|
+
position: absolute;
|
|
53
|
+
bottom: 0;
|
|
54
|
+
left: 0;
|
|
55
|
+
width: 0;
|
|
56
|
+
height: 2px;
|
|
57
|
+
background: var(--input-color);
|
|
58
|
+
transition: width 0.3s ease;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.input:focus ~ .border {
|
|
62
|
+
width: 100%;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.glow {
|
|
66
|
+
position: absolute;
|
|
67
|
+
bottom: 0;
|
|
68
|
+
left: 50%;
|
|
69
|
+
width: 0;
|
|
70
|
+
height: 1.25rem;
|
|
71
|
+
background: radial-gradient(ellipse, var(--input-color), transparent);
|
|
72
|
+
opacity: 0;
|
|
73
|
+
transform: translateX(-50%);
|
|
74
|
+
transition: all 0.3s ease;
|
|
75
|
+
pointer-events: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.input:focus ~ .glow {
|
|
79
|
+
width: 80%;
|
|
80
|
+
opacity: 0.3;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.error {
|
|
84
|
+
font-family: var(--font-mono, 'Share Tech Mono', monospace);
|
|
85
|
+
font-size: 0.75rem;
|
|
86
|
+
color: #ff0040;
|
|
87
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { forwardRef, InputHTMLAttributes } from 'react';
|
|
4
|
+
import styles from './cyber-input.module.css';
|
|
5
|
+
|
|
6
|
+
export interface CyberInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
7
|
+
/** Neon color variant */
|
|
8
|
+
variant?: 'cyan' | 'pink' | 'green' | 'purple';
|
|
9
|
+
/** Label above input */
|
|
10
|
+
label?: string;
|
|
11
|
+
/** Error message */
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const CyberInput = forwardRef<HTMLInputElement, CyberInputProps>(
|
|
16
|
+
(
|
|
17
|
+
{
|
|
18
|
+
variant = 'cyan',
|
|
19
|
+
label,
|
|
20
|
+
error,
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
},
|
|
24
|
+
ref
|
|
25
|
+
) => {
|
|
26
|
+
const wrapperClasses = [
|
|
27
|
+
styles.wrapper,
|
|
28
|
+
styles[variant],
|
|
29
|
+
error && styles.hasError,
|
|
30
|
+
className,
|
|
31
|
+
].filter(Boolean).join(' ');
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={wrapperClasses}>
|
|
35
|
+
{label && <label className={styles.label}>{label}</label>}
|
|
36
|
+
<div className={styles.inputWrapper}>
|
|
37
|
+
<input ref={ref} className={styles.input} {...props} />
|
|
38
|
+
<span className={styles.border} />
|
|
39
|
+
<span className={styles.glow} />
|
|
40
|
+
</div>
|
|
41
|
+
{error && <span className={styles.error}>{error}</span>}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
CyberInput.displayName = 'CyberInput';
|
|
48
|
+
|
|
49
|
+
export default CyberInput;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { forwardRef, InputHTMLAttributes } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface CyberInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
variant?: 'cyan' | 'pink' | 'green' | 'purple';
|
|
7
|
+
label?: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const variantStyles = {
|
|
12
|
+
cyan: 'focus:border-cyan-400 focus:shadow-[0_0_10px_rgba(0,240,255,0.3)]',
|
|
13
|
+
pink: 'focus:border-fuchsia-500 focus:shadow-[0_0_10px_rgba(255,0,255,0.3)]',
|
|
14
|
+
green: 'focus:border-emerald-400 focus:shadow-[0_0_10px_rgba(0,255,136,0.3)]',
|
|
15
|
+
purple: 'focus:border-purple-500 focus:shadow-[0_0_10px_rgba(168,85,247,0.3)]',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const labelColors = {
|
|
19
|
+
cyan: 'text-cyan-400',
|
|
20
|
+
pink: 'text-fuchsia-500',
|
|
21
|
+
green: 'text-emerald-400',
|
|
22
|
+
purple: 'text-purple-500',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const CyberInput = forwardRef<HTMLInputElement, CyberInputProps>(
|
|
26
|
+
({ variant = 'cyan', label, error, className = '', ...props }, ref) => {
|
|
27
|
+
return (
|
|
28
|
+
<div className={`flex flex-col gap-2 ${className}`}>
|
|
29
|
+
{label && (
|
|
30
|
+
<label className={`font-['Rajdhani',sans-serif] text-xs font-semibold uppercase tracking-widest ${labelColors[variant]}`}>
|
|
31
|
+
{label}
|
|
32
|
+
</label>
|
|
33
|
+
)}
|
|
34
|
+
<input
|
|
35
|
+
ref={ref}
|
|
36
|
+
className={`
|
|
37
|
+
w-full px-4 py-3
|
|
38
|
+
font-['Share_Tech_Mono',monospace] text-sm text-white
|
|
39
|
+
bg-black/50 border border-white/10
|
|
40
|
+
outline-none transition-all duration-300
|
|
41
|
+
placeholder:text-white/30
|
|
42
|
+
${error ? 'border-rose-500 focus:border-rose-500' : variantStyles[variant]}
|
|
43
|
+
`}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
{error && (
|
|
47
|
+
<span className="font-['Share_Tech_Mono',monospace] text-xs text-rose-500">{error}</span>
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CyberInput.displayName = 'CyberInput';
|
|
55
|
+
export default CyberInput;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
.loader {
|
|
2
|
+
--loader-color: #00f0ff;
|
|
3
|
+
--loader-size: 2.5rem;
|
|
4
|
+
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.cyan { --loader-color: #00f0ff; }
|
|
11
|
+
.pink { --loader-color: #ff00ff; }
|
|
12
|
+
.green { --loader-color: #00ff88; }
|
|
13
|
+
.purple { --loader-color: #a855f7; }
|
|
14
|
+
|
|
15
|
+
.sm { --loader-size: 1.5rem; }
|
|
16
|
+
.md { --loader-size: 2.5rem; }
|
|
17
|
+
.lg { --loader-size: 4rem; }
|
|
18
|
+
|
|
19
|
+
/* Spinner */
|
|
20
|
+
.spinner {
|
|
21
|
+
width: var(--loader-size);
|
|
22
|
+
height: var(--loader-size);
|
|
23
|
+
border: 3px solid rgba(255, 255, 255, 0.1);
|
|
24
|
+
border-top-color: var(--loader-color);
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
animation: spin 1s linear infinite;
|
|
27
|
+
box-shadow: 0 0 10px var(--loader-color);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@keyframes spin {
|
|
31
|
+
to { transform: rotate(360deg); }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Dots */
|
|
35
|
+
.dots {
|
|
36
|
+
gap: 0.5rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.dot {
|
|
40
|
+
width: calc(var(--loader-size) / 4);
|
|
41
|
+
height: calc(var(--loader-size) / 4);
|
|
42
|
+
background: var(--loader-color);
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
animation: dot-bounce 1.4s ease-in-out infinite;
|
|
45
|
+
box-shadow: 0 0 10px var(--loader-color);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.dot:nth-child(1) { animation-delay: 0s; }
|
|
49
|
+
.dot:nth-child(2) { animation-delay: 0.2s; }
|
|
50
|
+
.dot:nth-child(3) { animation-delay: 0.4s; }
|
|
51
|
+
|
|
52
|
+
@keyframes dot-bounce {
|
|
53
|
+
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
|
|
54
|
+
40% { transform: scale(1); opacity: 1; }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Bars */
|
|
58
|
+
.bars {
|
|
59
|
+
gap: 0.25rem;
|
|
60
|
+
height: var(--loader-size);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.bar {
|
|
64
|
+
width: calc(var(--loader-size) / 6);
|
|
65
|
+
height: 100%;
|
|
66
|
+
background: var(--loader-color);
|
|
67
|
+
animation: bar-stretch 1.2s ease-in-out infinite;
|
|
68
|
+
box-shadow: 0 0 8px var(--loader-color);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.bar:nth-child(1) { animation-delay: 0s; }
|
|
72
|
+
.bar:nth-child(2) { animation-delay: 0.1s; }
|
|
73
|
+
.bar:nth-child(3) { animation-delay: 0.2s; }
|
|
74
|
+
.bar:nth-child(4) { animation-delay: 0.3s; }
|
|
75
|
+
|
|
76
|
+
@keyframes bar-stretch {
|
|
77
|
+
0%, 40%, 100% { transform: scaleY(0.4); }
|
|
78
|
+
20% { transform: scaleY(1); }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Pulse */
|
|
82
|
+
.pulse {
|
|
83
|
+
width: var(--loader-size);
|
|
84
|
+
height: var(--loader-size);
|
|
85
|
+
background: var(--loader-color);
|
|
86
|
+
border-radius: 50%;
|
|
87
|
+
animation: pulse-grow 1.5s ease-in-out infinite;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@keyframes pulse-grow {
|
|
91
|
+
0% { transform: scale(0.5); opacity: 1; }
|
|
92
|
+
100% { transform: scale(1.5); opacity: 0; }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Hexagon */
|
|
96
|
+
.hexagon {
|
|
97
|
+
width: var(--loader-size);
|
|
98
|
+
height: var(--loader-size);
|
|
99
|
+
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
|
100
|
+
background: conic-gradient(from 0deg, var(--loader-color), transparent);
|
|
101
|
+
animation: spin 1.5s linear infinite;
|
|
102
|
+
}
|