@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
package/bin/cli.js CHANGED
@@ -24,10 +24,25 @@ const COMPONENTS = {
24
24
  'flicker-text': { category: 'text', description: 'Text that flickers randomly', status: 'ready' },
25
25
  'distortion-text': { category: 'text', description: 'Wave/shake/skew/blur text effects', status: 'ready' },
26
26
  'falling-text': { category: 'text', description: 'Letters falling in cascade', status: 'ready' },
27
+ 'typing-text': { category: 'text', description: 'Terminal typing effect', status: 'ready' },
28
+ 'char-glitch': { category: 'text', description: 'Per-character glitch effect', status: 'ready' },
29
+ 'reveal-text': { category: 'text', description: 'Text reveal on scroll', status: 'ready' },
30
+ 'strike-reveal': { category: 'text', description: 'Strikethrough then reveal', status: 'ready' },
31
+ 'giant-layers': { category: 'text', description: 'Giant text with 3D shadow layers', status: 'ready' },
32
+ 'blood-drip': { category: 'text', description: 'Dripping blood effect letters', status: 'ready' },
33
+ 'rotate-text': { category: 'text', description: 'Vertical rotating text', status: 'ready' },
34
+ 'ascii-art': { category: 'text', description: 'Styled ASCII art block', status: 'ready' },
35
+ 'countdown-display': { category: 'text', description: 'Stylized countdown timer', status: 'ready' },
36
+ 'terminal-output': { category: 'text', description: 'Terminal block with prompt', status: 'ready' },
27
37
 
28
38
  // Buttons
29
39
  'glitch-button': { category: 'buttons', description: 'Button with glitch hover effect', status: 'ready' },
30
40
  'chaos-button': { category: 'buttons', description: 'Chaotic animated button with debris', status: 'ready' },
41
+ 'dead-button': { category: 'buttons', description: 'Destroyed/glitched button with layers', status: 'ready' },
42
+ 'deeper-button': { category: 'buttons', description: 'Go deeper style descent button', status: 'ready' },
43
+ 'dual-choice': { category: 'buttons', description: 'Yes/No dual button choice', status: 'ready' },
44
+ 'cta-brutal': { category: 'buttons', description: 'Brutalist CTA button', status: 'ready' },
45
+ 'tension-bar': { category: 'buttons', description: 'Dramatic tension/progress bar', status: 'ready' },
31
46
 
32
47
  // Backgrounds
33
48
  'noise-canvas': { category: 'backgrounds', description: 'Animated noise canvas background', status: 'ready' },
@@ -39,6 +54,49 @@ const COMPONENTS = {
39
54
  'warning-tape': { category: 'effects', description: 'Scrolling warning tape banner', status: 'ready' },
40
55
  'cursor-follower': { category: 'effects', description: 'Custom cursor with trail', status: 'ready' },
41
56
  'screen-distortion': { category: 'effects', description: 'Full screen distortion effect', status: 'ready' },
57
+ 'glowing-border': { category: 'effects', description: 'Glowing border container with pulse', status: 'ready' },
58
+ 'glitch-image': { category: 'effects', description: 'Image with RGB glitch on hover', status: 'ready' },
59
+
60
+ // Neon components
61
+ 'neon-button': { category: 'neon', description: 'Button with neon glow effect', status: 'ready' },
62
+ 'neon-badge': { category: 'neon', description: 'Luminous status badges', status: 'ready' },
63
+ 'neon-progress': { category: 'neon', description: 'Glowing progress bar with shimmer', status: 'ready' },
64
+ 'neon-toggle': { category: 'neon', description: 'On/off switch with neon glow', status: 'ready' },
65
+ 'neon-alert': { category: 'neon', description: 'Alert notifications with neon style', status: 'ready' },
66
+ 'neon-tabs': { category: 'neon', description: 'Tab navigation with glow effect', status: 'ready' },
67
+ 'neon-divider': { category: 'neon', description: 'Luminous section dividers', status: 'ready' },
68
+
69
+ // Cyber components
70
+ 'cyber-input': { category: 'cyber', description: 'Input with animated border glow', status: 'ready' },
71
+ 'cyber-loader': { category: 'cyber', description: 'Futuristic spinners and loaders', status: 'ready' },
72
+ 'cyber-modal': { category: 'cyber', description: 'Modal with scanlines and glow', status: 'ready' },
73
+ 'cyber-avatar': { category: 'cyber', description: 'Avatar with neon border and status', status: 'ready' },
74
+ 'cyber-slider': { category: 'cyber', description: 'Slider with neon track and thumb', status: 'ready' },
75
+ 'cyber-tooltip': { category: 'cyber', description: 'Terminal-style tooltips', status: 'ready' },
76
+
77
+ // Layout components
78
+ 'hologram-card': { category: 'layout', description: 'Holographic card with scanlines', status: 'ready' },
79
+ 'data-grid': { category: 'layout', description: 'Terminal-style data table', status: 'ready' },
80
+ 'horizontal-scroll': { category: 'layout', description: 'Horizontal scrolling panels', status: 'ready' },
81
+ 'void-frame': { category: 'layout', description: 'Frame with decorative corners', status: 'ready' },
82
+ 'tower-pricing': { category: 'layout', description: 'Vertical stacked pricing cards', status: 'ready' },
83
+ 'spec-grid': { category: 'layout', description: 'Terminal-style specs grid', status: 'ready' },
84
+ 'tracklist': { category: 'layout', description: 'Music tracklist component', status: 'ready' },
85
+
86
+ // Navigation
87
+ 'hexagon-menu': { category: 'navigation', description: 'Honeycomb hexagon menu', status: 'ready' },
88
+ 'scattered-nav': { category: 'navigation', description: 'Scattered fragment navigation', status: 'ready' },
89
+ 'vertical-nav': { category: 'navigation', description: 'Vertical nav with glyphs', status: 'ready' },
90
+ 'brutal-nav': { category: 'navigation', description: 'Brutalist chaotic navigation', status: 'ready' },
91
+ 'progress-dots': { category: 'navigation', description: 'Section progress indicator', status: 'ready' },
92
+ 'scroll-indicator': { category: 'navigation', description: 'Vertical scroll indicator', status: 'ready' },
93
+
94
+ // Decorative
95
+ 'rune-symbols': { category: 'decorative', description: 'Animated runic symbols', status: 'ready' },
96
+ 'ornaments': { category: 'decorative', description: 'Medieval ornaments', status: 'ready' },
97
+ 'coffee-stain': { category: 'decorative', description: 'Coffee stain/aged paper effect', status: 'ready' },
98
+ 'sheet-music': { category: 'decorative', description: 'Floating music notes', status: 'ready' },
99
+ 'inscription': { category: 'decorative', description: 'Stone carved inscription', status: 'ready' },
42
100
  };
43
101
 
44
102
  const program = new Command();
@@ -75,8 +133,15 @@ program
75
133
  .command('add [component]')
76
134
  .description('Add a component to your project')
77
135
  .option('-d, --dir <path>', 'Target directory', './components/chaos')
136
+ .option('-v, --variant <type>', 'Styling variant: css or tailwind', 'css')
78
137
  .option('-y, --yes', 'Skip confirmation')
79
138
  .action(async (componentName, options) => {
139
+ const variant = options.variant.toLowerCase();
140
+ if (!['css', 'tailwind'].includes(variant)) {
141
+ console.log(pc.red(`\n✗ Invalid variant "${variant}". Use 'css' or 'tailwind'.\n`));
142
+ return;
143
+ }
144
+
80
145
  // If no component specified, show interactive picker
81
146
  if (!componentName) {
82
147
  const choices = Object.entries(COMPONENTS).map(([name, info]) => ({
@@ -85,20 +150,33 @@ program
85
150
  value: name,
86
151
  }));
87
152
 
88
- const response = await prompts({
89
- type: 'autocomplete',
90
- name: 'component',
91
- message: 'Which component?',
92
- choices,
93
- suggest: (input, choices) =>
94
- choices.filter(c => c.title.includes(input) || c.description.toLowerCase().includes(input.toLowerCase()))
95
- });
153
+ const response = await prompts([
154
+ {
155
+ type: 'autocomplete',
156
+ name: 'component',
157
+ message: 'Which component?',
158
+ choices,
159
+ suggest: (input, choices) =>
160
+ choices.filter(c => c.title.includes(input) || c.description.toLowerCase().includes(input.toLowerCase()))
161
+ },
162
+ {
163
+ type: 'select',
164
+ name: 'variant',
165
+ message: 'Styling variant?',
166
+ choices: [
167
+ { title: 'CSS Modules', value: 'css' },
168
+ { title: 'Tailwind', value: 'tailwind' },
169
+ ],
170
+ initial: variant === 'tailwind' ? 1 : 0,
171
+ }
172
+ ]);
96
173
 
97
174
  if (!response.component) {
98
175
  console.log(pc.dim('Cancelled.'));
99
176
  return;
100
177
  }
101
178
  componentName = response.component;
179
+ options.variant = response.variant || variant;
102
180
  }
103
181
 
104
182
  // Validate component exists
@@ -109,22 +187,36 @@ program
109
187
  }
110
188
 
111
189
  const info = COMPONENTS[componentName];
112
- const sourceDir = path.join(COMPONENTS_DIR, info.category, componentName);
190
+ const baseDir = path.join(COMPONENTS_DIR, info.category, componentName);
191
+
192
+ // Check if component has variant subdirs or is legacy (flat structure)
193
+ const variantDir = path.join(baseDir, options.variant);
194
+ const hasVariants = fs.existsSync(path.join(baseDir, 'css')) || fs.existsSync(path.join(baseDir, 'tailwind'));
195
+ const sourceDir = hasVariants ? variantDir : baseDir;
113
196
  const targetDir = path.resolve(options.dir, info.category);
114
197
 
115
198
  // Check source exists
116
199
  if (!fs.existsSync(sourceDir)) {
117
- console.log(pc.yellow(`\n⚠ Component "${componentName}" is not yet implemented.`));
118
- console.log(pc.dim(' Coming soon!\n'));
200
+ if (hasVariants) {
201
+ console.log(pc.yellow(`\n⚠ Variant "${options.variant}" not available for "${componentName}".`));
202
+ const available = [];
203
+ if (fs.existsSync(path.join(baseDir, 'css'))) available.push('css');
204
+ if (fs.existsSync(path.join(baseDir, 'tailwind'))) available.push('tailwind');
205
+ console.log(pc.dim(` Available variants: ${available.join(', ')}\n`));
206
+ } else {
207
+ console.log(pc.yellow(`\n⚠ Component "${componentName}" is not yet implemented.`));
208
+ console.log(pc.dim(' Coming soon!\n'));
209
+ }
119
210
  return;
120
211
  }
121
212
 
122
213
  // Confirm
123
214
  if (!options.yes) {
215
+ const variantLabel = hasVariants ? ` (${pc.magenta(options.variant)})` : '';
124
216
  const confirm = await prompts({
125
217
  type: 'confirm',
126
218
  name: 'value',
127
- message: `Add ${pc.cyan(componentName)} to ${pc.dim(targetDir)}?`,
219
+ message: `Add ${pc.cyan(componentName)}${variantLabel} to ${pc.dim(targetDir)}?`,
128
220
  initial: true,
129
221
  });
130
222
 
@@ -139,7 +231,7 @@ program
139
231
  await fs.ensureDir(targetDir);
140
232
  await fs.copy(sourceDir, path.join(targetDir, componentName));
141
233
 
142
- console.log(pc.green(`\n✓ Added ${componentName}`));
234
+ console.log(pc.green(`\n✓ Added ${componentName}`) + (hasVariants ? pc.magenta(` (${options.variant})`) : ''));
143
235
  console.log(pc.dim(` → ${path.join(targetDir, componentName)}\n`));
144
236
 
145
237
  // Show usage hint
@@ -21,7 +21,7 @@ export interface GlowOrbsProps extends HTMLAttributes<HTMLDivElement> {
21
21
  export const GlowOrbs = forwardRef<HTMLDivElement, GlowOrbsProps>(
22
22
  (
23
23
  {
24
- colors = ['#7c3aed', '#06b6d4', '#ec4899', '#10b981'],
24
+ colors = ['hsl(var(--primary))', 'hsl(var(--secondary))', 'hsl(var(--accent, 300 100% 50%))', 'hsl(var(--muted))'],
25
25
  count = 5,
26
26
  sizeRange = [100, 300],
27
27
  blur = 80,
@@ -1,5 +1,6 @@
1
1
  .button {
2
- --accent: #ff0040;
2
+ --accent: hsl(var(--primary, 347 100% 50%));
3
+ --accent-alt: hsl(var(--secondary, 180 100% 50%));
3
4
  position: relative;
4
5
  padding: 1rem 2.5rem;
5
6
  font-family: inherit;
@@ -132,7 +133,7 @@
132
133
  }
133
134
 
134
135
  .ghost1 { color: var(--accent); }
135
- .ghost2 { color: #00ffff; }
136
+ .ghost2 { color: var(--accent-alt); }
136
137
 
137
138
  .button:hover .ghost1 {
138
139
  opacity: 0.6;
@@ -0,0 +1,81 @@
1
+ /* cta-brutal - CTA style brutaliste */
2
+ .button {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ gap: 1rem;
7
+ padding: 1.5rem 3rem;
8
+ background: var(--chaos-blood, #ff0040);
9
+ color: var(--chaos-void, #0a0a0a);
10
+ font-family: var(--chaos-font-display, 'Bebas Neue', sans-serif);
11
+ font-size: 1.5rem;
12
+ letter-spacing: 0.1em;
13
+ text-transform: uppercase;
14
+ text-decoration: none;
15
+ border: none;
16
+ cursor: pointer;
17
+ position: relative;
18
+ transition: all 0.15s;
19
+ clip-path: polygon(0 0, 100% 0, 95% 100%, 0 100%);
20
+ }
21
+
22
+ .button:hover {
23
+ transform: translate(-2px, -2px);
24
+ box-shadow: 4px 4px 0 var(--chaos-bone, #e8e8e8);
25
+ }
26
+
27
+ .button:active {
28
+ transform: translate(0, 0);
29
+ box-shadow: none;
30
+ }
31
+
32
+ .icon { font-size: 1.2em; transition: transform 0.3s; }
33
+ .button:hover .icon { transform: translateX(5px); }
34
+
35
+ .variantOutline {
36
+ background: transparent;
37
+ border: 3px solid var(--chaos-blood, #ff0040);
38
+ color: var(--chaos-blood, #ff0040);
39
+ clip-path: none;
40
+ }
41
+
42
+ .variantOutline:hover {
43
+ background: var(--chaos-blood, #ff0040);
44
+ color: var(--chaos-void, #0a0a0a);
45
+ }
46
+
47
+ .variantGold { background: var(--chaos-gold, #c9a227); }
48
+ .variantGold:hover { box-shadow: 4px 4px 0 var(--chaos-gold-dark, #8b7017); }
49
+
50
+ .variantInverse { background: var(--chaos-bone, #e8e8e8); color: var(--chaos-void, #0a0a0a); }
51
+ .variantInverse:hover { box-shadow: 4px 4px 0 var(--chaos-blood, #ff0040); }
52
+
53
+ .sizeSm { padding: 0.8rem 1.5rem; font-size: 1rem; }
54
+ .sizeLg { padding: 2rem 4rem; font-size: 2rem; }
55
+ .sizeXl { padding: 2.5rem 5rem; font-size: 2.5rem; }
56
+
57
+ .fullWidth { width: 100%; clip-path: none; }
58
+ .disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
59
+
60
+ .loading { position: relative; color: transparent; }
61
+ .loading::after {
62
+ content: '';
63
+ position: absolute;
64
+ width: 1.5em;
65
+ height: 1.5em;
66
+ border: 2px solid currentColor;
67
+ border-top-color: transparent;
68
+ border-radius: 50%;
69
+ animation: spin 0.8s linear infinite;
70
+ }
71
+
72
+ @keyframes spin { to { transform: rotate(360deg); } }
73
+
74
+ .shake:hover { animation: shake 0.1s infinite; }
75
+
76
+ @keyframes shake {
77
+ 0%, 100% { transform: translate(-2px, -2px); }
78
+ 25% { transform: translate(-1px, -3px); }
79
+ 50% { transform: translate(-3px, -1px); }
80
+ 75% { transform: translate(-2px, -2px); }
81
+ }
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, ButtonHTMLAttributes, AnchorHTMLAttributes, ReactNode } from 'react';
4
+ import styles from './cta-brutal.module.css';
5
+
6
+ type BaseProps = {
7
+ children: ReactNode;
8
+ variant?: 'default' | 'outline' | 'gold' | 'inverse';
9
+ size?: 'sm' | 'md' | 'lg' | 'xl';
10
+ icon?: ReactNode;
11
+ iconPosition?: 'left' | 'right';
12
+ fullWidth?: boolean;
13
+ shake?: boolean;
14
+ loading?: boolean;
15
+ disabled?: boolean;
16
+ };
17
+
18
+ type ButtonProps = BaseProps & ButtonHTMLAttributes<HTMLButtonElement> & { href?: never };
19
+ type AnchorProps = BaseProps & AnchorHTMLAttributes<HTMLAnchorElement> & { href: string };
20
+
21
+ export type CtaBrutalProps = ButtonProps | AnchorProps;
22
+
23
+ export const CtaBrutal = forwardRef<HTMLButtonElement | HTMLAnchorElement, CtaBrutalProps>(
24
+ ({ children, variant = 'default', size = 'md', icon, iconPosition = 'right', fullWidth = false, shake = false, loading = false, disabled = false, className, ...props }, ref) => {
25
+ const classes = [
26
+ styles.button,
27
+ variant === 'outline' && styles.variantOutline,
28
+ variant === 'gold' && styles.variantGold,
29
+ variant === 'inverse' && styles.variantInverse,
30
+ size === 'sm' && styles.sizeSm,
31
+ size === 'lg' && styles.sizeLg,
32
+ size === 'xl' && styles.sizeXl,
33
+ fullWidth && styles.fullWidth,
34
+ shake && styles.shake,
35
+ loading && styles.loading,
36
+ disabled && styles.disabled,
37
+ className,
38
+ ].filter(Boolean).join(' ');
39
+
40
+ const content = (
41
+ <>
42
+ {icon && iconPosition === 'left' && <span className={styles.icon}>{icon}</span>}
43
+ {children}
44
+ {icon && iconPosition === 'right' && <span className={styles.icon}>{icon}</span>}
45
+ </>
46
+ );
47
+
48
+ if ('href' in props && props.href) {
49
+ return <a ref={ref as any} className={classes} {...(props as AnchorProps)}>{content}</a>;
50
+ }
51
+ return <button ref={ref as any} className={classes} disabled={disabled || loading} {...(props as ButtonProps)}>{content}</button>;
52
+ }
53
+ );
54
+
55
+ CtaBrutal.displayName = 'CtaBrutal';
56
+ export default CtaBrutal;
@@ -0,0 +1,111 @@
1
+ /* dead-button - Bouton détruit/glitché avec layers */
2
+ .container {
3
+ position: relative;
4
+ width: 280px;
5
+ height: 60px;
6
+ animation: btnShake 0.1s infinite;
7
+ cursor: not-allowed;
8
+ }
9
+
10
+ .container:hover { animation: btnShakeHard 0.05s infinite; }
11
+
12
+ @keyframes btnShake {
13
+ 0%, 100% { transform: translate(0); }
14
+ 25% { transform: translate(0.5px, 0.5px); }
15
+ 50% { transform: translate(-0.5px, -0.5px); }
16
+ 75% { transform: translate(0.5px, -0.5px); }
17
+ }
18
+
19
+ @keyframes btnShakeHard {
20
+ 0%, 100% { transform: translate(0) rotate(0); }
21
+ 25% { transform: translate(2px, 1px) rotate(0.5deg); }
22
+ 50% { transform: translate(-1px, -2px) rotate(-0.5deg); }
23
+ 75% { transform: translate(1px, -1px) rotate(0.3deg); }
24
+ }
25
+
26
+ .clean {
27
+ position: absolute;
28
+ inset: 0;
29
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
30
+ border-radius: 16px;
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: 0.8rem;
35
+ font-weight: 600;
36
+ font-size: 1rem;
37
+ color: white;
38
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
39
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
40
+ letter-spacing: 0.02em;
41
+ }
42
+
43
+ .gradient2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); box-shadow: 0 4px 15px rgba(245, 87, 108, 0.4); }
44
+ .gradient3 { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); box-shadow: 0 4px 15px rgba(79, 172, 254, 0.4); }
45
+ .gradient4 { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); box-shadow: 0 4px 15px rgba(250, 112, 154, 0.4); }
46
+
47
+ .icon { font-size: 1.1rem; }
48
+
49
+ .destroy {
50
+ position: absolute;
51
+ inset: 0;
52
+ background: repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(255, 0, 64, 0.1) 2px, rgba(255, 0, 64, 0.1) 4px),
53
+ repeating-linear-gradient(0deg, transparent, transparent 3px, rgba(0, 0, 0, 0.3) 3px, rgba(0, 0, 0, 0.3) 4px);
54
+ border-radius: 16px;
55
+ mix-blend-mode: multiply;
56
+ }
57
+
58
+ .noise {
59
+ position: absolute;
60
+ inset: 0;
61
+ border-radius: 16px;
62
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
63
+ opacity: 0.15;
64
+ mix-blend-mode: overlay;
65
+ animation: noiseMove 0.2s steps(5) infinite;
66
+ }
67
+
68
+ @keyframes noiseMove {
69
+ 0% { transform: translate(0, 0); }
70
+ 20% { transform: translate(-2px, 1px); }
71
+ 40% { transform: translate(1px, -1px); }
72
+ 60% { transform: translate(2px, 2px); }
73
+ 80% { transform: translate(-1px, -2px); }
74
+ 100% { transform: translate(0, 0); }
75
+ }
76
+
77
+ .glitch1, .glitch2 { position: absolute; inset: 0; border-radius: 16px; pointer-events: none; }
78
+ .glitch1 { background: var(--chaos-blood, #ff0040); mix-blend-mode: color-dodge; opacity: 0; animation: glitchSlice1 3s infinite; }
79
+ .glitch2 { background: cyan; mix-blend-mode: color-dodge; opacity: 0; animation: glitchSlice2 3s infinite; }
80
+
81
+ @keyframes glitchSlice1 {
82
+ 0%, 89%, 91%, 93%, 95%, 100% { opacity: 0; clip-path: inset(0 0 100% 0); }
83
+ 90% { opacity: 0.8; clip-path: inset(20% 0 60% 0); transform: translateX(-5px); }
84
+ 92% { opacity: 0.6; clip-path: inset(50% 0 30% 0); transform: translateX(3px); }
85
+ 94% { opacity: 0.7; clip-path: inset(70% 0 10% 0); transform: translateX(-2px); }
86
+ }
87
+
88
+ @keyframes glitchSlice2 {
89
+ 0%, 89%, 91%, 93%, 95%, 100% { opacity: 0; clip-path: inset(0 0 100% 0); }
90
+ 90% { opacity: 0.5; clip-path: inset(30% 0 50% 0); transform: translateX(4px); }
91
+ 92% { opacity: 0.4; clip-path: inset(10% 0 70% 0); transform: translateX(-3px); }
92
+ 94% { opacity: 0.6; clip-path: inset(60% 0 20% 0); transform: translateX(5px); }
93
+ }
94
+
95
+ .container::after {
96
+ content: '';
97
+ position: absolute;
98
+ top: 50%;
99
+ left: -10px;
100
+ right: -10px;
101
+ height: 3px;
102
+ background: var(--chaos-blood, #ff0040);
103
+ transform: rotate(-3deg);
104
+ box-shadow: 0 0 10px var(--chaos-blood, #ff0040);
105
+ }
106
+
107
+ .noStrike::after { display: none; }
108
+ .sizeSm { width: 200px; height: 45px; }
109
+ .sizeSm .clean { font-size: 0.85rem; border-radius: 12px; }
110
+ .sizeLg { width: 340px; height: 70px; }
111
+ .sizeLg .clean { font-size: 1.2rem; border-radius: 20px; }
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, ReactNode } from 'react';
4
+ import styles from './dead-button.module.css';
5
+
6
+ export interface DeadButtonProps extends HTMLAttributes<HTMLDivElement> {
7
+ children: ReactNode;
8
+ icon?: ReactNode;
9
+ gradient?: 1 | 2 | 3 | 4;
10
+ size?: 'sm' | 'md' | 'lg';
11
+ showStrike?: boolean;
12
+ }
13
+
14
+ export const DeadButton = forwardRef<HTMLDivElement, DeadButtonProps>(
15
+ ({ children, icon, gradient = 1, size = 'md', showStrike = true, className, ...props }, ref) => {
16
+ const containerClasses = [
17
+ styles.container,
18
+ size === 'sm' && styles.sizeSm,
19
+ size === 'lg' && styles.sizeLg,
20
+ !showStrike && styles.noStrike,
21
+ className,
22
+ ].filter(Boolean).join(' ');
23
+
24
+ const cleanClasses = [
25
+ styles.clean,
26
+ gradient === 2 && styles.gradient2,
27
+ gradient === 3 && styles.gradient3,
28
+ gradient === 4 && styles.gradient4,
29
+ ].filter(Boolean).join(' ');
30
+
31
+ return (
32
+ <div ref={ref} className={containerClasses} {...props}>
33
+ <div className={cleanClasses}>
34
+ {icon && <span className={styles.icon}>{icon}</span>}
35
+ {children}
36
+ </div>
37
+ <div className={styles.destroy} />
38
+ <div className={styles.noise} />
39
+ <div className={styles.glitch1} />
40
+ <div className={styles.glitch2} />
41
+ </div>
42
+ );
43
+ }
44
+ );
45
+
46
+ DeadButton.displayName = 'DeadButton';
47
+ export default DeadButton;
@@ -0,0 +1,76 @@
1
+ /* deeper-button - Bouton "aller plus loin" style descent */
2
+ .button {
3
+ padding: 2rem 4rem;
4
+ background: transparent;
5
+ border: 2px solid var(--chaos-blood, #ff0040);
6
+ color: var(--chaos-blood, #ff0040);
7
+ font-family: var(--chaos-font-display, 'Bebas Neue', sans-serif);
8
+ font-size: 1.5rem;
9
+ text-decoration: none;
10
+ position: relative;
11
+ overflow: hidden;
12
+ transition: all 0.3s;
13
+ letter-spacing: 0.1em;
14
+ cursor: pointer;
15
+ display: inline-block;
16
+ text-transform: uppercase;
17
+ }
18
+
19
+ .button::before {
20
+ content: '';
21
+ position: absolute;
22
+ top: 0;
23
+ left: -100%;
24
+ width: 100%;
25
+ height: 100%;
26
+ background: var(--chaos-blood, #ff0040);
27
+ transition: left 0.3s;
28
+ z-index: -1;
29
+ }
30
+
31
+ .button:hover { color: var(--chaos-void, #0a0a0a); }
32
+ .button:hover::before { left: 0; }
33
+
34
+ .arrow {
35
+ display: inline-block;
36
+ margin-left: 1rem;
37
+ transition: transform 0.3s;
38
+ }
39
+
40
+ .button:hover .arrow { animation: arrowBounce 0.5s ease-in-out infinite; }
41
+
42
+ @keyframes arrowBounce {
43
+ 0%, 100% { transform: translateY(0); }
44
+ 50% { transform: translateY(5px); }
45
+ }
46
+
47
+ .variantGold { border-color: var(--chaos-gold, #c9a227); color: var(--chaos-gold, #c9a227); }
48
+ .variantGold::before { background: var(--chaos-gold, #c9a227); }
49
+ .variantGold:hover { color: var(--chaos-void, #0a0a0a); }
50
+
51
+ .variantOutline { border-width: 1px; }
52
+ .variantOutline:hover { border-width: 2px; }
53
+
54
+ .variantGhost { border-color: transparent; border-bottom-color: var(--chaos-blood, #ff0040); }
55
+ .variantGhost::before { display: none; }
56
+ .variantGhost:hover { color: var(--chaos-blood, #ff0040); border-color: var(--chaos-blood, #ff0040); }
57
+
58
+ .sizeSm { padding: 1rem 2rem; font-size: 1rem; }
59
+ .sizeLg { padding: 2.5rem 5rem; font-size: 2rem; }
60
+
61
+ .pulsing { animation: pulse 2s ease-in-out infinite; }
62
+
63
+ @keyframes pulse {
64
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 0, 64, 0.4); }
65
+ 50% { box-shadow: 0 0 0 15px rgba(255, 0, 64, 0); }
66
+ }
67
+
68
+ .variantGold.pulsing { animation: pulseGold 2s ease-in-out infinite; }
69
+
70
+ @keyframes pulseGold {
71
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(201, 162, 39, 0.4); }
72
+ 50% { box-shadow: 0 0 0 15px rgba(201, 162, 39, 0); }
73
+ }
74
+
75
+ .iconLeft { margin-right: 1rem; }
76
+ .iconRight { margin-left: 1rem; }
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, ButtonHTMLAttributes, AnchorHTMLAttributes, ReactNode } from 'react';
4
+ import styles from './deeper-button.module.css';
5
+
6
+ type BaseProps = {
7
+ children: ReactNode;
8
+ variant?: 'default' | 'gold' | 'outline' | 'ghost';
9
+ size?: 'sm' | 'md' | 'lg';
10
+ showArrow?: boolean;
11
+ pulsing?: boolean;
12
+ iconLeft?: ReactNode;
13
+ iconRight?: ReactNode;
14
+ };
15
+
16
+ type ButtonProps = BaseProps & ButtonHTMLAttributes<HTMLButtonElement> & { href?: never };
17
+ type AnchorProps = BaseProps & AnchorHTMLAttributes<HTMLAnchorElement> & { href: string };
18
+
19
+ export type DeeperButtonProps = ButtonProps | AnchorProps;
20
+
21
+ export const DeeperButton = forwardRef<HTMLButtonElement | HTMLAnchorElement, DeeperButtonProps>(
22
+ ({ children, variant = 'default', size = 'md', showArrow = true, pulsing = false, iconLeft, iconRight, className, ...props }, ref) => {
23
+ const classes = [
24
+ styles.button,
25
+ variant === 'gold' && styles.variantGold,
26
+ variant === 'outline' && styles.variantOutline,
27
+ variant === 'ghost' && styles.variantGhost,
28
+ size === 'sm' && styles.sizeSm,
29
+ size === 'lg' && styles.sizeLg,
30
+ pulsing && styles.pulsing,
31
+ className,
32
+ ].filter(Boolean).join(' ');
33
+
34
+ const content = (
35
+ <>
36
+ {iconLeft && <span className={styles.iconLeft}>{iconLeft}</span>}
37
+ {children}
38
+ {iconRight && <span className={styles.iconRight}>{iconRight}</span>}
39
+ {showArrow && <span className={styles.arrow}>↓</span>}
40
+ </>
41
+ );
42
+
43
+ if ('href' in props && props.href) {
44
+ return <a ref={ref as any} className={classes} {...(props as AnchorProps)}>{content}</a>;
45
+ }
46
+ return <button ref={ref as any} className={classes} {...(props as ButtonProps)}>{content}</button>;
47
+ }
48
+ );
49
+
50
+ DeeperButton.displayName = 'DeeperButton';
51
+ export default DeeperButton;