@oalacea/chaosui 0.4.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 (68) hide show
  1. package/bin/cli.js +32 -2
  2. package/components/buttons/cta-brutal/cta-brutal.module.css +81 -0
  3. package/components/buttons/cta-brutal/index.tsx +56 -0
  4. package/components/buttons/dead-button/dead-button.module.css +111 -0
  5. package/components/buttons/dead-button/index.tsx +47 -0
  6. package/components/buttons/deeper-button/deeper-button.module.css +76 -0
  7. package/components/buttons/deeper-button/index.tsx +51 -0
  8. package/components/buttons/dual-choice/dual-choice.module.css +90 -0
  9. package/components/buttons/dual-choice/index.tsx +54 -0
  10. package/components/buttons/tension-bar/index.tsx +79 -0
  11. package/components/buttons/tension-bar/tension-bar.module.css +105 -0
  12. package/components/decorative/coffee-stain/coffee-stain.module.css +24 -0
  13. package/components/decorative/coffee-stain/index.tsx +55 -0
  14. package/components/decorative/ornaments/index.tsx +51 -0
  15. package/components/decorative/ornaments/ornaments.module.css +33 -0
  16. package/components/decorative/rune-symbols/index.tsx +55 -0
  17. package/components/decorative/rune-symbols/rune-symbols.module.css +22 -0
  18. package/components/layout/horizontal-scroll/horizontal-scroll.module.css +30 -0
  19. package/components/layout/horizontal-scroll/index.tsx +78 -0
  20. package/components/layout/spec-grid/index.tsx +56 -0
  21. package/components/layout/spec-grid/spec-grid.module.css +21 -0
  22. package/components/layout/tower-pricing/index.tsx +56 -0
  23. package/components/layout/tower-pricing/tower-pricing.module.css +27 -0
  24. package/components/layout/tracklist/index.tsx +45 -0
  25. package/components/layout/tracklist/tracklist.module.css +24 -0
  26. package/components/layout/void-frame/index.tsx +32 -0
  27. package/components/layout/void-frame/void-frame.module.css +38 -0
  28. package/components/navigation/brutal-nav/brutal-nav.module.css +85 -0
  29. package/components/navigation/brutal-nav/index.tsx +71 -0
  30. package/components/navigation/progress-dots/index.tsx +55 -0
  31. package/components/navigation/progress-dots/progress-dots.module.css +91 -0
  32. package/components/navigation/scattered-nav/index.tsx +59 -0
  33. package/components/navigation/scattered-nav/scattered-nav.module.css +113 -0
  34. package/components/navigation/scroll-indicator/index.tsx +58 -0
  35. package/components/navigation/scroll-indicator/scroll-indicator.module.css +82 -0
  36. package/components/navigation/vertical-nav/index.tsx +59 -0
  37. package/components/navigation/vertical-nav/vertical-nav.module.css +98 -0
  38. package/components/text/ascii-art/css/ascii-art.module.css +173 -0
  39. package/components/text/ascii-art/css/index.tsx +116 -0
  40. package/components/text/ascii-art/tailwind/index.tsx +124 -0
  41. package/components/text/blood-drip/css/blood-drip.module.css +142 -0
  42. package/components/text/blood-drip/css/index.tsx +113 -0
  43. package/components/text/blood-drip/tailwind/index.tsx +133 -0
  44. package/components/text/char-glitch/css/char-glitch.module.css +124 -0
  45. package/components/text/char-glitch/css/index.tsx +153 -0
  46. package/components/text/char-glitch/tailwind/index.tsx +126 -0
  47. package/components/text/countdown-display/css/countdown-display.module.css +179 -0
  48. package/components/text/countdown-display/css/index.tsx +190 -0
  49. package/components/text/countdown-display/tailwind/index.tsx +155 -0
  50. package/components/text/giant-layers/css/giant-layers.module.css +156 -0
  51. package/components/text/giant-layers/css/index.tsx +97 -0
  52. package/components/text/giant-layers/tailwind/index.tsx +111 -0
  53. package/components/text/reveal-text/css/index.tsx +180 -0
  54. package/components/text/reveal-text/css/reveal-text.module.css +129 -0
  55. package/components/text/reveal-text/tailwind/index.tsx +135 -0
  56. package/components/text/rotate-text/css/index.tsx +139 -0
  57. package/components/text/rotate-text/css/rotate-text.module.css +162 -0
  58. package/components/text/rotate-text/tailwind/index.tsx +127 -0
  59. package/components/text/strike-reveal/css/index.tsx +124 -0
  60. package/components/text/strike-reveal/css/strike-reveal.module.css +139 -0
  61. package/components/text/strike-reveal/tailwind/index.tsx +138 -0
  62. package/components/text/terminal-output/css/index.tsx +179 -0
  63. package/components/text/terminal-output/css/terminal-output.module.css +203 -0
  64. package/components/text/terminal-output/tailwind/index.tsx +174 -0
  65. package/components/text/typing-text/css/index.tsx +115 -0
  66. package/components/text/typing-text/css/typing-text.module.css +84 -0
  67. package/components/text/typing-text/tailwind/index.tsx +126 -0
  68. package/package.json +1 -1
@@ -0,0 +1,124 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState, useRef } from 'react';
4
+ import styles from './strike-reveal.module.css';
5
+
6
+ export interface StrikeRevealProps extends HTMLAttributes<HTMLSpanElement> {
7
+ /** Original text to strike through */
8
+ children: string;
9
+ /** Text to reveal after strike */
10
+ revealText?: string;
11
+ /** Visual variant */
12
+ variant?: 'permanent' | 'crossout' | 'redacted' | 'censored' | 'glitch';
13
+ /** Color variant */
14
+ color?: 'default' | 'blood' | 'cyber' | 'acid' | 'void';
15
+ /** Trigger mode */
16
+ trigger?: 'auto' | 'hover' | 'scroll' | 'click';
17
+ /** Delay before animation (ms) */
18
+ delay?: number;
19
+ /** Double strike line */
20
+ double?: boolean;
21
+ /** Custom strike color */
22
+ strikeColor?: string;
23
+ /** Callback when reveal completes */
24
+ onReveal?: () => void;
25
+ }
26
+
27
+ export const StrikeReveal = forwardRef<HTMLSpanElement, StrikeRevealProps>(
28
+ (
29
+ {
30
+ children,
31
+ revealText,
32
+ variant = 'permanent',
33
+ color = 'default',
34
+ trigger = 'auto',
35
+ delay = 0,
36
+ double = false,
37
+ strikeColor,
38
+ onReveal,
39
+ className,
40
+ style,
41
+ ...props
42
+ },
43
+ ref
44
+ ) => {
45
+ const [isActive, setIsActive] = useState(false);
46
+ const containerRef = useRef<HTMLSpanElement>(null);
47
+
48
+ useEffect(() => {
49
+ if (trigger === 'auto') {
50
+ const timeout = setTimeout(() => {
51
+ setIsActive(true);
52
+ setTimeout(() => onReveal?.(), 700);
53
+ }, delay);
54
+ return () => clearTimeout(timeout);
55
+ }
56
+
57
+ if (trigger === 'scroll') {
58
+ const observer = new IntersectionObserver(
59
+ ([entry]) => {
60
+ if (entry.isIntersecting) {
61
+ setTimeout(() => {
62
+ setIsActive(true);
63
+ setTimeout(() => onReveal?.(), 700);
64
+ }, delay);
65
+ }
66
+ },
67
+ { threshold: 0.5 }
68
+ );
69
+
70
+ if (containerRef.current) {
71
+ observer.observe(containerRef.current);
72
+ }
73
+
74
+ return () => observer.disconnect();
75
+ }
76
+ }, [trigger, delay, onReveal]);
77
+
78
+ const handleClick = () => {
79
+ if (trigger === 'click') {
80
+ setIsActive(true);
81
+ setTimeout(() => onReveal?.(), 700);
82
+ }
83
+ };
84
+
85
+ const containerClasses = [
86
+ styles.container,
87
+ styles[variant],
88
+ color !== 'default' && styles[color],
89
+ double && styles.double,
90
+ trigger === 'hover' && styles.hover,
91
+ isActive && styles.active,
92
+ className
93
+ ].filter(Boolean).join(' ');
94
+
95
+ return (
96
+ <span
97
+ ref={(node) => {
98
+ (containerRef as React.MutableRefObject<HTMLSpanElement | null>).current = node;
99
+ if (typeof ref === 'function') ref(node);
100
+ else if (ref) ref.current = node;
101
+ }}
102
+ className={containerClasses}
103
+ style={{
104
+ '--strike-color': strikeColor,
105
+ '--delay': `${delay}ms`,
106
+ ...style
107
+ } as React.CSSProperties}
108
+ onClick={handleClick}
109
+ {...props}
110
+ >
111
+ <span className={styles.text}>
112
+ {children}
113
+ <span className={styles.strike} />
114
+ </span>
115
+ {revealText && (
116
+ <span className={styles.revealed}>{revealText}</span>
117
+ )}
118
+ </span>
119
+ );
120
+ }
121
+ );
122
+
123
+ StrikeReveal.displayName = 'StrikeReveal';
124
+ export default StrikeReveal;
@@ -0,0 +1,139 @@
1
+ .container {
2
+ position: relative;
3
+ display: inline-block;
4
+ }
5
+
6
+ .text {
7
+ position: relative;
8
+ display: inline;
9
+ }
10
+
11
+ /* Strike line */
12
+ .strike {
13
+ position: absolute;
14
+ left: 0;
15
+ top: 50%;
16
+ width: 0;
17
+ height: 2px;
18
+ background: currentColor;
19
+ transform: translateY(-50%);
20
+ transition: width 0.4s cubic-bezier(0.19, 1, 0.22, 1);
21
+ }
22
+
23
+ .container.active .strike {
24
+ width: 100%;
25
+ }
26
+
27
+ /* Hidden text that reveals */
28
+ .revealed {
29
+ display: block;
30
+ opacity: 0;
31
+ transform: translateY(10px);
32
+ transition: opacity 0.4s ease, transform 0.4s ease;
33
+ transition-delay: 0.3s;
34
+ margin-top: 0.5em;
35
+ }
36
+
37
+ .container.active .revealed {
38
+ opacity: 1;
39
+ transform: translateY(0);
40
+ }
41
+
42
+ /* Variant: strikethrough stays */
43
+ .permanent .strike {
44
+ background: var(--strike-color, #ff0040);
45
+ }
46
+
47
+ .permanent.active .text {
48
+ opacity: 0.5;
49
+ }
50
+
51
+ /* Variant: text fades then reveals */
52
+ .crossout .text {
53
+ transition: opacity 0.3s ease;
54
+ }
55
+
56
+ .crossout.active .text {
57
+ opacity: 0.3;
58
+ }
59
+
60
+ /* Variant: redacted style */
61
+ .redacted .strike {
62
+ height: 1em;
63
+ background: var(--strike-color, #0a0a0a);
64
+ top: 50%;
65
+ transform: translateY(-50%);
66
+ }
67
+
68
+ .redacted.active .text {
69
+ color: transparent;
70
+ }
71
+
72
+ /* Variant: censored with bars */
73
+ .censored {
74
+ position: relative;
75
+ }
76
+
77
+ .censored .strike {
78
+ height: 100%;
79
+ background: repeating-linear-gradient(
80
+ 90deg,
81
+ var(--strike-color, #0a0a0a) 0px,
82
+ var(--strike-color, #0a0a0a) 8px,
83
+ transparent 8px,
84
+ transparent 12px
85
+ );
86
+ top: 0;
87
+ transform: none;
88
+ }
89
+
90
+ /* Variant: glitch strike */
91
+ .glitch .strike {
92
+ background: var(--strike-color, #ff0040);
93
+ animation: none;
94
+ }
95
+
96
+ .glitch.active .strike {
97
+ animation: glitchStrike 0.5s ease forwards;
98
+ }
99
+
100
+ @keyframes glitchStrike {
101
+ 0% { width: 0; transform: translateY(-50%); }
102
+ 30% { width: 110%; transform: translateY(-50%) translateX(-5%); }
103
+ 50% { width: 95%; transform: translateY(-50%) translateX(3%); }
104
+ 70% { width: 102%; transform: translateY(-50%) translateX(-2%); }
105
+ 100% { width: 100%; transform: translateY(-50%); }
106
+ }
107
+
108
+ /* Colors */
109
+ .blood .strike { background: #ff0040; }
110
+ .cyber .strike { background: #00ffff; }
111
+ .acid .strike { background: #aaff00; }
112
+ .void .strike { background: #0a0a0a; }
113
+
114
+ /* Hover trigger */
115
+ .hover:hover .strike {
116
+ width: 100%;
117
+ }
118
+
119
+ .hover:hover .revealed {
120
+ opacity: 1;
121
+ transform: translateY(0);
122
+ }
123
+
124
+ /* Animation on scroll */
125
+ .scroll .strike {
126
+ transition-delay: var(--delay, 0ms);
127
+ }
128
+
129
+ /* Double strike */
130
+ .double .strike::after {
131
+ content: '';
132
+ position: absolute;
133
+ left: 0;
134
+ top: 4px;
135
+ width: 100%;
136
+ height: 2px;
137
+ background: inherit;
138
+ opacity: 0.5;
139
+ }
@@ -0,0 +1,138 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState, useRef } from 'react';
4
+
5
+ export interface StrikeRevealProps extends HTMLAttributes<HTMLSpanElement> {
6
+ children: string;
7
+ revealText?: string;
8
+ variant?: 'permanent' | 'crossout' | 'redacted' | 'censored' | 'glitch';
9
+ color?: 'default' | 'blood' | 'cyber' | 'acid' | 'void';
10
+ trigger?: 'auto' | 'hover' | 'scroll' | 'click';
11
+ delay?: number;
12
+ double?: boolean;
13
+ strikeColor?: string;
14
+ onReveal?: () => void;
15
+ }
16
+
17
+ const colorStyles = {
18
+ default: 'bg-current',
19
+ blood: 'bg-rose-500',
20
+ cyber: 'bg-cyan-400',
21
+ acid: 'bg-lime-400',
22
+ void: 'bg-[#0a0a0a]',
23
+ };
24
+
25
+ export const StrikeReveal = forwardRef<HTMLSpanElement, StrikeRevealProps>(
26
+ (
27
+ {
28
+ children,
29
+ revealText,
30
+ variant = 'permanent',
31
+ color = 'default',
32
+ trigger = 'auto',
33
+ delay = 0,
34
+ double = false,
35
+ strikeColor,
36
+ onReveal,
37
+ className = '',
38
+ style,
39
+ ...props
40
+ },
41
+ ref
42
+ ) => {
43
+ const [isActive, setIsActive] = useState(false);
44
+ const containerRef = useRef<HTMLSpanElement>(null);
45
+
46
+ useEffect(() => {
47
+ if (trigger === 'auto') {
48
+ const timeout = setTimeout(() => {
49
+ setIsActive(true);
50
+ setTimeout(() => onReveal?.(), 700);
51
+ }, delay);
52
+ return () => clearTimeout(timeout);
53
+ }
54
+
55
+ if (trigger === 'scroll') {
56
+ const observer = new IntersectionObserver(
57
+ ([entry]) => {
58
+ if (entry.isIntersecting) {
59
+ setTimeout(() => {
60
+ setIsActive(true);
61
+ setTimeout(() => onReveal?.(), 700);
62
+ }, delay);
63
+ }
64
+ },
65
+ { threshold: 0.5 }
66
+ );
67
+
68
+ if (containerRef.current) observer.observe(containerRef.current);
69
+ return () => observer.disconnect();
70
+ }
71
+ }, [trigger, delay, onReveal]);
72
+
73
+ const handleClick = () => {
74
+ if (trigger === 'click') {
75
+ setIsActive(true);
76
+ setTimeout(() => onReveal?.(), 700);
77
+ }
78
+ };
79
+
80
+ const handleMouseEnter = () => {
81
+ if (trigger === 'hover') setIsActive(true);
82
+ };
83
+
84
+ const handleMouseLeave = () => {
85
+ if (trigger === 'hover') setIsActive(false);
86
+ };
87
+
88
+ const strikeHeight = variant === 'redacted' ? 'h-[1em]' : variant === 'censored' ? 'h-full' : 'h-0.5';
89
+ const bgStyle = strikeColor ? { backgroundColor: strikeColor } : undefined;
90
+
91
+ return (
92
+ <span
93
+ ref={(node) => {
94
+ (containerRef as React.MutableRefObject<HTMLSpanElement | null>).current = node;
95
+ if (typeof ref === 'function') ref(node);
96
+ else if (ref) ref.current = node;
97
+ }}
98
+ className={`relative inline-block ${className}`}
99
+ style={style}
100
+ onClick={handleClick}
101
+ onMouseEnter={handleMouseEnter}
102
+ onMouseLeave={handleMouseLeave}
103
+ {...props}
104
+ >
105
+ <span className={`relative inline ${isActive && variant === 'crossout' ? 'opacity-30' : ''} ${isActive && variant === 'redacted' ? 'text-transparent' : ''} transition-opacity duration-300`}>
106
+ {children}
107
+ <span
108
+ className={`absolute left-0 top-1/2 -translate-y-1/2 ${strikeHeight} ${colorStyles[color]} transition-all duration-400 ease-out ${
109
+ isActive ? 'w-full' : 'w-0'
110
+ }`}
111
+ style={bgStyle}
112
+ />
113
+ {double && (
114
+ <span
115
+ className={`absolute left-0 top-1/2 translate-y-0.5 h-0.5 ${colorStyles[color]} opacity-50 transition-all duration-400 ease-out ${
116
+ isActive ? 'w-full' : 'w-0'
117
+ }`}
118
+ style={bgStyle}
119
+ />
120
+ )}
121
+ </span>
122
+ {revealText && (
123
+ <span
124
+ className={`block mt-2 transition-all duration-400 ease-out ${
125
+ isActive ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-2'
126
+ }`}
127
+ style={{ transitionDelay: '300ms' }}
128
+ >
129
+ {revealText}
130
+ </span>
131
+ )}
132
+ </span>
133
+ );
134
+ }
135
+ );
136
+
137
+ StrikeReveal.displayName = 'StrikeReveal';
138
+ export default StrikeReveal;
@@ -0,0 +1,179 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState, useRef } from 'react';
4
+ import styles from './terminal-output.module.css';
5
+
6
+ export interface TerminalLine {
7
+ type: 'command' | 'output' | 'error' | 'success' | 'warning' | 'info';
8
+ content: string;
9
+ prompt?: string;
10
+ delay?: number;
11
+ }
12
+
13
+ export interface TerminalOutputProps extends HTMLAttributes<HTMLDivElement> {
14
+ /** Lines to display */
15
+ lines: TerminalLine[];
16
+ /** Default prompt string */
17
+ prompt?: string;
18
+ /** Visual variant */
19
+ variant?: 'default' | 'hacker' | 'blood' | 'cyber';
20
+ /** Show window header with dots */
21
+ showHeader?: boolean;
22
+ /** Window title */
23
+ title?: string;
24
+ /** Animate lines appearing */
25
+ animated?: boolean;
26
+ /** Base delay between animated lines (ms) */
27
+ animationDelay?: number;
28
+ /** Show typing cursor on last command */
29
+ typingCursor?: boolean;
30
+ /** Enable scanlines overlay */
31
+ scanlines?: boolean;
32
+ /** Enable glow effect */
33
+ glowing?: boolean;
34
+ /** Show interactive input */
35
+ showInput?: boolean;
36
+ /** Input placeholder */
37
+ inputPlaceholder?: string;
38
+ /** Callback when command is submitted */
39
+ onCommand?: (command: string) => void;
40
+ /** Auto-scroll to bottom */
41
+ autoScroll?: boolean;
42
+ }
43
+
44
+ export const TerminalOutput = forwardRef<HTMLDivElement, TerminalOutputProps>(
45
+ (
46
+ {
47
+ lines,
48
+ prompt = '❯',
49
+ variant = 'default',
50
+ showHeader = true,
51
+ title = 'terminal',
52
+ animated = false,
53
+ animationDelay = 100,
54
+ typingCursor = false,
55
+ scanlines = false,
56
+ glowing = false,
57
+ showInput = false,
58
+ inputPlaceholder = 'Type a command...',
59
+ onCommand,
60
+ autoScroll = true,
61
+ className,
62
+ ...props
63
+ },
64
+ ref
65
+ ) => {
66
+ const [visibleLines, setVisibleLines] = useState<number>(animated ? 0 : lines.length);
67
+ const [inputValue, setInputValue] = useState('');
68
+ const contentRef = useRef<HTMLDivElement>(null);
69
+
70
+ useEffect(() => {
71
+ if (animated) {
72
+ setVisibleLines(0);
73
+ let index = 0;
74
+
75
+ const showLine = () => {
76
+ if (index < lines.length) {
77
+ const line = lines[index];
78
+ const delay = line.delay ?? animationDelay;
79
+
80
+ setTimeout(() => {
81
+ setVisibleLines(index + 1);
82
+ index++;
83
+ showLine();
84
+ }, delay);
85
+ }
86
+ };
87
+
88
+ showLine();
89
+ } else {
90
+ setVisibleLines(lines.length);
91
+ }
92
+ }, [lines, animated, animationDelay]);
93
+
94
+ useEffect(() => {
95
+ if (autoScroll && contentRef.current) {
96
+ contentRef.current.scrollTop = contentRef.current.scrollHeight;
97
+ }
98
+ }, [visibleLines, autoScroll]);
99
+
100
+ const handleSubmit = (e: React.FormEvent) => {
101
+ e.preventDefault();
102
+ if (inputValue.trim()) {
103
+ onCommand?.(inputValue);
104
+ setInputValue('');
105
+ }
106
+ };
107
+
108
+ const containerClasses = [
109
+ styles.container,
110
+ styles[variant],
111
+ animated && styles.animated,
112
+ scanlines && styles.scanlines,
113
+ glowing && styles.glowing,
114
+ className
115
+ ].filter(Boolean).join(' ');
116
+
117
+ return (
118
+ <div ref={ref} className={containerClasses} {...props}>
119
+ {showHeader && (
120
+ <div className={styles.header}>
121
+ <div className={styles.dots}>
122
+ <span className={`${styles.dot} ${styles.red}`} />
123
+ <span className={`${styles.dot} ${styles.yellow}`} />
124
+ <span className={`${styles.dot} ${styles.green}`} />
125
+ </div>
126
+ <span className={styles.title}>{title}</span>
127
+ </div>
128
+ )}
129
+
130
+ <div ref={contentRef} className={styles.content}>
131
+ {lines.slice(0, visibleLines).map((line, i) => {
132
+ const isLastCommand = i === visibleLines - 1 && line.type === 'command' && typingCursor;
133
+ const lineClasses = [
134
+ styles.line,
135
+ line.type !== 'command' && line.type !== 'output' && styles[line.type]
136
+ ].filter(Boolean).join(' ');
137
+
138
+ return (
139
+ <div
140
+ key={i}
141
+ className={lineClasses}
142
+ style={animated ? { animationDelay: `${i * 50}ms` } : undefined}
143
+ >
144
+ {line.type === 'command' && (
145
+ <>
146
+ <span className={styles.prompt}>{line.prompt || prompt}</span>
147
+ <span className={`${styles.command} ${isLastCommand ? styles.typing : ''}`}>
148
+ {line.content}
149
+ </span>
150
+ </>
151
+ )}
152
+ {line.type !== 'command' && (
153
+ <span className={styles.output}>{line.content}</span>
154
+ )}
155
+ </div>
156
+ );
157
+ })}
158
+
159
+ {showInput && (
160
+ <form onSubmit={handleSubmit} className={styles.inputLine}>
161
+ <span className={styles.prompt}>{prompt}</span>
162
+ <input
163
+ type="text"
164
+ value={inputValue}
165
+ onChange={(e) => setInputValue(e.target.value)}
166
+ placeholder={inputPlaceholder}
167
+ className={styles.input}
168
+ autoFocus
169
+ />
170
+ </form>
171
+ )}
172
+ </div>
173
+ </div>
174
+ );
175
+ }
176
+ );
177
+
178
+ TerminalOutput.displayName = 'TerminalOutput';
179
+ export default TerminalOutput;