@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,136 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './ornaments.module.css';
5
+
6
+ export const ORNAMENT_SYMBOLS = {
7
+ cross: '✝',
8
+ maltese: '✠',
9
+ orthodox: '☦',
10
+ fleurDeLis: '⚜',
11
+ star: '✦',
12
+ diamond: '◆',
13
+ heart: '❧',
14
+ leaf: '❦',
15
+ dagger: '†',
16
+ doubleDagger: '‡',
17
+ asterisk: '✽',
18
+ florette: '✿',
19
+ skull: '☠',
20
+ crown: '♔',
21
+ swords: '⚔',
22
+ };
23
+
24
+ export interface OrnamentsProps extends HTMLAttributes<HTMLDivElement> {
25
+ /** Ornament type */
26
+ type?: 'divider' | 'corner' | 'frame' | 'fleuron' | 'symbols';
27
+ /** Symbol to use */
28
+ symbol?: keyof typeof ORNAMENT_SYMBOLS | string;
29
+ /** Multiple symbols */
30
+ symbols?: (keyof typeof ORNAMENT_SYMBOLS | string)[];
31
+ /** Color variant */
32
+ variant?: 'gold' | 'bone' | 'blood' | 'iron';
33
+ /** Size */
34
+ size?: 'sm' | 'md' | 'lg';
35
+ /** Animate */
36
+ animated?: boolean;
37
+ /** Corner position (for type='corner') */
38
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'all';
39
+ }
40
+
41
+ const getSymbol = (s: keyof typeof ORNAMENT_SYMBOLS | string): string =>
42
+ ORNAMENT_SYMBOLS[s as keyof typeof ORNAMENT_SYMBOLS] || s;
43
+
44
+ export const Ornaments = forwardRef<HTMLDivElement, OrnamentsProps>(
45
+ (
46
+ {
47
+ type = 'divider',
48
+ symbol = 'cross',
49
+ symbols,
50
+ variant = 'gold',
51
+ size = 'md',
52
+ animated = false,
53
+ position = 'all',
54
+ className,
55
+ ...props
56
+ },
57
+ ref
58
+ ) => {
59
+ const baseClasses = `${styles.ornament} ${styles[variant]} ${styles[size]} ${animated ? styles.animated : ''}`;
60
+ const sym = getSymbol(symbol);
61
+
62
+ if (type === 'divider') {
63
+ return (
64
+ <div ref={ref} className={`${baseClasses} ${styles.divider} ${className || ''}`} {...props}>
65
+ <span className={styles.line} />
66
+ <span className={styles.symbol}>{sym}</span>
67
+ <span className={styles.line} />
68
+ </div>
69
+ );
70
+ }
71
+
72
+ if (type === 'fleuron') {
73
+ return (
74
+ <div ref={ref} className={`${baseClasses} ${className || ''}`} {...props}>
75
+ <span className={styles.fleuron}>{sym}</span>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ if (type === 'symbols') {
81
+ const syms = symbols || ['star', 'diamond', 'star'];
82
+ return (
83
+ <div ref={ref} className={`${baseClasses} ${styles.symbols} ${className || ''}`} {...props}>
84
+ {syms.map((s, i) => (
85
+ <span key={i} className={styles.symbol}>{getSymbol(s)}</span>
86
+ ))}
87
+ </div>
88
+ );
89
+ }
90
+
91
+ if (type === 'corner') {
92
+ const corners = position === 'all'
93
+ ? ['TopLeft', 'TopRight', 'BottomLeft', 'BottomRight']
94
+ : [position.split('-').map((p, i) => i === 0 ? p.charAt(0).toUpperCase() + p.slice(1) : p.charAt(0).toUpperCase() + p.slice(1)).join('')];
95
+
96
+ return (
97
+ <>
98
+ {corners.map((pos) => (
99
+ <div
100
+ key={pos}
101
+ ref={pos === corners[0] ? ref : undefined}
102
+ className={`${styles.corner} ${styles[`corner${pos}`]} ${styles[variant]} ${className || ''}`}
103
+ {...props}
104
+ >
105
+ <span className={styles.cornerInner}>
106
+ <span className={styles.cornerSymbol}>{sym}</span>
107
+ </span>
108
+ </div>
109
+ ))}
110
+ </>
111
+ );
112
+ }
113
+
114
+ if (type === 'frame') {
115
+ return (
116
+ <div ref={ref} className={`${styles.frame} ${styles[variant]} ${className || ''}`} {...props}>
117
+ <div className={styles.frameTop}>
118
+ <span className={styles.frameLine} />
119
+ <span className={styles.frameSymbol}>{sym}</span>
120
+ <span className={styles.frameLine} />
121
+ </div>
122
+ <div className={styles.frameBottom}>
123
+ <span className={styles.frameLine} />
124
+ <span className={styles.frameSymbol}>{sym}</span>
125
+ <span className={styles.frameLine} />
126
+ </div>
127
+ </div>
128
+ );
129
+ }
130
+
131
+ return null;
132
+ }
133
+ );
134
+
135
+ Ornaments.displayName = 'Ornaments';
136
+ export default Ornaments;
@@ -0,0 +1,144 @@
1
+ .ornament {
2
+ --ornament-color: #c9a227;
3
+
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ color: var(--ornament-color);
8
+ }
9
+
10
+ /* Divider style */
11
+ .divider {
12
+ gap: 1rem;
13
+ width: 100%;
14
+ }
15
+
16
+ .line {
17
+ flex: 1;
18
+ height: 1px;
19
+ background: linear-gradient(90deg, transparent, var(--ornament-color), transparent);
20
+ }
21
+
22
+ .symbol {
23
+ font-size: 1.25rem;
24
+ opacity: 0.8;
25
+ }
26
+
27
+ /* Corner ornament */
28
+ .corner {
29
+ position: absolute;
30
+ width: 40px;
31
+ height: 40px;
32
+ }
33
+
34
+ .cornerTopLeft { top: 0; left: 0; }
35
+ .cornerTopRight { top: 0; right: 0; transform: scaleX(-1); }
36
+ .cornerBottomLeft { bottom: 0; left: 0; transform: scaleY(-1); }
37
+ .cornerBottomRight { bottom: 0; right: 0; transform: scale(-1); }
38
+
39
+ .cornerInner {
40
+ position: absolute;
41
+ top: 0;
42
+ left: 0;
43
+ }
44
+
45
+ .cornerInner::before {
46
+ content: '';
47
+ position: absolute;
48
+ top: 8px;
49
+ left: 0;
50
+ width: 30px;
51
+ height: 1px;
52
+ background: var(--ornament-color);
53
+ }
54
+
55
+ .cornerInner::after {
56
+ content: '';
57
+ position: absolute;
58
+ top: 0;
59
+ left: 8px;
60
+ width: 1px;
61
+ height: 30px;
62
+ background: var(--ornament-color);
63
+ }
64
+
65
+ .cornerSymbol {
66
+ position: absolute;
67
+ top: 4px;
68
+ left: 4px;
69
+ font-size: 0.75rem;
70
+ }
71
+
72
+ /* Fleuron */
73
+ .fleuron {
74
+ font-family: 'Times New Roman', serif;
75
+ font-size: 2rem;
76
+ opacity: 0.7;
77
+ }
78
+
79
+ /* Cross variations */
80
+ .cross {
81
+ font-size: 1.5rem;
82
+ }
83
+
84
+ /* Frame ornament */
85
+ .frame {
86
+ position: absolute;
87
+ inset: 0;
88
+ pointer-events: none;
89
+ }
90
+
91
+ .frameTop,
92
+ .frameBottom {
93
+ position: absolute;
94
+ left: 50%;
95
+ transform: translateX(-50%);
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 0.5rem;
99
+ }
100
+
101
+ .frameTop { top: -0.5em; }
102
+ .frameBottom { bottom: -0.5em; }
103
+
104
+ .frameLine {
105
+ width: 60px;
106
+ height: 1px;
107
+ background: var(--ornament-color);
108
+ }
109
+
110
+ .frameSymbol {
111
+ font-size: 0.875rem;
112
+ }
113
+
114
+ /* Variants */
115
+ .gold { --ornament-color: #c9a227; }
116
+ .bone { --ornament-color: #d4c5a9; }
117
+ .blood { --ornament-color: #8b1a1a; }
118
+ .iron { --ornament-color: #4a4a4a; }
119
+
120
+ /* Common ornament symbols */
121
+ .symbols {
122
+ display: flex;
123
+ gap: 0.5rem;
124
+ opacity: 0.6;
125
+ }
126
+
127
+ /* Animated */
128
+ .animated .symbol,
129
+ .animated .fleuron {
130
+ animation: ornamentPulse 3s ease-in-out infinite;
131
+ }
132
+
133
+ @keyframes ornamentPulse {
134
+ 0%, 100% { opacity: 0.5; }
135
+ 50% { opacity: 0.9; }
136
+ }
137
+
138
+ /* Size variants */
139
+ .sm .symbol { font-size: 0.875rem; }
140
+ .sm .fleuron { font-size: 1.25rem; }
141
+ .md .symbol { font-size: 1.25rem; }
142
+ .md .fleuron { font-size: 2rem; }
143
+ .lg .symbol { font-size: 1.75rem; }
144
+ .lg .fleuron { font-size: 3rem; }
@@ -0,0 +1,122 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+
5
+ export const ORNAMENT_SYMBOLS = {
6
+ cross: '✝', maltese: '✠', orthodox: '☦', fleurDeLis: '⚜', star: '✦',
7
+ diamond: '◆', heart: '❧', leaf: '❦', dagger: '†', doubleDagger: '‡',
8
+ asterisk: '✽', florette: '✿', skull: '☠', crown: '♔', swords: '⚔',
9
+ };
10
+
11
+ export interface OrnamentsProps extends HTMLAttributes<HTMLDivElement> {
12
+ type?: 'divider' | 'corner' | 'frame' | 'fleuron' | 'symbols';
13
+ symbol?: keyof typeof ORNAMENT_SYMBOLS | string;
14
+ symbols?: (keyof typeof ORNAMENT_SYMBOLS | string)[];
15
+ variant?: 'gold' | 'bone' | 'blood' | 'iron';
16
+ size?: 'sm' | 'md' | 'lg';
17
+ animated?: boolean;
18
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'all';
19
+ }
20
+
21
+ const variantColors = {
22
+ gold: 'text-amber-600',
23
+ bone: 'text-stone-400',
24
+ blood: 'text-red-800',
25
+ iron: 'text-zinc-500',
26
+ };
27
+
28
+ const sizeClasses = {
29
+ sm: { symbol: 'text-sm', fleuron: 'text-xl' },
30
+ md: { symbol: 'text-xl', fleuron: 'text-3xl' },
31
+ lg: { symbol: 'text-2xl', fleuron: 'text-5xl' },
32
+ };
33
+
34
+ const getSymbol = (s: keyof typeof ORNAMENT_SYMBOLS | string): string =>
35
+ ORNAMENT_SYMBOLS[s as keyof typeof ORNAMENT_SYMBOLS] || s;
36
+
37
+ export const Ornaments = forwardRef<HTMLDivElement, OrnamentsProps>(
38
+ ({ type = 'divider', symbol = 'cross', symbols, variant = 'gold', size = 'md', animated = false, position = 'all', className = '', ...props }, ref) => {
39
+ const color = variantColors[variant];
40
+ const sizes = sizeClasses[size];
41
+ const sym = getSymbol(symbol);
42
+ const animClass = animated ? 'animate-pulse' : '';
43
+
44
+ if (type === 'divider') {
45
+ return (
46
+ <div ref={ref} className={`flex items-center justify-center gap-4 w-full ${color} ${className}`} {...props}>
47
+ <span className="flex-1 h-px bg-gradient-to-r from-transparent via-current to-transparent" />
48
+ <span className={`${sizes.symbol} opacity-80 ${animClass}`}>{sym}</span>
49
+ <span className="flex-1 h-px bg-gradient-to-l from-transparent via-current to-transparent" />
50
+ </div>
51
+ );
52
+ }
53
+
54
+ if (type === 'fleuron') {
55
+ return (
56
+ <div ref={ref} className={`flex items-center justify-center ${color} ${className}`} {...props}>
57
+ <span className={`${sizes.fleuron} opacity-70 ${animClass}`}>{sym}</span>
58
+ </div>
59
+ );
60
+ }
61
+
62
+ if (type === 'symbols') {
63
+ const syms = symbols || ['star', 'diamond', 'star'];
64
+ return (
65
+ <div ref={ref} className={`flex gap-2 opacity-60 ${color} ${className}`} {...props}>
66
+ {syms.map((s, i) => (
67
+ <span key={i} className={`${sizes.symbol} ${animClass}`}>{getSymbol(s)}</span>
68
+ ))}
69
+ </div>
70
+ );
71
+ }
72
+
73
+ if (type === 'corner') {
74
+ const cornerPositions = {
75
+ 'top-left': 'top-0 left-0',
76
+ 'top-right': 'top-0 right-0 -scale-x-100',
77
+ 'bottom-left': 'bottom-0 left-0 -scale-y-100',
78
+ 'bottom-right': 'bottom-0 right-0 -scale-100',
79
+ };
80
+ const corners = position === 'all' ? Object.keys(cornerPositions) : [position];
81
+
82
+ return (
83
+ <>
84
+ {corners.map((pos) => (
85
+ <div
86
+ key={pos}
87
+ ref={pos === corners[0] ? ref : undefined}
88
+ className={`absolute w-10 h-10 ${cornerPositions[pos as keyof typeof cornerPositions]} ${color} ${className}`}
89
+ {...props}
90
+ >
91
+ <span className="absolute top-2 left-0 w-[30px] h-px bg-current" />
92
+ <span className="absolute top-0 left-2 w-px h-[30px] bg-current" />
93
+ <span className="absolute top-1 left-1 text-xs">{sym}</span>
94
+ </div>
95
+ ))}
96
+ </>
97
+ );
98
+ }
99
+
100
+ if (type === 'frame') {
101
+ return (
102
+ <div ref={ref} className={`absolute inset-0 pointer-events-none ${color} ${className}`} {...props}>
103
+ <div className="absolute -top-[0.5em] left-1/2 -translate-x-1/2 flex items-center gap-2">
104
+ <span className="w-15 h-px bg-current" />
105
+ <span className="text-sm">{sym}</span>
106
+ <span className="w-15 h-px bg-current" />
107
+ </div>
108
+ <div className="absolute -bottom-[0.5em] left-1/2 -translate-x-1/2 flex items-center gap-2">
109
+ <span className="w-15 h-px bg-current" />
110
+ <span className="text-sm">{sym}</span>
111
+ <span className="w-15 h-px bg-current" />
112
+ </div>
113
+ </div>
114
+ );
115
+ }
116
+
117
+ return null;
118
+ }
119
+ );
120
+
121
+ Ornaments.displayName = 'Ornaments';
122
+ export default Ornaments;
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './rune-symbols.module.css';
5
+
6
+ // Elder Futhark runes
7
+ export const RUNES = {
8
+ fehu: 'ᚠ', // wealth
9
+ uruz: 'ᚢ', // strength
10
+ thurisaz: 'ᚦ', // giant
11
+ ansuz: 'ᚨ', // god
12
+ raidho: 'ᚱ', // journey
13
+ kenaz: 'ᚲ', // torch
14
+ gebo: 'ᚷ', // gift
15
+ wunjo: 'ᚹ', // joy
16
+ hagalaz: 'ᚺ', // hail
17
+ nauthiz: 'ᚾ', // need
18
+ isaz: 'ᛁ', // ice
19
+ jera: 'ᛃ', // year
20
+ eihwaz: 'ᛇ', // yew
21
+ perthro: 'ᛈ', // mystery
22
+ algiz: 'ᛉ', // protection
23
+ sowilo: 'ᛊ', // sun
24
+ tiwaz: 'ᛏ', // victory
25
+ berkano: 'ᛒ', // birch
26
+ ehwaz: 'ᛖ', // horse
27
+ mannaz: 'ᛗ', // man
28
+ laguz: 'ᛚ', // water
29
+ ingwaz: 'ᛝ', // fertility
30
+ dagaz: 'ᛞ', // day
31
+ othala: 'ᛟ', // heritage
32
+ };
33
+
34
+ export interface RuneSymbolsProps extends HTMLAttributes<HTMLDivElement> {
35
+ /** Runes to display (array of rune names or custom symbols) */
36
+ runes?: (keyof typeof RUNES | string)[];
37
+ /** Number of random runes to display */
38
+ count?: number;
39
+ /** Color variant */
40
+ variant?: 'gold' | 'blood' | 'bone' | 'iron' | 'cyan';
41
+ /** Animation style */
42
+ animation?: 'glow' | 'floating' | 'pulsing' | 'flickering' | 'none';
43
+ /** Layout direction */
44
+ direction?: 'horizontal' | 'vertical';
45
+ /** Size variant */
46
+ size?: 'sm' | 'md' | 'lg' | 'xl';
47
+ /** Scattered positioning (requires parent with dimensions) */
48
+ scattered?: boolean;
49
+ }
50
+
51
+ const getRandomRunes = (count: number): string[] => {
52
+ const runeValues = Object.values(RUNES);
53
+ return Array.from({ length: count }, () =>
54
+ runeValues[Math.floor(Math.random() * runeValues.length)]
55
+ );
56
+ };
57
+
58
+ export const RuneSymbols = forwardRef<HTMLDivElement, RuneSymbolsProps>(
59
+ (
60
+ {
61
+ runes,
62
+ count = 6,
63
+ variant = 'gold',
64
+ animation = 'glow',
65
+ direction = 'horizontal',
66
+ size = 'md',
67
+ scattered = false,
68
+ className,
69
+ style,
70
+ ...props
71
+ },
72
+ ref
73
+ ) => {
74
+ const resolvedRunes = runes
75
+ ? runes.map(r => RUNES[r as keyof typeof RUNES] || r)
76
+ : getRandomRunes(count);
77
+
78
+ const animationClass = animation !== 'none' ? styles[animation] : '';
79
+
80
+ return (
81
+ <div
82
+ ref={ref}
83
+ className={`
84
+ ${styles.container}
85
+ ${styles[variant]}
86
+ ${styles[size]}
87
+ ${direction === 'vertical' ? styles.vertical : ''}
88
+ ${scattered ? styles.scattered : ''}
89
+ ${animationClass}
90
+ ${className || ''}
91
+ `}
92
+ style={style}
93
+ {...props}
94
+ >
95
+ {resolvedRunes.map((rune, i) => (
96
+ <span
97
+ key={i}
98
+ className={styles.rune}
99
+ style={{
100
+ '--delay': `${i * 0.5}s`,
101
+ ...(scattered ? {
102
+ left: `${Math.random() * 80 + 10}%`,
103
+ top: `${Math.random() * 80 + 10}%`,
104
+ } : {}),
105
+ } as React.CSSProperties}
106
+ >
107
+ {rune}
108
+ </span>
109
+ ))}
110
+ </div>
111
+ );
112
+ }
113
+ );
114
+
115
+ RuneSymbols.displayName = 'RuneSymbols';
116
+ export default RuneSymbols;
@@ -0,0 +1,116 @@
1
+ .container {
2
+ --rune-color: #c9a227;
3
+ --rune-glow: rgba(201, 162, 39, 0.5);
4
+
5
+ display: flex;
6
+ gap: 1.5rem;
7
+ font-size: 2rem;
8
+ }
9
+
10
+ .rune {
11
+ color: var(--rune-color);
12
+ opacity: 0.6;
13
+ transition: all 0.4s ease;
14
+ animation: runeGlow 4s ease-in-out infinite;
15
+ animation-delay: var(--delay, 0s);
16
+ }
17
+
18
+ .rune:hover {
19
+ opacity: 1;
20
+ text-shadow: 0 0 20px var(--rune-glow);
21
+ transform: scale(1.1);
22
+ }
23
+
24
+ @keyframes runeGlow {
25
+ 0%, 100% {
26
+ opacity: 0.4;
27
+ text-shadow: none;
28
+ }
29
+ 50% {
30
+ opacity: 0.8;
31
+ text-shadow: 0 0 15px var(--rune-glow);
32
+ }
33
+ }
34
+
35
+ /* Floating animation */
36
+ .floating .rune {
37
+ animation: runeFloat 6s ease-in-out infinite;
38
+ }
39
+
40
+ @keyframes runeFloat {
41
+ 0%, 100% {
42
+ transform: translateY(0) rotate(0deg);
43
+ opacity: 0.4;
44
+ }
45
+ 25% {
46
+ transform: translateY(-10px) rotate(5deg);
47
+ opacity: 0.7;
48
+ }
49
+ 50% {
50
+ transform: translateY(0) rotate(0deg);
51
+ opacity: 0.5;
52
+ }
53
+ 75% {
54
+ transform: translateY(10px) rotate(-5deg);
55
+ opacity: 0.7;
56
+ }
57
+ }
58
+
59
+ /* Pulsing animation */
60
+ .pulsing .rune {
61
+ animation: runePulse 2s ease-in-out infinite;
62
+ }
63
+
64
+ @keyframes runePulse {
65
+ 0%, 100% {
66
+ transform: scale(1);
67
+ opacity: 0.5;
68
+ }
69
+ 50% {
70
+ transform: scale(1.15);
71
+ opacity: 1;
72
+ text-shadow: 0 0 25px var(--rune-glow);
73
+ }
74
+ }
75
+
76
+ /* Flickering animation */
77
+ .flickering .rune {
78
+ animation: runeFlicker 0.5s steps(2) infinite;
79
+ }
80
+
81
+ @keyframes runeFlicker {
82
+ 0% { opacity: 0.3; }
83
+ 25% { opacity: 0.8; }
84
+ 50% { opacity: 0.5; }
85
+ 75% { opacity: 0.9; }
86
+ 100% { opacity: 0.3; }
87
+ }
88
+
89
+ /* Scattered positioning */
90
+ .scattered {
91
+ position: relative;
92
+ width: 100%;
93
+ height: 100%;
94
+ }
95
+
96
+ .scattered .rune {
97
+ position: absolute;
98
+ }
99
+
100
+ /* Variants */
101
+ .gold { --rune-color: #c9a227; --rune-glow: rgba(201, 162, 39, 0.5); }
102
+ .blood { --rune-color: #8b1a1a; --rune-glow: rgba(139, 26, 26, 0.5); }
103
+ .bone { --rune-color: #d4c5a9; --rune-glow: rgba(212, 197, 169, 0.5); }
104
+ .iron { --rune-color: #4a4a4a; --rune-glow: rgba(74, 74, 74, 0.5); }
105
+ .cyan { --rune-color: #00f0ff; --rune-glow: rgba(0, 240, 255, 0.5); }
106
+
107
+ /* Vertical layout */
108
+ .vertical {
109
+ flex-direction: column;
110
+ }
111
+
112
+ /* Size variants */
113
+ .sm { font-size: 1rem; gap: 0.75rem; }
114
+ .md { font-size: 2rem; gap: 1.5rem; }
115
+ .lg { font-size: 3rem; gap: 2rem; }
116
+ .xl { font-size: 4rem; gap: 3rem; }