@oalacea/chaosui 0.4.0 → 0.5.1

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 (178) hide show
  1. package/buttons/cta-brutal/css/cta-brutal.module.css +175 -0
  2. package/buttons/cta-brutal/css/index.tsx +78 -0
  3. package/buttons/cta-brutal/tailwind/index.tsx +85 -0
  4. package/buttons/dead-button/css/dead-button.module.css +216 -0
  5. package/buttons/dead-button/css/index.tsx +47 -0
  6. package/buttons/dead-button/tailwind/index.tsx +90 -0
  7. package/buttons/deeper-button/css/deeper-button.module.css +141 -0
  8. package/buttons/deeper-button/css/index.tsx +62 -0
  9. package/buttons/deeper-button/tailwind/index.tsx +78 -0
  10. package/buttons/dual-choice/css/dual-choice.module.css +176 -0
  11. package/buttons/dual-choice/css/index.tsx +56 -0
  12. package/buttons/dual-choice/tailwind/index.tsx +89 -0
  13. package/buttons/tension-bar/css/index.tsx +96 -0
  14. package/buttons/tension-bar/css/tension-bar.module.css +204 -0
  15. package/buttons/tension-bar/tailwind/index.tsx +122 -0
  16. package/decorative/inscription/css/index.tsx +66 -0
  17. package/decorative/inscription/css/inscription.module.css +160 -0
  18. package/decorative/inscription/tailwind/index.tsx +91 -0
  19. package/decorative/ornaments/css/index.tsx +136 -0
  20. package/decorative/ornaments/css/ornaments.module.css +144 -0
  21. package/decorative/ornaments/tailwind/index.tsx +122 -0
  22. package/decorative/rune-symbols/css/index.tsx +116 -0
  23. package/decorative/rune-symbols/css/rune-symbols.module.css +116 -0
  24. package/decorative/rune-symbols/tailwind/index.tsx +108 -0
  25. package/decorative/sheet-music/css/index.tsx +144 -0
  26. package/decorative/sheet-music/css/sheet-music.module.css +152 -0
  27. package/decorative/sheet-music/tailwind/index.tsx +111 -0
  28. package/layout/horizontal-scroll/css/horizontal-scroll.module.css +93 -0
  29. package/layout/horizontal-scroll/css/index.tsx +114 -0
  30. package/layout/horizontal-scroll/tailwind/index.tsx +108 -0
  31. package/layout/spec-grid/css/index.tsx +103 -0
  32. package/layout/spec-grid/css/spec-grid.module.css +125 -0
  33. package/layout/spec-grid/tailwind/index.tsx +91 -0
  34. package/layout/tower-pricing/css/index.tsx +91 -0
  35. package/layout/tower-pricing/css/tower-pricing.module.css +177 -0
  36. package/layout/tower-pricing/tailwind/index.tsx +106 -0
  37. package/layout/tracklist/css/index.tsx +96 -0
  38. package/layout/tracklist/css/tracklist.module.css +141 -0
  39. package/layout/tracklist/tailwind/index.tsx +92 -0
  40. package/layout/void-frame/css/index.tsx +60 -0
  41. package/layout/void-frame/css/void-frame.module.css +141 -0
  42. package/layout/void-frame/tailwind/index.tsx +64 -0
  43. package/navigation/brutal-nav/css/brutal-nav.module.css +178 -0
  44. package/navigation/brutal-nav/css/index.tsx +80 -0
  45. package/navigation/brutal-nav/tailwind/index.tsx +95 -0
  46. package/navigation/progress-dots/css/index.tsx +60 -0
  47. package/navigation/progress-dots/css/progress-dots.module.css +152 -0
  48. package/navigation/progress-dots/tailwind/index.tsx +105 -0
  49. package/navigation/scattered-nav/css/index.tsx +59 -0
  50. package/navigation/scattered-nav/css/scattered-nav.module.css +121 -0
  51. package/navigation/scattered-nav/tailwind/index.tsx +77 -0
  52. package/navigation/scroll-indicator/css/index.tsx +72 -0
  53. package/navigation/scroll-indicator/css/scroll-indicator.module.css +129 -0
  54. package/navigation/scroll-indicator/tailwind/index.tsx +107 -0
  55. package/navigation/vertical-nav/css/index.tsx +69 -0
  56. package/navigation/vertical-nav/css/vertical-nav.module.css +117 -0
  57. package/navigation/vertical-nav/tailwind/index.tsx +91 -0
  58. package/package.json +8 -33
  59. package/text/ascii-art/css/ascii-art.module.css +173 -0
  60. package/text/ascii-art/css/index.tsx +116 -0
  61. package/text/ascii-art/tailwind/index.tsx +124 -0
  62. package/text/blood-drip/css/blood-drip.module.css +142 -0
  63. package/text/blood-drip/css/index.tsx +113 -0
  64. package/text/blood-drip/tailwind/index.tsx +133 -0
  65. package/text/char-glitch/css/char-glitch.module.css +124 -0
  66. package/text/char-glitch/css/index.tsx +153 -0
  67. package/text/char-glitch/tailwind/index.tsx +126 -0
  68. package/text/countdown-display/css/countdown-display.module.css +179 -0
  69. package/text/countdown-display/css/index.tsx +190 -0
  70. package/text/countdown-display/tailwind/index.tsx +155 -0
  71. package/text/giant-layers/css/giant-layers.module.css +156 -0
  72. package/text/giant-layers/css/index.tsx +97 -0
  73. package/text/giant-layers/tailwind/index.tsx +111 -0
  74. package/text/reveal-text/css/index.tsx +180 -0
  75. package/text/reveal-text/css/reveal-text.module.css +129 -0
  76. package/text/reveal-text/tailwind/index.tsx +135 -0
  77. package/text/rotate-text/css/index.tsx +139 -0
  78. package/text/rotate-text/css/rotate-text.module.css +162 -0
  79. package/text/rotate-text/tailwind/index.tsx +127 -0
  80. package/text/strike-reveal/css/index.tsx +124 -0
  81. package/text/strike-reveal/css/strike-reveal.module.css +139 -0
  82. package/text/strike-reveal/tailwind/index.tsx +138 -0
  83. package/text/terminal-output/css/index.tsx +179 -0
  84. package/text/terminal-output/css/terminal-output.module.css +203 -0
  85. package/text/terminal-output/tailwind/index.tsx +174 -0
  86. package/text/typing-text/css/index.tsx +115 -0
  87. package/text/typing-text/css/typing-text.module.css +84 -0
  88. package/text/typing-text/tailwind/index.tsx +126 -0
  89. package/bin/cli.js +0 -278
  90. package/components/backgrounds/light-beams/index.tsx +0 -80
  91. package/components/backgrounds/light-beams/light-beams.module.css +0 -27
  92. package/components/navigation/hexagon-menu/css/hexagon-menu.module.css +0 -55
  93. package/components/navigation/hexagon-menu/css/index.tsx +0 -35
  94. package/components/navigation/hexagon-menu/tailwind/index.tsx +0 -53
  95. /package/{components/backgrounds → backgrounds}/glow-orbs/glow-orbs.module.css +0 -0
  96. /package/{components/backgrounds → backgrounds}/glow-orbs/index.tsx +0 -0
  97. /package/{components/backgrounds → backgrounds}/noise-canvas/index.tsx +0 -0
  98. /package/{components/backgrounds → backgrounds}/noise-canvas/noise-canvas.module.css +0 -0
  99. /package/{components/backgrounds → backgrounds}/particle-field/index.tsx +0 -0
  100. /package/{components/backgrounds → backgrounds}/particle-field/particle-field.module.css +0 -0
  101. /package/{components/buttons → buttons}/chaos-button/chaos-button.module.css +0 -0
  102. /package/{components/buttons → buttons}/chaos-button/index.tsx +0 -0
  103. /package/{components/buttons → buttons}/glitch-button/glitch-button.module.css +0 -0
  104. /package/{components/buttons → buttons}/glitch-button/index.tsx +0 -0
  105. /package/{components/chaos-vars.css → chaos-vars.css} +0 -0
  106. /package/{components/cyber → cyber}/cyber-avatar/css/cyber-avatar.module.css +0 -0
  107. /package/{components/cyber → cyber}/cyber-avatar/css/index.tsx +0 -0
  108. /package/{components/cyber → cyber}/cyber-avatar/tailwind/index.tsx +0 -0
  109. /package/{components/cyber → cyber}/cyber-input/css/cyber-input.module.css +0 -0
  110. /package/{components/cyber → cyber}/cyber-input/css/index.tsx +0 -0
  111. /package/{components/cyber → cyber}/cyber-input/tailwind/index.tsx +0 -0
  112. /package/{components/cyber → cyber}/cyber-loader/css/cyber-loader.module.css +0 -0
  113. /package/{components/cyber → cyber}/cyber-loader/css/index.tsx +0 -0
  114. /package/{components/cyber → cyber}/cyber-loader/tailwind/index.tsx +0 -0
  115. /package/{components/cyber → cyber}/cyber-modal/css/cyber-modal.module.css +0 -0
  116. /package/{components/cyber → cyber}/cyber-modal/css/index.tsx +0 -0
  117. /package/{components/cyber → cyber}/cyber-modal/tailwind/index.tsx +0 -0
  118. /package/{components/cyber → cyber}/cyber-slider/css/cyber-slider.module.css +0 -0
  119. /package/{components/cyber → cyber}/cyber-slider/css/index.tsx +0 -0
  120. /package/{components/cyber → cyber}/cyber-slider/tailwind/index.tsx +0 -0
  121. /package/{components/cyber → cyber}/cyber-tooltip/css/cyber-tooltip.module.css +0 -0
  122. /package/{components/cyber → cyber}/cyber-tooltip/css/index.tsx +0 -0
  123. /package/{components/cyber → cyber}/cyber-tooltip/tailwind/index.tsx +0 -0
  124. /package/{components/effects → effects}/cursor-follower/cursor-follower.module.css +0 -0
  125. /package/{components/effects → effects}/cursor-follower/index.tsx +0 -0
  126. /package/{components/effects → effects}/glitch-image/css/glitch-image.module.css +0 -0
  127. /package/{components/effects → effects}/glitch-image/css/index.tsx +0 -0
  128. /package/{components/effects → effects}/glitch-image/tailwind/index.tsx +0 -0
  129. /package/{components/effects → effects}/glowing-border/css/glowing-border.module.css +0 -0
  130. /package/{components/effects → effects}/glowing-border/css/index.tsx +0 -0
  131. /package/{components/effects → effects}/glowing-border/tailwind/index.tsx +0 -0
  132. /package/{components/effects → effects}/screen-distortion/index.tsx +0 -0
  133. /package/{components/effects → effects}/screen-distortion/screen-distortion.module.css +0 -0
  134. /package/{components/effects → effects}/warning-tape/index.tsx +0 -0
  135. /package/{components/effects → effects}/warning-tape/warning-tape.module.css +0 -0
  136. /package/{components/layout → layout}/data-grid/css/data-grid.module.css +0 -0
  137. /package/{components/layout → layout}/data-grid/css/index.tsx +0 -0
  138. /package/{components/layout → layout}/data-grid/tailwind/index.tsx +0 -0
  139. /package/{components/layout → layout}/hologram-card/css/hologram-card.module.css +0 -0
  140. /package/{components/layout → layout}/hologram-card/css/index.tsx +0 -0
  141. /package/{components/layout → layout}/hologram-card/tailwind/index.tsx +0 -0
  142. /package/{components/neon → neon}/neon-alert/css/index.tsx +0 -0
  143. /package/{components/neon → neon}/neon-alert/css/neon-alert.module.css +0 -0
  144. /package/{components/neon → neon}/neon-alert/tailwind/index.tsx +0 -0
  145. /package/{components/neon → neon}/neon-badge/css/index.tsx +0 -0
  146. /package/{components/neon → neon}/neon-badge/css/neon-badge.module.css +0 -0
  147. /package/{components/neon → neon}/neon-badge/tailwind/index.tsx +0 -0
  148. /package/{components/neon → neon}/neon-button/css/index.tsx +0 -0
  149. /package/{components/neon → neon}/neon-button/css/neon-button.module.css +0 -0
  150. /package/{components/neon → neon}/neon-button/tailwind/index.tsx +0 -0
  151. /package/{components/neon → neon}/neon-divider/css/index.tsx +0 -0
  152. /package/{components/neon → neon}/neon-divider/css/neon-divider.module.css +0 -0
  153. /package/{components/neon → neon}/neon-divider/tailwind/index.tsx +0 -0
  154. /package/{components/neon → neon}/neon-progress/css/index.tsx +0 -0
  155. /package/{components/neon → neon}/neon-progress/css/neon-progress.module.css +0 -0
  156. /package/{components/neon → neon}/neon-progress/tailwind/index.tsx +0 -0
  157. /package/{components/neon → neon}/neon-tabs/css/index.tsx +0 -0
  158. /package/{components/neon → neon}/neon-tabs/css/neon-tabs.module.css +0 -0
  159. /package/{components/neon → neon}/neon-tabs/tailwind/index.tsx +0 -0
  160. /package/{components/neon → neon}/neon-toggle/css/index.tsx +0 -0
  161. /package/{components/neon → neon}/neon-toggle/css/neon-toggle.module.css +0 -0
  162. /package/{components/neon → neon}/neon-toggle/tailwind/index.tsx +0 -0
  163. /package/{components/overlays → overlays}/noise-overlay/index.tsx +0 -0
  164. /package/{components/overlays → overlays}/noise-overlay/noise-overlay.module.css +0 -0
  165. /package/{components/overlays → overlays}/scanlines/index.tsx +0 -0
  166. /package/{components/overlays → overlays}/scanlines/scanlines.module.css +0 -0
  167. /package/{components/overlays → overlays}/static-flicker/index.tsx +0 -0
  168. /package/{components/overlays → overlays}/static-flicker/static-flicker.module.css +0 -0
  169. /package/{components/overlays → overlays}/vignette/index.tsx +0 -0
  170. /package/{components/overlays → overlays}/vignette/vignette.module.css +0 -0
  171. /package/{components/text → text}/distortion-text/distortion-text.module.css +0 -0
  172. /package/{components/text → text}/distortion-text/index.tsx +0 -0
  173. /package/{components/text → text}/falling-text/falling-text.module.css +0 -0
  174. /package/{components/text → text}/falling-text/index.tsx +0 -0
  175. /package/{components/text → text}/flicker-text/flicker-text.module.css +0 -0
  176. /package/{components/text → text}/flicker-text/index.tsx +0 -0
  177. /package/{components/text → text}/glitch-text/glitch-text.module.css +0 -0
  178. /package/{components/text → text}/glitch-text/index.tsx +0 -0
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, ReactNode, useRef, useState, useEffect } from 'react';
4
+ import styles from './horizontal-scroll.module.css';
5
+
6
+ export interface HorizontalScrollProps extends HTMLAttributes<HTMLDivElement> {
7
+ /** Panel contents */
8
+ children: ReactNode;
9
+ /** Color variant */
10
+ variant?: 'cyan' | 'green' | 'amber' | 'blood';
11
+ /** Panel size */
12
+ panelSize?: 'full' | 'large' | 'medium' | 'small';
13
+ /** Show fade on edges */
14
+ fadeEdges?: boolean;
15
+ /** Show navigation indicators */
16
+ showIndicators?: boolean;
17
+ /** Gap between panels in rem */
18
+ gap?: number;
19
+ }
20
+
21
+ export const HorizontalScroll = forwardRef<HTMLDivElement, HorizontalScrollProps>(
22
+ (
23
+ {
24
+ children,
25
+ variant = 'cyan',
26
+ panelSize = 'large',
27
+ fadeEdges = false,
28
+ showIndicators = false,
29
+ gap = 2,
30
+ className,
31
+ style,
32
+ ...props
33
+ },
34
+ ref
35
+ ) => {
36
+ const containerRef = useRef<HTMLDivElement>(null);
37
+ const [activeIndex, setActiveIndex] = useState(0);
38
+ const [panelCount, setPanelCount] = useState(0);
39
+
40
+ useEffect(() => {
41
+ const container = containerRef.current;
42
+ if (!container) return;
43
+
44
+ const panels = container.querySelectorAll(`.${styles.panel}`);
45
+ setPanelCount(panels.length);
46
+
47
+ const handleScroll = () => {
48
+ if (!container) return;
49
+ const scrollLeft = container.scrollLeft;
50
+ const panelWidth = container.scrollWidth / panels.length;
51
+ setActiveIndex(Math.round(scrollLeft / panelWidth));
52
+ };
53
+
54
+ container.addEventListener('scroll', handleScroll, { passive: true });
55
+ return () => container.removeEventListener('scroll', handleScroll);
56
+ }, [children]);
57
+
58
+ const scrollToPanel = (index: number) => {
59
+ const container = containerRef.current;
60
+ if (!container) return;
61
+ const panelWidth = container.scrollWidth / panelCount;
62
+ container.scrollTo({ left: panelWidth * index, behavior: 'smooth' });
63
+ };
64
+
65
+ const panelSizeClass = {
66
+ full: styles.panelFull,
67
+ large: styles.panelLarge,
68
+ medium: styles.panelMedium,
69
+ small: styles.panelSmall,
70
+ }[panelSize];
71
+
72
+ return (
73
+ <div className={className}>
74
+ <div
75
+ ref={(node) => {
76
+ (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
77
+ if (typeof ref === 'function') ref(node);
78
+ else if (ref) ref.current = node;
79
+ }}
80
+ className={`${styles.container} ${styles[variant]} ${fadeEdges ? styles.fadeEdges : ''}`}
81
+ style={{ '--panel-gap': `${gap}rem`, ...style } as React.CSSProperties}
82
+ {...props}
83
+ >
84
+ <div className={styles.track}>
85
+ {Array.isArray(children)
86
+ ? children.map((child, i) => (
87
+ <div key={i} className={`${styles.panel} ${panelSizeClass}`}>
88
+ {child}
89
+ </div>
90
+ ))
91
+ : <div className={`${styles.panel} ${panelSizeClass}`}>{children}</div>
92
+ }
93
+ </div>
94
+ </div>
95
+
96
+ {showIndicators && panelCount > 1 && (
97
+ <div className={styles.indicators}>
98
+ {Array.from({ length: panelCount }).map((_, i) => (
99
+ <button
100
+ key={i}
101
+ className={`${styles.indicator} ${i === activeIndex ? styles.indicatorActive : ''}`}
102
+ onClick={() => scrollToPanel(i)}
103
+ aria-label={`Go to panel ${i + 1}`}
104
+ />
105
+ ))}
106
+ </div>
107
+ )}
108
+ </div>
109
+ );
110
+ }
111
+ );
112
+
113
+ HorizontalScroll.displayName = 'HorizontalScroll';
114
+ export default HorizontalScroll;
@@ -0,0 +1,108 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, ReactNode, useRef, useState, useEffect } from 'react';
4
+
5
+ export interface HorizontalScrollProps extends HTMLAttributes<HTMLDivElement> {
6
+ children: ReactNode;
7
+ variant?: 'cyan' | 'green' | 'amber' | 'blood';
8
+ panelSize?: 'full' | 'large' | 'medium' | 'small';
9
+ fadeEdges?: boolean;
10
+ showIndicators?: boolean;
11
+ gap?: number;
12
+ }
13
+
14
+ const variantStyles = {
15
+ cyan: { scrollbar: 'scrollbar-thumb-cyan-400', indicator: 'border-cyan-400 hover:bg-cyan-400 hover:shadow-[0_0_10px_#00f0ff]' },
16
+ green: { scrollbar: 'scrollbar-thumb-emerald-400', indicator: 'border-emerald-400 hover:bg-emerald-400 hover:shadow-[0_0_10px_#00ff88]' },
17
+ amber: { scrollbar: 'scrollbar-thumb-amber-400', indicator: 'border-amber-400 hover:bg-amber-400 hover:shadow-[0_0_10px_#ffaa00]' },
18
+ blood: { scrollbar: 'scrollbar-thumb-red-800', indicator: 'border-red-800 hover:bg-red-800 hover:shadow-[0_0_10px_#8b1a1a]' },
19
+ };
20
+
21
+ const sizeClasses = {
22
+ full: 'w-screen',
23
+ large: 'w-[80vw]',
24
+ medium: 'w-[60vw]',
25
+ small: 'w-[40vw]',
26
+ };
27
+
28
+ export const HorizontalScroll = forwardRef<HTMLDivElement, HorizontalScrollProps>(
29
+ ({ children, variant = 'cyan', panelSize = 'large', fadeEdges = false, showIndicators = false, gap = 2, className = '', ...props }, ref) => {
30
+ const containerRef = useRef<HTMLDivElement>(null);
31
+ const [activeIndex, setActiveIndex] = useState(0);
32
+ const [panelCount, setPanelCount] = useState(0);
33
+
34
+ useEffect(() => {
35
+ const container = containerRef.current;
36
+ if (!container) return;
37
+ const panels = container.querySelectorAll('[data-panel]');
38
+ setPanelCount(panels.length);
39
+
40
+ const handleScroll = () => {
41
+ if (!container || panels.length === 0) return;
42
+ const panelWidth = container.scrollWidth / panels.length;
43
+ setActiveIndex(Math.round(container.scrollLeft / panelWidth));
44
+ };
45
+
46
+ container.addEventListener('scroll', handleScroll, { passive: true });
47
+ return () => container.removeEventListener('scroll', handleScroll);
48
+ }, [children]);
49
+
50
+ const scrollToPanel = (index: number) => {
51
+ const container = containerRef.current;
52
+ if (!container || panelCount === 0) return;
53
+ const panelWidth = container.scrollWidth / panelCount;
54
+ container.scrollTo({ left: panelWidth * index, behavior: 'smooth' });
55
+ };
56
+
57
+ const colors = variantStyles[variant];
58
+
59
+ return (
60
+ <div className={className}>
61
+ <div
62
+ ref={(node) => {
63
+ (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
64
+ if (typeof ref === 'function') ref(node);
65
+ else if (ref) ref.current = node;
66
+ }}
67
+ className={`
68
+ w-full overflow-x-auto overflow-y-hidden snap-x snap-mandatory scroll-smooth
69
+ scrollbar-thin scrollbar-track-black/30 ${colors.scrollbar}
70
+ ${fadeEdges ? '[mask-image:linear-gradient(90deg,transparent_0%,black_5%,black_95%,transparent_100%)]' : ''}
71
+ `}
72
+ {...props}
73
+ >
74
+ <div className="flex w-max" style={{ gap: `${gap}rem`, padding: '1rem 0' }}>
75
+ {Array.isArray(children)
76
+ ? children.map((child, i) => (
77
+ <div key={i} data-panel className={`flex-none snap-start ${sizeClasses[panelSize]}`}>
78
+ {child}
79
+ </div>
80
+ ))
81
+ : <div data-panel className={`flex-none snap-start ${sizeClasses[panelSize]}`}>{children}</div>
82
+ }
83
+ </div>
84
+ </div>
85
+
86
+ {showIndicators && panelCount > 1 && (
87
+ <div className="flex justify-center gap-2 mt-4">
88
+ {Array.from({ length: panelCount }).map((_, i) => (
89
+ <button
90
+ key={i}
91
+ className={`
92
+ w-2 h-2 rounded-full bg-black/30 border transition-all duration-300
93
+ ${colors.indicator}
94
+ ${i === activeIndex ? `${variant === 'cyan' ? 'bg-cyan-400 shadow-[0_0_10px_#00f0ff]' : variant === 'green' ? 'bg-emerald-400 shadow-[0_0_10px_#00ff88]' : variant === 'amber' ? 'bg-amber-400 shadow-[0_0_10px_#ffaa00]' : 'bg-red-800 shadow-[0_0_10px_#8b1a1a]'}` : ''}
95
+ `}
96
+ onClick={() => scrollToPanel(i)}
97
+ aria-label={`Go to panel ${i + 1}`}
98
+ />
99
+ ))}
100
+ </div>
101
+ )}
102
+ </div>
103
+ );
104
+ }
105
+ );
106
+
107
+ HorizontalScroll.displayName = 'HorizontalScroll';
108
+ export default HorizontalScroll;
@@ -0,0 +1,103 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './spec-grid.module.css';
5
+
6
+ export interface SpecItem {
7
+ label: string;
8
+ value: string | number;
9
+ unit?: string;
10
+ description?: string;
11
+ icon?: string;
12
+ highlighted?: boolean;
13
+ }
14
+
15
+ export interface SpecGridProps extends HTMLAttributes<HTMLDivElement> {
16
+ /** Spec items */
17
+ specs: SpecItem[];
18
+ /** Color variant */
19
+ variant?: 'cyan' | 'green' | 'amber' | 'blood';
20
+ /** Grid columns */
21
+ columns?: number;
22
+ /** Show terminal header */
23
+ showHeader?: boolean;
24
+ /** Header title */
25
+ headerTitle?: string;
26
+ /** Compact mode */
27
+ compact?: boolean;
28
+ /** Striped rows */
29
+ striped?: boolean;
30
+ }
31
+
32
+ export const SpecGrid = forwardRef<HTMLDivElement, SpecGridProps>(
33
+ (
34
+ {
35
+ specs,
36
+ variant = 'cyan',
37
+ columns,
38
+ showHeader = false,
39
+ headerTitle = 'SYSTEM SPECS',
40
+ compact = false,
41
+ striped = false,
42
+ className,
43
+ style,
44
+ ...props
45
+ },
46
+ ref
47
+ ) => {
48
+ return (
49
+ <div
50
+ ref={ref}
51
+ className={`
52
+ ${styles.grid}
53
+ ${styles[variant]}
54
+ ${compact ? styles.compact : ''}
55
+ ${striped ? styles.striped : ''}
56
+ ${className || ''}
57
+ `}
58
+ style={{
59
+ gridTemplateColumns: columns ? `repeat(${columns}, 1fr)` : undefined,
60
+ ...style,
61
+ }}
62
+ {...props}
63
+ >
64
+ {showHeader && (
65
+ <div className={styles.header}>
66
+ <span>{headerTitle}</span>
67
+ <div className={styles.headerDots}>
68
+ <span className={styles.dot} />
69
+ <span className={styles.dot} />
70
+ <span className={styles.dot} />
71
+ </div>
72
+ </div>
73
+ )}
74
+
75
+ {specs.map((spec, index) => (
76
+ <div
77
+ key={index}
78
+ className={`
79
+ ${styles.item}
80
+ ${spec.icon ? styles.hasIcon : ''}
81
+ ${spec.highlighted ? styles.highlighted : ''}
82
+ `}
83
+ >
84
+ {spec.icon && <span className={styles.icon}>{spec.icon}</span>}
85
+ <div className={styles.content}>
86
+ <span className={styles.label}>{spec.label}</span>
87
+ <span className={styles.value}>
88
+ {spec.value}
89
+ {spec.unit && <span className={styles.unit}>{spec.unit}</span>}
90
+ </span>
91
+ {spec.description && (
92
+ <span className={styles.description}>{spec.description}</span>
93
+ )}
94
+ </div>
95
+ </div>
96
+ ))}
97
+ </div>
98
+ );
99
+ }
100
+ );
101
+
102
+ SpecGrid.displayName = 'SpecGrid';
103
+ export default SpecGrid;
@@ -0,0 +1,125 @@
1
+ .grid {
2
+ --spec-accent: #00f0ff;
3
+ --spec-bg: rgba(0, 0, 0, 0.5);
4
+ --spec-border: rgba(255, 255, 255, 0.1);
5
+
6
+ display: grid;
7
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
8
+ gap: 1px;
9
+ background: var(--spec-border);
10
+ border: 1px solid var(--spec-border);
11
+ font-family: 'Share Tech Mono', monospace;
12
+ }
13
+
14
+ .item {
15
+ padding: 1.5rem;
16
+ background: var(--spec-bg);
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: 0.5rem;
20
+ transition: all 0.3s ease;
21
+ }
22
+
23
+ .item:hover {
24
+ background: rgba(0, 240, 255, 0.05);
25
+ }
26
+
27
+ .label {
28
+ font-size: 0.65rem;
29
+ text-transform: uppercase;
30
+ letter-spacing: 0.2em;
31
+ color: var(--spec-accent);
32
+ opacity: 0.8;
33
+ }
34
+
35
+ .value {
36
+ font-size: 1.5rem;
37
+ font-weight: 700;
38
+ color: #fff;
39
+ line-height: 1.2;
40
+ }
41
+
42
+ .unit {
43
+ font-size: 0.75rem;
44
+ color: rgba(255, 255, 255, 0.4);
45
+ margin-left: 0.25rem;
46
+ }
47
+
48
+ .description {
49
+ font-size: 0.75rem;
50
+ color: rgba(255, 255, 255, 0.5);
51
+ margin-top: 0.5rem;
52
+ }
53
+
54
+ /* With icons */
55
+ .hasIcon {
56
+ flex-direction: row;
57
+ align-items: flex-start;
58
+ gap: 1rem;
59
+ }
60
+
61
+ .icon {
62
+ font-size: 1.5rem;
63
+ color: var(--spec-accent);
64
+ opacity: 0.6;
65
+ }
66
+
67
+ .content {
68
+ flex: 1;
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: 0.25rem;
72
+ }
73
+
74
+ /* Highlight important specs */
75
+ .highlighted {
76
+ background: rgba(0, 240, 255, 0.1);
77
+ border-left: 2px solid var(--spec-accent);
78
+ }
79
+
80
+ /* Terminal style header */
81
+ .header {
82
+ grid-column: 1 / -1;
83
+ padding: 0.75rem 1.5rem;
84
+ background: var(--spec-accent);
85
+ color: #000;
86
+ font-size: 0.7rem;
87
+ font-weight: 700;
88
+ text-transform: uppercase;
89
+ letter-spacing: 0.2em;
90
+ display: flex;
91
+ justify-content: space-between;
92
+ align-items: center;
93
+ }
94
+
95
+ .headerDots {
96
+ display: flex;
97
+ gap: 0.5rem;
98
+ }
99
+
100
+ .dot {
101
+ width: 8px;
102
+ height: 8px;
103
+ border-radius: 50%;
104
+ background: rgba(0, 0, 0, 0.3);
105
+ }
106
+
107
+ /* Variants */
108
+ .cyan { --spec-accent: #00f0ff; }
109
+ .green { --spec-accent: #00ff88; }
110
+ .amber { --spec-accent: #ffaa00; }
111
+ .blood { --spec-accent: #8b1a1a; }
112
+
113
+ /* Compact mode */
114
+ .compact .item {
115
+ padding: 1rem;
116
+ }
117
+
118
+ .compact .value {
119
+ font-size: 1.25rem;
120
+ }
121
+
122
+ /* Striped */
123
+ .striped .item:nth-child(even) {
124
+ background: rgba(255, 255, 255, 0.02);
125
+ }
@@ -0,0 +1,91 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+
5
+ export interface SpecItem {
6
+ label: string;
7
+ value: string | number;
8
+ unit?: string;
9
+ description?: string;
10
+ icon?: string;
11
+ highlighted?: boolean;
12
+ }
13
+
14
+ export interface SpecGridProps extends HTMLAttributes<HTMLDivElement> {
15
+ specs: SpecItem[];
16
+ variant?: 'cyan' | 'green' | 'amber' | 'blood';
17
+ columns?: number;
18
+ showHeader?: boolean;
19
+ headerTitle?: string;
20
+ compact?: boolean;
21
+ striped?: boolean;
22
+ }
23
+
24
+ const variantStyles = {
25
+ cyan: { accent: 'text-cyan-400', headerBg: 'bg-cyan-400', hoverBg: 'hover:bg-cyan-400/5', borderHighlight: 'border-l-cyan-400' },
26
+ green: { accent: 'text-emerald-400', headerBg: 'bg-emerald-400', hoverBg: 'hover:bg-emerald-400/5', borderHighlight: 'border-l-emerald-400' },
27
+ amber: { accent: 'text-amber-400', headerBg: 'bg-amber-400', hoverBg: 'hover:bg-amber-400/5', borderHighlight: 'border-l-amber-400' },
28
+ blood: { accent: 'text-red-800', headerBg: 'bg-red-800', hoverBg: 'hover:bg-red-800/5', borderHighlight: 'border-l-red-800' },
29
+ };
30
+
31
+ export const SpecGrid = forwardRef<HTMLDivElement, SpecGridProps>(
32
+ ({ specs, variant = 'cyan', columns, showHeader = false, headerTitle = 'SYSTEM SPECS', compact = false, striped = false, className = '', style, ...props }, ref) => {
33
+ const colors = variantStyles[variant];
34
+ const gridCols = columns ? { gridTemplateColumns: `repeat(${columns}, 1fr)` } : {};
35
+
36
+ return (
37
+ <div
38
+ ref={ref}
39
+ className={`
40
+ grid auto-rows-auto gap-px border border-white/10 bg-white/10
41
+ font-['Share_Tech_Mono',monospace]
42
+ ${!columns ? 'grid-cols-[repeat(auto-fit,minmax(200px,1fr))]' : ''}
43
+ ${className}
44
+ `}
45
+ style={{ ...gridCols, ...style }}
46
+ {...props}
47
+ >
48
+ {showHeader && (
49
+ <div className={`col-span-full py-3 px-6 ${colors.headerBg} text-black text-[0.7rem] font-bold uppercase tracking-[0.2em] flex justify-between items-center`}>
50
+ <span>{headerTitle}</span>
51
+ <div className="flex gap-2">
52
+ <span className="w-2 h-2 rounded-full bg-black/30" />
53
+ <span className="w-2 h-2 rounded-full bg-black/30" />
54
+ <span className="w-2 h-2 rounded-full bg-black/30" />
55
+ </div>
56
+ </div>
57
+ )}
58
+
59
+ {specs.map((spec, index) => (
60
+ <div
61
+ key={index}
62
+ className={`
63
+ ${compact ? 'p-4' : 'p-6'} bg-black/50 flex gap-4 transition-all duration-300
64
+ ${colors.hoverBg}
65
+ ${spec.icon ? 'flex-row items-start' : 'flex-col gap-2'}
66
+ ${spec.highlighted ? `bg-cyan-400/10 border-l-2 ${colors.borderHighlight}` : ''}
67
+ ${striped && index % 2 === 1 ? 'bg-white/[0.02]' : ''}
68
+ `}
69
+ >
70
+ {spec.icon && <span className={`text-2xl ${colors.accent} opacity-60`}>{spec.icon}</span>}
71
+ <div className="flex flex-col gap-1 flex-1">
72
+ <span className={`text-[0.65rem] uppercase tracking-[0.2em] ${colors.accent} opacity-80`}>
73
+ {spec.label}
74
+ </span>
75
+ <span className={`${compact ? 'text-xl' : 'text-2xl'} font-bold text-white leading-tight`}>
76
+ {spec.value}
77
+ {spec.unit && <span className="text-xs text-white/40 ml-1">{spec.unit}</span>}
78
+ </span>
79
+ {spec.description && (
80
+ <span className="text-xs text-white/50 mt-2">{spec.description}</span>
81
+ )}
82
+ </div>
83
+ </div>
84
+ ))}
85
+ </div>
86
+ );
87
+ }
88
+ );
89
+
90
+ SpecGrid.displayName = 'SpecGrid';
91
+ export default SpecGrid;
@@ -0,0 +1,91 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, ReactNode } from 'react';
4
+ import styles from './tower-pricing.module.css';
5
+
6
+ export interface PricingTier {
7
+ name: string;
8
+ price: number | string;
9
+ currency?: string;
10
+ period?: string;
11
+ features: string[];
12
+ featured?: boolean;
13
+ featuredLabel?: string;
14
+ buttonText?: string;
15
+ onSelect?: () => void;
16
+ }
17
+
18
+ export interface TowerPricingProps extends HTMLAttributes<HTMLDivElement> {
19
+ /** Pricing tiers */
20
+ tiers: PricingTier[];
21
+ /** Color variant */
22
+ variant?: 'gold' | 'blood' | 'cyan' | 'bone';
23
+ /** Show connectors between tiers */
24
+ showConnectors?: boolean;
25
+ }
26
+
27
+ export const TowerPricing = forwardRef<HTMLDivElement, TowerPricingProps>(
28
+ (
29
+ {
30
+ tiers,
31
+ variant = 'gold',
32
+ showConnectors = false,
33
+ className,
34
+ ...props
35
+ },
36
+ ref
37
+ ) => {
38
+ return (
39
+ <div
40
+ ref={ref}
41
+ className={`${styles.tower} ${styles[variant]} ${className || ''}`}
42
+ {...props}
43
+ >
44
+ {tiers.map((tier, index) => (
45
+ <div
46
+ key={tier.name}
47
+ className={`${styles.tier} ${tier.featured ? styles.featured : ''}`}
48
+ >
49
+ {tier.featured && tier.featuredLabel && (
50
+ <span className={styles.featuredBadge}>{tier.featuredLabel}</span>
51
+ )}
52
+
53
+ <div className={styles.tierHeader}>
54
+ <span className={styles.tierName}>{tier.name}</span>
55
+ <div className={styles.tierPrice}>
56
+ <span className={styles.currency}>{tier.currency || '$'}</span>
57
+ <span className={styles.amount}>{tier.price}</span>
58
+ {tier.period && <span className={styles.period}>/{tier.period}</span>}
59
+ </div>
60
+ </div>
61
+
62
+ <ul className={styles.tierFeatures}>
63
+ {tier.features.map((feature, i) => (
64
+ <li key={i} className={styles.feature}>
65
+ <span className={styles.featureIcon}>✦</span>
66
+ {feature}
67
+ </li>
68
+ ))}
69
+ </ul>
70
+
71
+ <div className={styles.tierAction}>
72
+ <button
73
+ className={styles.actionButton}
74
+ onClick={tier.onSelect}
75
+ >
76
+ {tier.buttonText || 'Select'}
77
+ </button>
78
+ </div>
79
+
80
+ {showConnectors && index < tiers.length - 1 && (
81
+ <span className={styles.connector} />
82
+ )}
83
+ </div>
84
+ ))}
85
+ </div>
86
+ );
87
+ }
88
+ );
89
+
90
+ TowerPricing.displayName = 'TowerPricing';
91
+ export default TowerPricing;