@oalacea/chaosui 0.1.0 → 0.5.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.
Files changed (139) hide show
  1. package/bin/cli.js +105 -13
  2. package/components/backgrounds/glow-orbs/index.tsx +1 -1
  3. package/components/buttons/chaos-button/chaos-button.module.css +3 -2
  4. package/components/buttons/cta-brutal/cta-brutal.module.css +81 -0
  5. package/components/buttons/cta-brutal/index.tsx +56 -0
  6. package/components/buttons/dead-button/dead-button.module.css +111 -0
  7. package/components/buttons/dead-button/index.tsx +47 -0
  8. package/components/buttons/deeper-button/deeper-button.module.css +76 -0
  9. package/components/buttons/deeper-button/index.tsx +51 -0
  10. package/components/buttons/dual-choice/dual-choice.module.css +90 -0
  11. package/components/buttons/dual-choice/index.tsx +54 -0
  12. package/components/buttons/glitch-button/glitch-button.module.css +7 -7
  13. package/components/buttons/tension-bar/index.tsx +79 -0
  14. package/components/buttons/tension-bar/tension-bar.module.css +105 -0
  15. package/components/chaos-vars.css +27 -0
  16. package/components/cyber/cyber-avatar/css/cyber-avatar.module.css +60 -0
  17. package/components/cyber/cyber-avatar/css/index.tsx +28 -0
  18. package/components/cyber/cyber-avatar/tailwind/index.tsx +46 -0
  19. package/components/cyber/cyber-input/css/cyber-input.module.css +87 -0
  20. package/components/cyber/cyber-input/css/index.tsx +49 -0
  21. package/components/cyber/cyber-input/tailwind/index.tsx +55 -0
  22. package/components/cyber/cyber-loader/css/cyber-loader.module.css +102 -0
  23. package/components/cyber/cyber-loader/css/index.tsx +58 -0
  24. package/components/cyber/cyber-loader/tailwind/index.tsx +63 -0
  25. package/components/cyber/cyber-modal/css/cyber-modal.module.css +124 -0
  26. package/components/cyber/cyber-modal/css/index.tsx +75 -0
  27. package/components/cyber/cyber-modal/tailwind/index.tsx +87 -0
  28. package/components/cyber/cyber-slider/css/cyber-slider.module.css +61 -0
  29. package/components/cyber/cyber-slider/css/index.tsx +41 -0
  30. package/components/cyber/cyber-slider/tailwind/index.tsx +51 -0
  31. package/components/cyber/cyber-tooltip/css/cyber-tooltip.module.css +67 -0
  32. package/components/cyber/cyber-tooltip/css/index.tsx +36 -0
  33. package/components/cyber/cyber-tooltip/tailwind/index.tsx +48 -0
  34. package/components/decorative/coffee-stain/coffee-stain.module.css +24 -0
  35. package/components/decorative/coffee-stain/index.tsx +55 -0
  36. package/components/decorative/ornaments/index.tsx +51 -0
  37. package/components/decorative/ornaments/ornaments.module.css +33 -0
  38. package/components/decorative/rune-symbols/index.tsx +55 -0
  39. package/components/decorative/rune-symbols/rune-symbols.module.css +22 -0
  40. package/components/effects/glitch-image/css/glitch-image.module.css +64 -0
  41. package/components/effects/glitch-image/css/index.tsx +25 -0
  42. package/components/effects/glitch-image/tailwind/index.tsx +49 -0
  43. package/components/effects/glowing-border/css/glowing-border.module.css +73 -0
  44. package/components/effects/glowing-border/css/index.tsx +45 -0
  45. package/components/effects/glowing-border/tailwind/index.tsx +40 -0
  46. package/components/effects/screen-distortion/screen-distortion.module.css +2 -2
  47. package/components/effects/warning-tape/index.tsx +4 -4
  48. package/components/effects/warning-tape/warning-tape.module.css +2 -0
  49. package/components/layout/data-grid/css/data-grid.module.css +52 -0
  50. package/components/layout/data-grid/css/index.tsx +76 -0
  51. package/components/layout/data-grid/tailwind/index.tsx +74 -0
  52. package/components/layout/hologram-card/css/hologram-card.module.css +102 -0
  53. package/components/layout/hologram-card/css/index.tsx +46 -0
  54. package/components/layout/hologram-card/tailwind/index.tsx +61 -0
  55. package/components/layout/horizontal-scroll/horizontal-scroll.module.css +30 -0
  56. package/components/layout/horizontal-scroll/index.tsx +78 -0
  57. package/components/layout/spec-grid/index.tsx +56 -0
  58. package/components/layout/spec-grid/spec-grid.module.css +21 -0
  59. package/components/layout/tower-pricing/index.tsx +56 -0
  60. package/components/layout/tower-pricing/tower-pricing.module.css +27 -0
  61. package/components/layout/tracklist/index.tsx +45 -0
  62. package/components/layout/tracklist/tracklist.module.css +24 -0
  63. package/components/layout/void-frame/index.tsx +32 -0
  64. package/components/layout/void-frame/void-frame.module.css +38 -0
  65. package/components/navigation/brutal-nav/brutal-nav.module.css +85 -0
  66. package/components/navigation/brutal-nav/index.tsx +71 -0
  67. package/components/navigation/hexagon-menu/css/hexagon-menu.module.css +55 -0
  68. package/components/navigation/hexagon-menu/css/index.tsx +35 -0
  69. package/components/navigation/hexagon-menu/tailwind/index.tsx +53 -0
  70. package/components/navigation/progress-dots/index.tsx +55 -0
  71. package/components/navigation/progress-dots/progress-dots.module.css +91 -0
  72. package/components/navigation/scattered-nav/index.tsx +59 -0
  73. package/components/navigation/scattered-nav/scattered-nav.module.css +113 -0
  74. package/components/navigation/scroll-indicator/index.tsx +58 -0
  75. package/components/navigation/scroll-indicator/scroll-indicator.module.css +82 -0
  76. package/components/navigation/vertical-nav/index.tsx +59 -0
  77. package/components/navigation/vertical-nav/vertical-nav.module.css +98 -0
  78. package/components/neon/neon-alert/css/index.tsx +53 -0
  79. package/components/neon/neon-alert/css/neon-alert.module.css +60 -0
  80. package/components/neon/neon-alert/tailwind/index.tsx +59 -0
  81. package/components/neon/neon-badge/css/index.tsx +49 -0
  82. package/components/neon/neon-badge/css/neon-badge.module.css +53 -0
  83. package/components/neon/neon-badge/tailwind/index.tsx +50 -0
  84. package/components/neon/neon-button/css/index.tsx +54 -0
  85. package/components/neon/neon-button/css/neon-button.module.css +114 -0
  86. package/components/neon/neon-button/tailwind/index.tsx +51 -0
  87. package/components/neon/neon-divider/css/index.tsx +26 -0
  88. package/components/neon/neon-divider/css/neon-divider.module.css +43 -0
  89. package/components/neon/neon-divider/tailwind/index.tsx +36 -0
  90. package/components/neon/neon-progress/css/index.tsx +65 -0
  91. package/components/neon/neon-progress/css/neon-progress.module.css +88 -0
  92. package/components/neon/neon-progress/tailwind/index.tsx +46 -0
  93. package/components/neon/neon-tabs/css/index.tsx +41 -0
  94. package/components/neon/neon-tabs/css/neon-tabs.module.css +45 -0
  95. package/components/neon/neon-tabs/tailwind/index.tsx +53 -0
  96. package/components/neon/neon-toggle/css/index.tsx +58 -0
  97. package/components/neon/neon-toggle/css/neon-toggle.module.css +79 -0
  98. package/components/neon/neon-toggle/tailwind/index.tsx +57 -0
  99. package/components/text/ascii-art/css/ascii-art.module.css +173 -0
  100. package/components/text/ascii-art/css/index.tsx +116 -0
  101. package/components/text/ascii-art/tailwind/index.tsx +124 -0
  102. package/components/text/blood-drip/css/blood-drip.module.css +142 -0
  103. package/components/text/blood-drip/css/index.tsx +113 -0
  104. package/components/text/blood-drip/tailwind/index.tsx +133 -0
  105. package/components/text/char-glitch/css/char-glitch.module.css +124 -0
  106. package/components/text/char-glitch/css/index.tsx +153 -0
  107. package/components/text/char-glitch/tailwind/index.tsx +126 -0
  108. package/components/text/countdown-display/css/countdown-display.module.css +179 -0
  109. package/components/text/countdown-display/css/index.tsx +190 -0
  110. package/components/text/countdown-display/tailwind/index.tsx +155 -0
  111. package/components/text/giant-layers/css/giant-layers.module.css +156 -0
  112. package/components/text/giant-layers/css/index.tsx +97 -0
  113. package/components/text/giant-layers/tailwind/index.tsx +111 -0
  114. package/components/text/glitch-text/glitch-text.module.css +2 -2
  115. package/components/text/reveal-text/css/index.tsx +180 -0
  116. package/components/text/reveal-text/css/reveal-text.module.css +129 -0
  117. package/components/text/reveal-text/tailwind/index.tsx +135 -0
  118. package/components/text/rotate-text/css/index.tsx +139 -0
  119. package/components/text/rotate-text/css/rotate-text.module.css +162 -0
  120. package/components/text/rotate-text/tailwind/index.tsx +127 -0
  121. package/components/text/strike-reveal/css/index.tsx +124 -0
  122. package/components/text/strike-reveal/css/strike-reveal.module.css +139 -0
  123. package/components/text/strike-reveal/tailwind/index.tsx +138 -0
  124. package/components/text/terminal-output/css/index.tsx +179 -0
  125. package/components/text/terminal-output/css/terminal-output.module.css +203 -0
  126. package/components/text/terminal-output/tailwind/index.tsx +174 -0
  127. package/components/text/typing-text/css/index.tsx +115 -0
  128. package/components/text/typing-text/css/typing-text.module.css +84 -0
  129. package/components/text/typing-text/tailwind/index.tsx +126 -0
  130. package/package.json +1 -1
  131. package/components/glow-orbs/glow-orbs.module.css +0 -31
  132. package/components/glow-orbs/index.tsx +0 -87
  133. package/components/light-beams/index.tsx +0 -80
  134. package/components/light-beams/light-beams.module.css +0 -27
  135. package/components/noise-canvas/index.tsx +0 -113
  136. package/components/noise-canvas/noise-canvas.module.css +0 -8
  137. package/components/package.json +0 -13
  138. package/components/particle-field/index.tsx +0 -81
  139. package/components/particle-field/particle-field.module.css +0 -31
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+
5
+ export interface NeonProgressProps extends HTMLAttributes<HTMLDivElement> {
6
+ value: number;
7
+ max?: number;
8
+ variant?: 'cyan' | 'pink' | 'green' | 'purple';
9
+ size?: 'sm' | 'md' | 'lg';
10
+ showValue?: boolean;
11
+ }
12
+
13
+ const variantStyles = {
14
+ cyan: { bg: 'bg-cyan-400', text: 'text-cyan-400', glow: 'shadow-[0_0_10px_#00f0ff]' },
15
+ pink: { bg: 'bg-fuchsia-500', text: 'text-fuchsia-500', glow: 'shadow-[0_0_10px_#ff00ff]' },
16
+ green: { bg: 'bg-emerald-400', text: 'text-emerald-400', glow: 'shadow-[0_0_10px_#00ff88]' },
17
+ purple: { bg: 'bg-purple-500', text: 'text-purple-500', glow: 'shadow-[0_0_10px_#a855f7]' },
18
+ };
19
+
20
+ const sizeStyles = { sm: 'h-1', md: 'h-2', lg: 'h-3' };
21
+
22
+ export const NeonProgress = forwardRef<HTMLDivElement, NeonProgressProps>(
23
+ ({ value, max = 100, variant = 'cyan', size = 'md', showValue = false, className = '', ...props }, ref) => {
24
+ const percentage = Math.min((value / max) * 100, 100);
25
+ const styles = variantStyles[variant];
26
+
27
+ return (
28
+ <div ref={ref} className={`flex items-center gap-3 ${className}`} {...props}>
29
+ <div className={`relative flex-1 ${sizeStyles[size]} bg-white/10 rounded-full overflow-hidden`}>
30
+ <div
31
+ className={`h-full ${styles.bg} ${styles.glow} rounded-full transition-all duration-500 ease-out`}
32
+ style={{ width: `${percentage}%` }}
33
+ />
34
+ </div>
35
+ {showValue && (
36
+ <span className={`font-['Orbitron',sans-serif] text-xs font-bold ${styles.text} min-w-[40px] text-right`}>
37
+ {Math.round(percentage)}%
38
+ </span>
39
+ )}
40
+ </div>
41
+ );
42
+ }
43
+ );
44
+
45
+ NeonProgress.displayName = 'NeonProgress';
46
+ export default NeonProgress;
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './neon-tabs.module.css';
5
+
6
+ export interface NeonTab {
7
+ id: string;
8
+ label: string;
9
+ icon?: React.ReactNode;
10
+ }
11
+
12
+ export interface NeonTabsProps extends HTMLAttributes<HTMLDivElement> {
13
+ tabs: NeonTab[];
14
+ activeTab: string;
15
+ onChange: (tabId: string) => void;
16
+ variant?: 'cyan' | 'pink' | 'green' | 'purple';
17
+ }
18
+
19
+ export const NeonTabs = forwardRef<HTMLDivElement, NeonTabsProps>(
20
+ ({ tabs, activeTab, onChange, variant = 'cyan', className, ...props }, ref) => {
21
+ return (
22
+ <div ref={ref} className={`${styles.tabs} ${styles[variant]} ${className || ''}`} role="tablist" {...props}>
23
+ {tabs.map((tab) => (
24
+ <button
25
+ key={tab.id}
26
+ role="tab"
27
+ aria-selected={activeTab === tab.id}
28
+ className={`${styles.tab} ${activeTab === tab.id ? styles.active : ''}`}
29
+ onClick={() => onChange(tab.id)}
30
+ >
31
+ {tab.icon && <span className={styles.icon}>{tab.icon}</span>}
32
+ <span className={styles.label}>{tab.label}</span>
33
+ </button>
34
+ ))}
35
+ </div>
36
+ );
37
+ }
38
+ );
39
+
40
+ NeonTabs.displayName = 'NeonTabs';
41
+ export default NeonTabs;
@@ -0,0 +1,45 @@
1
+ .tabs {
2
+ --tabs-color: #00f0ff;
3
+ display: flex;
4
+ gap: 0.25rem;
5
+ padding: 0.25rem;
6
+ background: rgba(0, 0, 0, 0.5);
7
+ border: 1px solid rgba(255, 255, 255, 0.1);
8
+ }
9
+
10
+ .cyan { --tabs-color: #00f0ff; }
11
+ .pink { --tabs-color: #ff00ff; }
12
+ .green { --tabs-color: #00ff88; }
13
+ .purple { --tabs-color: #a855f7; }
14
+
15
+ .tab {
16
+ display: flex;
17
+ align-items: center;
18
+ gap: 0.5rem;
19
+ padding: 0.625rem 1.25rem;
20
+ font-family: var(--font-display, 'Orbitron', sans-serif);
21
+ font-size: 0.75rem;
22
+ font-weight: 600;
23
+ text-transform: uppercase;
24
+ letter-spacing: 0.0625em;
25
+ color: rgba(255, 255, 255, 0.5);
26
+ background: transparent;
27
+ border: 1px solid transparent;
28
+ cursor: pointer;
29
+ transition: all 0.3s ease;
30
+ }
31
+
32
+ .tab:hover {
33
+ color: rgba(255, 255, 255, 0.8);
34
+ background: rgba(255, 255, 255, 0.05);
35
+ }
36
+
37
+ .active {
38
+ color: var(--tabs-color);
39
+ background: rgba(0, 240, 255, 0.1);
40
+ border-color: var(--tabs-color);
41
+ box-shadow: 0 0 10px var(--tabs-color), inset 0 0 10px rgba(0, 240, 255, 0.1);
42
+ }
43
+
44
+ .icon { font-size: 0.875rem; }
45
+ .label { white-space: nowrap; }
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+
5
+ export interface NeonTab {
6
+ id: string;
7
+ label: string;
8
+ icon?: React.ReactNode;
9
+ }
10
+
11
+ export interface NeonTabsProps extends HTMLAttributes<HTMLDivElement> {
12
+ tabs: NeonTab[];
13
+ activeTab: string;
14
+ onChange: (tabId: string) => void;
15
+ variant?: 'cyan' | 'pink' | 'green' | 'purple';
16
+ }
17
+
18
+ const variantStyles = {
19
+ cyan: { active: 'text-cyan-400 bg-cyan-400/10 border-cyan-400 shadow-[0_0_10px_#00f0ff]' },
20
+ pink: { active: 'text-fuchsia-500 bg-fuchsia-500/10 border-fuchsia-500 shadow-[0_0_10px_#ff00ff]' },
21
+ green: { active: 'text-emerald-400 bg-emerald-400/10 border-emerald-400 shadow-[0_0_10px_#00ff88]' },
22
+ purple: { active: 'text-purple-500 bg-purple-500/10 border-purple-500 shadow-[0_0_10px_#a855f7]' },
23
+ };
24
+
25
+ export const NeonTabs = forwardRef<HTMLDivElement, NeonTabsProps>(
26
+ ({ tabs, activeTab, onChange, variant = 'cyan', className = '', ...props }, ref) => {
27
+ const colors = variantStyles[variant];
28
+ return (
29
+ <div ref={ref} className={`flex gap-1 p-1 bg-black/50 border border-white/10 ${className}`} role="tablist" {...props}>
30
+ {tabs.map((tab) => (
31
+ <button
32
+ key={tab.id}
33
+ role="tab"
34
+ aria-selected={activeTab === tab.id}
35
+ className={`
36
+ flex items-center gap-2 px-5 py-2.5
37
+ font-['Orbitron',sans-serif] text-xs font-semibold uppercase tracking-wide
38
+ border transition-all duration-300 cursor-pointer
39
+ ${activeTab === tab.id ? colors.active : 'text-white/50 bg-transparent border-transparent hover:text-white/80 hover:bg-white/5'}
40
+ `}
41
+ onClick={() => onChange(tab.id)}
42
+ >
43
+ {tab.icon && <span className="text-sm">{tab.icon}</span>}
44
+ <span className="whitespace-nowrap">{tab.label}</span>
45
+ </button>
46
+ ))}
47
+ </div>
48
+ );
49
+ }
50
+ );
51
+
52
+ NeonTabs.displayName = 'NeonTabs';
53
+ export default NeonTabs;
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, ButtonHTMLAttributes } from 'react';
4
+ import styles from './neon-toggle.module.css';
5
+
6
+ export interface NeonToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
7
+ /** Current checked state */
8
+ checked: boolean;
9
+ /** Change callback */
10
+ onChange: (checked: boolean) => void;
11
+ /** Neon color variant */
12
+ variant?: 'cyan' | 'pink' | 'green' | 'purple';
13
+ /** Toggle size */
14
+ size?: 'sm' | 'md' | 'lg';
15
+ }
16
+
17
+ export const NeonToggle = forwardRef<HTMLButtonElement, NeonToggleProps>(
18
+ (
19
+ {
20
+ checked,
21
+ onChange,
22
+ variant = 'cyan',
23
+ size = 'md',
24
+ disabled = false,
25
+ className,
26
+ ...props
27
+ },
28
+ ref
29
+ ) => {
30
+ const classes = [
31
+ styles.toggle,
32
+ styles[variant],
33
+ styles[size],
34
+ checked && styles.checked,
35
+ className,
36
+ ].filter(Boolean).join(' ');
37
+
38
+ return (
39
+ <button
40
+ ref={ref}
41
+ type="button"
42
+ role="switch"
43
+ aria-checked={checked}
44
+ disabled={disabled}
45
+ className={classes}
46
+ onClick={() => !disabled && onChange(!checked)}
47
+ {...props}
48
+ >
49
+ <span className={styles.track} />
50
+ <span className={styles.thumb} />
51
+ </button>
52
+ );
53
+ }
54
+ );
55
+
56
+ NeonToggle.displayName = 'NeonToggle';
57
+
58
+ export default NeonToggle;
@@ -0,0 +1,79 @@
1
+ .toggle {
2
+ --toggle-color: #00f0ff;
3
+ --toggle-width: 3.25rem;
4
+ --toggle-height: 1.75rem;
5
+ --thumb-size: 1.25rem;
6
+
7
+ position: relative;
8
+ width: var(--toggle-width);
9
+ height: var(--toggle-height);
10
+ padding: 0;
11
+ background: transparent;
12
+ border: none;
13
+ cursor: pointer;
14
+ }
15
+
16
+ /* Sizes */
17
+ .sm {
18
+ --toggle-width: 2.5rem;
19
+ --toggle-height: 1.375rem;
20
+ --thumb-size: 1rem;
21
+ }
22
+
23
+ .lg {
24
+ --toggle-width: 4rem;
25
+ --toggle-height: 2.125rem;
26
+ --thumb-size: 1.625rem;
27
+ }
28
+
29
+ /* Variants */
30
+ .cyan { --toggle-color: #00f0ff; }
31
+ .pink { --toggle-color: #ff00ff; }
32
+ .green { --toggle-color: #00ff88; }
33
+ .purple { --toggle-color: #a855f7; }
34
+
35
+ .track {
36
+ position: absolute;
37
+ top: 0;
38
+ left: 0;
39
+ width: 100%;
40
+ height: 100%;
41
+ background: rgba(255, 255, 255, 0.1);
42
+ border: 1px solid rgba(255, 255, 255, 0.2);
43
+ border-radius: var(--toggle-height);
44
+ transition: all 0.3s ease;
45
+ }
46
+
47
+ .checked .track {
48
+ background: rgba(0, 240, 255, 0.2);
49
+ border-color: var(--toggle-color);
50
+ box-shadow: 0 0 10px var(--toggle-color), inset 0 0 10px rgba(0, 240, 255, 0.1);
51
+ }
52
+
53
+ .thumb {
54
+ position: absolute;
55
+ top: 50%;
56
+ left: 0.25rem;
57
+ width: var(--thumb-size);
58
+ height: var(--thumb-size);
59
+ background: #666;
60
+ border-radius: 50%;
61
+ transform: translateY(-50%);
62
+ transition: all 0.3s ease;
63
+ }
64
+
65
+ .checked .thumb {
66
+ left: calc(100% - var(--thumb-size) - 0.25rem);
67
+ background: var(--toggle-color);
68
+ box-shadow: 0 0 10px var(--toggle-color), 0 0 20px var(--toggle-color);
69
+ }
70
+
71
+ .toggle:disabled {
72
+ opacity: 0.5;
73
+ cursor: not-allowed;
74
+ }
75
+
76
+ .toggle:focus-visible .track {
77
+ outline: 2px solid var(--toggle-color);
78
+ outline-offset: 2px;
79
+ }
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, ButtonHTMLAttributes } from 'react';
4
+
5
+ export interface NeonToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
6
+ checked: boolean;
7
+ onChange: (checked: boolean) => void;
8
+ variant?: 'cyan' | 'pink' | 'green' | 'purple';
9
+ size?: 'sm' | 'md' | 'lg';
10
+ }
11
+
12
+ const sizeStyles = {
13
+ sm: { track: 'w-10 h-5', thumb: 'w-4 h-4', translate: 'translate-x-5' },
14
+ md: { track: 'w-[52px] h-7', thumb: 'w-5 h-5', translate: 'translate-x-6' },
15
+ lg: { track: 'w-16 h-8', thumb: 'w-6 h-6', translate: 'translate-x-8' },
16
+ };
17
+
18
+ const variantStyles = {
19
+ cyan: { active: 'bg-cyan-400/20 border-cyan-400 shadow-[0_0_10px_#00f0ff]', thumb: 'bg-cyan-400 shadow-[0_0_10px_#00f0ff]' },
20
+ pink: { active: 'bg-fuchsia-500/20 border-fuchsia-500 shadow-[0_0_10px_#ff00ff]', thumb: 'bg-fuchsia-500 shadow-[0_0_10px_#ff00ff]' },
21
+ green: { active: 'bg-emerald-400/20 border-emerald-400 shadow-[0_0_10px_#00ff88]', thumb: 'bg-emerald-400 shadow-[0_0_10px_#00ff88]' },
22
+ purple: { active: 'bg-purple-500/20 border-purple-500 shadow-[0_0_10px_#a855f7]', thumb: 'bg-purple-500 shadow-[0_0_10px_#a855f7]' },
23
+ };
24
+
25
+ export const NeonToggle = forwardRef<HTMLButtonElement, NeonToggleProps>(
26
+ ({ checked, onChange, variant = 'cyan', size = 'md', disabled = false, className = '', ...props }, ref) => {
27
+ const sizes = sizeStyles[size];
28
+ const colors = variantStyles[variant];
29
+
30
+ return (
31
+ <button
32
+ ref={ref}
33
+ type="button"
34
+ role="switch"
35
+ aria-checked={checked}
36
+ disabled={disabled}
37
+ className={`
38
+ relative ${sizes.track} rounded-full border transition-all duration-300
39
+ ${checked ? colors.active : 'bg-white/10 border-white/20'}
40
+ ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
41
+ ${className}
42
+ `}
43
+ onClick={() => !disabled && onChange(!checked)}
44
+ {...props}
45
+ >
46
+ <span className={`
47
+ absolute top-1/2 left-1 -translate-y-1/2
48
+ ${sizes.thumb} rounded-full transition-all duration-300
49
+ ${checked ? `${sizes.translate} ${colors.thumb}` : 'bg-gray-500'}
50
+ `} />
51
+ </button>
52
+ );
53
+ }
54
+ );
55
+
56
+ NeonToggle.displayName = 'NeonToggle';
57
+ export default NeonToggle;
@@ -0,0 +1,173 @@
1
+ .container {
2
+ display: block;
3
+ font-family: 'Courier New', 'Monaco', 'Consolas', monospace;
4
+ white-space: pre;
5
+ line-height: 1.2;
6
+ letter-spacing: 0;
7
+ overflow-x: auto;
8
+ }
9
+
10
+ /* Size variants */
11
+ .xs { font-size: 0.5rem; }
12
+ .sm { font-size: 0.65rem; }
13
+ .md { font-size: 0.8rem; }
14
+ .lg { font-size: 1rem; }
15
+ .xl { font-size: 1.25rem; }
16
+
17
+ /* Color variants */
18
+ .blood {
19
+ color: #ff0040;
20
+ text-shadow: 0 0 5px #ff004080;
21
+ }
22
+
23
+ .cyber {
24
+ color: #00ffff;
25
+ text-shadow: 0 0 5px #00ffff80;
26
+ }
27
+
28
+ .matrix {
29
+ color: #00ff00;
30
+ text-shadow: 0 0 5px #00ff0080;
31
+ }
32
+
33
+ .amber {
34
+ color: #ffaa00;
35
+ text-shadow: 0 0 5px #ffaa0080;
36
+ }
37
+
38
+ .ghost {
39
+ color: #666;
40
+ opacity: 0.7;
41
+ }
42
+
43
+ .gradient {
44
+ background: linear-gradient(180deg, #ff0040 0%, #00ffff 50%, #00ff00 100%);
45
+ -webkit-background-clip: text;
46
+ -webkit-text-fill-color: transparent;
47
+ background-clip: text;
48
+ }
49
+
50
+ /* Animation variants */
51
+ .typing {
52
+ overflow: hidden;
53
+ white-space: nowrap;
54
+ animation: typeIn 2s steps(40, end) forwards;
55
+ }
56
+
57
+ @keyframes typeIn {
58
+ from { max-width: 0; }
59
+ to { max-width: 100%; }
60
+ }
61
+
62
+ .reveal .line {
63
+ opacity: 0;
64
+ animation: lineReveal 0.5s forwards;
65
+ }
66
+
67
+ @keyframes lineReveal {
68
+ from { opacity: 0; transform: translateX(-10px); }
69
+ to { opacity: 1; transform: translateX(0); }
70
+ }
71
+
72
+ .glitch {
73
+ position: relative;
74
+ }
75
+
76
+ .glitch::before,
77
+ .glitch::after {
78
+ content: attr(data-text);
79
+ position: absolute;
80
+ top: 0;
81
+ left: 0;
82
+ width: 100%;
83
+ height: 100%;
84
+ pointer-events: none;
85
+ }
86
+
87
+ .glitch::before {
88
+ color: #ff0040;
89
+ animation: asciiGlitch1 3s infinite;
90
+ clip-path: polygon(0 0, 100% 0, 100% 45%, 0 45%);
91
+ }
92
+
93
+ .glitch::after {
94
+ color: #00ffff;
95
+ animation: asciiGlitch2 3s infinite;
96
+ clip-path: polygon(0 55%, 100% 55%, 100% 100%, 0 100%);
97
+ }
98
+
99
+ @keyframes asciiGlitch1 {
100
+ 0%, 90%, 100% { transform: translate(0); opacity: 0; }
101
+ 92% { transform: translate(-2px, 0); opacity: 0.8; }
102
+ 94% { transform: translate(2px, 0); opacity: 0.8; }
103
+ }
104
+
105
+ @keyframes asciiGlitch2 {
106
+ 0%, 90%, 100% { transform: translate(0); opacity: 0; }
107
+ 93% { transform: translate(2px, 0); opacity: 0.8; }
108
+ 95% { transform: translate(-2px, 0); opacity: 0.8; }
109
+ }
110
+
111
+ /* Scanline effect */
112
+ .scanlines {
113
+ position: relative;
114
+ }
115
+
116
+ .scanlines::after {
117
+ content: '';
118
+ position: absolute;
119
+ top: 0;
120
+ left: 0;
121
+ right: 0;
122
+ bottom: 0;
123
+ background: repeating-linear-gradient(
124
+ 0deg,
125
+ transparent,
126
+ transparent 2px,
127
+ rgba(0, 0, 0, 0.1) 2px,
128
+ rgba(0, 0, 0, 0.1) 4px
129
+ );
130
+ pointer-events: none;
131
+ }
132
+
133
+ /* Border styles */
134
+ .bordered {
135
+ border: 1px solid currentColor;
136
+ padding: 1rem;
137
+ position: relative;
138
+ }
139
+
140
+ .bordered::before {
141
+ content: '┌' attr(data-title) '┐';
142
+ position: absolute;
143
+ top: -0.6em;
144
+ left: 1rem;
145
+ background: inherit;
146
+ padding: 0 0.5rem;
147
+ font-size: 0.8em;
148
+ }
149
+
150
+ /* Flicker effect */
151
+ .flicker {
152
+ animation: asciiFlicker 0.15s infinite;
153
+ }
154
+
155
+ @keyframes asciiFlicker {
156
+ 0%, 100% { opacity: 1; }
157
+ 50% { opacity: 0.8; }
158
+ }
159
+
160
+ /* Pulse effect */
161
+ .pulse {
162
+ animation: asciiPulse 2s ease-in-out infinite;
163
+ }
164
+
165
+ @keyframes asciiPulse {
166
+ 0%, 100% { opacity: 0.6; }
167
+ 50% { opacity: 1; }
168
+ }
169
+
170
+ /* Line container for reveal animation */
171
+ .line {
172
+ display: block;
173
+ }
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState } from 'react';
4
+ import styles from './ascii-art.module.css';
5
+
6
+ export interface AsciiArtProps extends HTMLAttributes<HTMLPreElement> {
7
+ /** ASCII art content (multi-line string) */
8
+ children: string;
9
+ /** Size variant */
10
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
+ /** Color variant */
12
+ variant?: 'default' | 'blood' | 'cyber' | 'matrix' | 'amber' | 'ghost' | 'gradient';
13
+ /** Animation effect */
14
+ animation?: 'none' | 'typing' | 'reveal' | 'glitch' | 'flicker' | 'pulse';
15
+ /** Show scanlines overlay */
16
+ scanlines?: boolean;
17
+ /** Show border */
18
+ bordered?: boolean;
19
+ /** Border title */
20
+ title?: string;
21
+ /** Delay between lines for reveal animation (ms) */
22
+ revealDelay?: number;
23
+ /** Custom color */
24
+ color?: string;
25
+ }
26
+
27
+ export const AsciiArt = forwardRef<HTMLPreElement, AsciiArtProps>(
28
+ (
29
+ {
30
+ children,
31
+ size = 'md',
32
+ variant = 'default',
33
+ animation = 'none',
34
+ scanlines = false,
35
+ bordered = false,
36
+ title,
37
+ revealDelay = 100,
38
+ color,
39
+ className,
40
+ style,
41
+ ...props
42
+ },
43
+ ref
44
+ ) => {
45
+ const [revealedLines, setRevealedLines] = useState<number>(0);
46
+ const lines = children.split('\n');
47
+
48
+ useEffect(() => {
49
+ if (animation === 'reveal') {
50
+ setRevealedLines(0);
51
+ let lineIndex = 0;
52
+
53
+ const interval = setInterval(() => {
54
+ if (lineIndex < lines.length) {
55
+ setRevealedLines(lineIndex + 1);
56
+ lineIndex++;
57
+ } else {
58
+ clearInterval(interval);
59
+ }
60
+ }, revealDelay);
61
+
62
+ return () => clearInterval(interval);
63
+ }
64
+ }, [animation, lines.length, revealDelay, children]);
65
+
66
+ const containerClasses = [
67
+ styles.container,
68
+ styles[size],
69
+ variant !== 'default' && styles[variant],
70
+ animation !== 'none' && styles[animation],
71
+ scanlines && styles.scanlines,
72
+ bordered && styles.bordered,
73
+ className
74
+ ].filter(Boolean).join(' ');
75
+
76
+ const renderContent = () => {
77
+ if (animation === 'reveal') {
78
+ return lines.map((line, i) => (
79
+ <span
80
+ key={i}
81
+ className={styles.line}
82
+ style={{
83
+ opacity: i < revealedLines ? 1 : 0,
84
+ animationDelay: `${i * revealDelay}ms`,
85
+ transform: i < revealedLines ? 'translateX(0)' : 'translateX(-10px)',
86
+ transition: 'opacity 0.3s ease, transform 0.3s ease',
87
+ }}
88
+ >
89
+ {line}
90
+ {'\n'}
91
+ </span>
92
+ ));
93
+ }
94
+ return children;
95
+ };
96
+
97
+ return (
98
+ <pre
99
+ ref={ref}
100
+ className={containerClasses}
101
+ style={{
102
+ color: color,
103
+ ...style
104
+ }}
105
+ data-text={animation === 'glitch' ? children : undefined}
106
+ data-title={bordered ? title : undefined}
107
+ {...props}
108
+ >
109
+ {renderContent()}
110
+ </pre>
111
+ );
112
+ }
113
+ );
114
+
115
+ AsciiArt.displayName = 'AsciiArt';
116
+ export default AsciiArt;