@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,141 @@
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 {
32
+ color: var(--chaos-void, #0a0a0a);
33
+ }
34
+
35
+ .button:hover::before {
36
+ left: 0;
37
+ }
38
+
39
+ /* Arrow animation */
40
+ .arrow {
41
+ display: inline-block;
42
+ margin-left: 1rem;
43
+ transition: transform 0.3s;
44
+ }
45
+
46
+ .button:hover .arrow {
47
+ animation: arrowBounce 0.5s ease-in-out infinite;
48
+ }
49
+
50
+ @keyframes arrowBounce {
51
+ 0%, 100% { transform: translateY(0); }
52
+ 50% { transform: translateY(5px); }
53
+ }
54
+
55
+ /* Variants */
56
+ .variantGold {
57
+ border-color: var(--chaos-gold, #c9a227);
58
+ color: var(--chaos-gold, #c9a227);
59
+ }
60
+
61
+ .variantGold::before {
62
+ background: var(--chaos-gold, #c9a227);
63
+ }
64
+
65
+ .variantGold:hover {
66
+ color: var(--chaos-void, #0a0a0a);
67
+ }
68
+
69
+ .variantOutline {
70
+ border-width: 1px;
71
+ }
72
+
73
+ .variantOutline:hover {
74
+ border-width: 2px;
75
+ }
76
+
77
+ .variantGhost {
78
+ border-color: transparent;
79
+ border-bottom-color: var(--chaos-blood, #ff0040);
80
+ }
81
+
82
+ .variantGhost::before {
83
+ display: none;
84
+ }
85
+
86
+ .variantGhost:hover {
87
+ color: var(--chaos-blood, #ff0040);
88
+ border-color: var(--chaos-blood, #ff0040);
89
+ }
90
+
91
+ /* Size variants */
92
+ .sizeSm {
93
+ padding: 1rem 2rem;
94
+ font-size: 1rem;
95
+ }
96
+
97
+ .sizeLg {
98
+ padding: 2.5rem 5rem;
99
+ font-size: 2rem;
100
+ }
101
+
102
+ /* Pulsing effect */
103
+ .pulsing {
104
+ animation: pulse 2s ease-in-out infinite;
105
+ }
106
+
107
+ @keyframes pulse {
108
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 0, 64, 0.4); }
109
+ 50% { box-shadow: 0 0 0 15px rgba(255, 0, 64, 0); }
110
+ }
111
+
112
+ .variantGold.pulsing {
113
+ animation: pulseGold 2s ease-in-out infinite;
114
+ }
115
+
116
+ @keyframes pulseGold {
117
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(201, 162, 39, 0.4); }
118
+ 50% { box-shadow: 0 0 0 15px rgba(201, 162, 39, 0); }
119
+ }
120
+
121
+ /* Icon support */
122
+ .iconLeft {
123
+ margin-right: 1rem;
124
+ }
125
+
126
+ .iconRight {
127
+ margin-left: 1rem;
128
+ }
129
+
130
+ /* Glitch on hover */
131
+ .glitchHover:hover {
132
+ animation: glitchText 0.3s infinite;
133
+ }
134
+
135
+ @keyframes glitchText {
136
+ 0%, 100% { transform: translate(0); }
137
+ 20% { transform: translate(-2px, 2px); }
138
+ 40% { transform: translate(2px, -2px); }
139
+ 60% { transform: translate(-1px, -1px); }
140
+ 80% { transform: translate(1px, 1px); }
141
+ }
@@ -0,0 +1,62 @@
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
+ glitchOnHover?: boolean;
13
+ iconLeft?: ReactNode;
14
+ iconRight?: ReactNode;
15
+ };
16
+
17
+ type ButtonProps = BaseProps & ButtonHTMLAttributes<HTMLButtonElement> & { href?: never };
18
+ type AnchorProps = BaseProps & AnchorHTMLAttributes<HTMLAnchorElement> & { href: string };
19
+
20
+ export type DeeperButtonProps = ButtonProps | AnchorProps;
21
+
22
+ export const DeeperButton = forwardRef<HTMLButtonElement | HTMLAnchorElement, DeeperButtonProps>(
23
+ ({ children, variant = 'default', size = 'md', showArrow = true, pulsing = false, glitchOnHover = false, iconLeft, iconRight, className, ...props }, ref) => {
24
+ const classes = [
25
+ styles.button,
26
+ variant === 'gold' && styles.variantGold,
27
+ variant === 'outline' && styles.variantOutline,
28
+ variant === 'ghost' && styles.variantGhost,
29
+ size === 'sm' && styles.sizeSm,
30
+ size === 'lg' && styles.sizeLg,
31
+ pulsing && styles.pulsing,
32
+ glitchOnHover && styles.glitchHover,
33
+ className,
34
+ ].filter(Boolean).join(' ');
35
+
36
+ const content = (
37
+ <>
38
+ {iconLeft && <span className={styles.iconLeft}>{iconLeft}</span>}
39
+ {children}
40
+ {iconRight && <span className={styles.iconRight}>{iconRight}</span>}
41
+ {showArrow && <span className={styles.arrow}>↓</span>}
42
+ </>
43
+ );
44
+
45
+ if ('href' in props && props.href) {
46
+ return (
47
+ <a ref={ref as any} className={classes} {...(props as AnchorProps)}>
48
+ {content}
49
+ </a>
50
+ );
51
+ }
52
+
53
+ return (
54
+ <button ref={ref as any} className={classes} {...(props as ButtonProps)}>
55
+ {content}
56
+ </button>
57
+ );
58
+ }
59
+ );
60
+
61
+ DeeperButton.displayName = 'DeeperButton';
62
+ export default DeeperButton;
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, ButtonHTMLAttributes, AnchorHTMLAttributes, ReactNode } from 'react';
4
+
5
+ type BaseProps = {
6
+ children: ReactNode;
7
+ variant?: 'default' | 'gold' | 'outline' | 'ghost';
8
+ size?: 'sm' | 'md' | 'lg';
9
+ showArrow?: boolean;
10
+ pulsing?: boolean;
11
+ glitchOnHover?: 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
+ const variantClasses: Record<string, string> = {
22
+ default: 'border-red-500 text-red-500 hover:text-black before:bg-red-500',
23
+ gold: 'border-amber-500 text-amber-500 hover:text-black before:bg-amber-500',
24
+ outline: 'border border-red-500 text-red-500 hover:border-2 hover:text-black before:bg-red-500',
25
+ ghost: 'border-transparent border-b-red-500 text-red-500 hover:border-red-500 before:hidden',
26
+ };
27
+
28
+ const sizeClasses: Record<string, string> = {
29
+ sm: 'py-4 px-8 text-base',
30
+ md: 'py-8 px-16 text-2xl',
31
+ lg: 'py-10 px-20 text-3xl',
32
+ };
33
+
34
+ export const DeeperButton = forwardRef<HTMLButtonElement | HTMLAnchorElement, DeeperButtonProps>(
35
+ ({ children, variant = 'default', size = 'md', showArrow = true, pulsing = false, glitchOnHover = false, iconLeft, iconRight, className, ...props }, ref) => {
36
+ const classes = `
37
+ relative overflow-hidden border-2 bg-transparent
38
+ font-display tracking-[0.1em] uppercase cursor-pointer
39
+ transition-all inline-block no-underline
40
+ before:content-[''] before:absolute before:top-0 before:-left-full
41
+ before:w-full before:h-full before:transition-[left] before:duration-300 before:-z-10
42
+ hover:before:left-0
43
+ ${variantClasses[variant]}
44
+ ${sizeClasses[size]}
45
+ ${pulsing ? 'animate-[pulse_2s_ease-in-out_infinite]' : ''}
46
+ ${glitchOnHover ? 'hover:animate-[glitchText_0.3s_infinite]' : ''}
47
+ ${className || ''}
48
+ `;
49
+
50
+ const content = (
51
+ <>
52
+ {iconLeft && <span className="mr-4">{iconLeft}</span>}
53
+ {children}
54
+ {iconRight && <span className="ml-4">{iconRight}</span>}
55
+ {showArrow && (
56
+ <span className="inline-block ml-4 transition-transform group-hover:animate-bounce">↓</span>
57
+ )}
58
+ </>
59
+ );
60
+
61
+ if ('href' in props && props.href) {
62
+ return (
63
+ <a ref={ref as any} className={`group ${classes}`} {...(props as AnchorProps)}>
64
+ {content}
65
+ </a>
66
+ );
67
+ }
68
+
69
+ return (
70
+ <button ref={ref as any} className={`group ${classes}`} {...(props as ButtonProps)}>
71
+ {content}
72
+ </button>
73
+ );
74
+ }
75
+ );
76
+
77
+ DeeperButton.displayName = 'DeeperButton';
78
+ export default DeeperButton;
@@ -0,0 +1,176 @@
1
+ /* dual-choice - Deux boutons OUI/NON côte à côte */
2
+ .container {
3
+ display: flex;
4
+ gap: 0;
5
+ font-family: var(--chaos-font-display, 'Bebas Neue', sans-serif);
6
+ }
7
+
8
+ .button {
9
+ flex: 1;
10
+ padding: 1.5rem 3rem;
11
+ font-size: 1.5rem;
12
+ letter-spacing: 0.15em;
13
+ text-transform: uppercase;
14
+ border: 2px solid var(--chaos-iron, #333);
15
+ background: transparent;
16
+ cursor: pointer;
17
+ transition: all 0.2s;
18
+ position: relative;
19
+ overflow: hidden;
20
+ }
21
+
22
+ .button::before {
23
+ content: '';
24
+ position: absolute;
25
+ top: 0;
26
+ left: -100%;
27
+ width: 100%;
28
+ height: 100%;
29
+ transition: left 0.3s;
30
+ z-index: -1;
31
+ }
32
+
33
+ .button:hover::before {
34
+ left: 0;
35
+ }
36
+
37
+ /* Yes button */
38
+ .yes {
39
+ color: var(--chaos-bone, #e8e8e8);
40
+ border-color: var(--chaos-bone-dark, #666);
41
+ }
42
+
43
+ .yes::before {
44
+ background: var(--chaos-bone, #e8e8e8);
45
+ }
46
+
47
+ .yes:hover {
48
+ color: var(--chaos-void, #0a0a0a);
49
+ border-color: var(--chaos-bone, #e8e8e8);
50
+ }
51
+
52
+ /* No button */
53
+ .no {
54
+ color: var(--chaos-blood, #ff0040);
55
+ border-color: var(--chaos-blood, #ff0040);
56
+ border-left: none;
57
+ }
58
+
59
+ .no::before {
60
+ background: var(--chaos-blood, #ff0040);
61
+ }
62
+
63
+ .no:hover {
64
+ color: var(--chaos-void, #0a0a0a);
65
+ }
66
+
67
+ /* Divider */
68
+ .divider {
69
+ width: 2px;
70
+ background: var(--chaos-iron, #333);
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ position: relative;
75
+ }
76
+
77
+ .divider::before {
78
+ content: 'OU';
79
+ position: absolute;
80
+ font-size: 0.6rem;
81
+ color: var(--chaos-bone-dark, #666);
82
+ background: var(--chaos-void, #0a0a0a);
83
+ padding: 0.3rem;
84
+ letter-spacing: 0.1em;
85
+ }
86
+
87
+ /* Variant: dramatic */
88
+ .variantDramatic .yes {
89
+ color: var(--chaos-gold, #c9a227);
90
+ border-color: var(--chaos-gold, #c9a227);
91
+ }
92
+
93
+ .variantDramatic .yes::before {
94
+ background: var(--chaos-gold, #c9a227);
95
+ }
96
+
97
+ .variantDramatic .no {
98
+ color: var(--chaos-blood, #ff0040);
99
+ }
100
+
101
+ /* Variant: minimal */
102
+ .variantMinimal .button {
103
+ border: none;
104
+ padding: 1rem 2rem;
105
+ font-size: 1rem;
106
+ }
107
+
108
+ .variantMinimal .button::after {
109
+ content: '';
110
+ position: absolute;
111
+ bottom: 0;
112
+ left: 0;
113
+ right: 0;
114
+ height: 2px;
115
+ background: currentColor;
116
+ transform: scaleX(0);
117
+ transition: transform 0.3s;
118
+ }
119
+
120
+ .variantMinimal .button:hover::after {
121
+ transform: scaleX(1);
122
+ }
123
+
124
+ .variantMinimal .button::before {
125
+ display: none;
126
+ }
127
+
128
+ .variantMinimal .button:hover {
129
+ color: inherit;
130
+ }
131
+
132
+ .variantMinimal .divider {
133
+ display: none;
134
+ }
135
+
136
+ /* Variant: stacked */
137
+ .variantStacked {
138
+ flex-direction: column;
139
+ }
140
+
141
+ .variantStacked .no {
142
+ border-left: 2px solid var(--chaos-blood, #ff0040);
143
+ border-top: none;
144
+ }
145
+
146
+ .variantStacked .divider {
147
+ width: 100%;
148
+ height: 2px;
149
+ }
150
+
151
+ .variantStacked .divider::before {
152
+ padding: 0 0.5rem;
153
+ }
154
+
155
+ /* Size variants */
156
+ .sizeSm .button {
157
+ padding: 0.8rem 1.5rem;
158
+ font-size: 1rem;
159
+ }
160
+
161
+ .sizeLg .button {
162
+ padding: 2rem 4rem;
163
+ font-size: 2rem;
164
+ }
165
+
166
+ /* Disabled state */
167
+ .disabled {
168
+ opacity: 0.5;
169
+ pointer-events: none;
170
+ }
171
+
172
+ /* Active/selected state */
173
+ .selected {
174
+ background: currentColor;
175
+ color: var(--chaos-void, #0a0a0a) !important;
176
+ }
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './dual-choice.module.css';
5
+
6
+ export interface DualChoiceProps extends HTMLAttributes<HTMLDivElement> {
7
+ yesLabel?: string;
8
+ noLabel?: string;
9
+ onYes?: () => void;
10
+ onNo?: () => void;
11
+ variant?: 'default' | 'dramatic' | 'minimal' | 'stacked';
12
+ size?: 'sm' | 'md' | 'lg';
13
+ showDivider?: boolean;
14
+ disabled?: boolean;
15
+ selectedValue?: 'yes' | 'no' | null;
16
+ }
17
+
18
+ export const DualChoice = forwardRef<HTMLDivElement, DualChoiceProps>(
19
+ ({ yesLabel = 'OUI', noLabel = 'NON', onYes, onNo, variant = 'default', size = 'md', showDivider = true, disabled = false, selectedValue = null, className, ...props }, ref) => {
20
+ const containerClasses = [
21
+ styles.container,
22
+ variant === 'dramatic' && styles.variantDramatic,
23
+ variant === 'minimal' && styles.variantMinimal,
24
+ variant === 'stacked' && styles.variantStacked,
25
+ size === 'sm' && styles.sizeSm,
26
+ size === 'lg' && styles.sizeLg,
27
+ disabled && styles.disabled,
28
+ className,
29
+ ].filter(Boolean).join(' ');
30
+
31
+ return (
32
+ <div ref={ref} className={containerClasses} {...props}>
33
+ <button
34
+ className={`${styles.button} ${styles.yes} ${selectedValue === 'yes' ? styles.selected : ''}`}
35
+ onClick={onYes}
36
+ disabled={disabled}
37
+ >
38
+ {yesLabel}
39
+ </button>
40
+
41
+ {showDivider && variant !== 'minimal' && <div className={styles.divider} />}
42
+
43
+ <button
44
+ className={`${styles.button} ${styles.no} ${selectedValue === 'no' ? styles.selected : ''}`}
45
+ onClick={onNo}
46
+ disabled={disabled}
47
+ >
48
+ {noLabel}
49
+ </button>
50
+ </div>
51
+ );
52
+ }
53
+ );
54
+
55
+ DualChoice.displayName = 'DualChoice';
56
+ export default DualChoice;
@@ -0,0 +1,89 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+
5
+ export interface DualChoiceProps extends HTMLAttributes<HTMLDivElement> {
6
+ yesLabel?: string;
7
+ noLabel?: string;
8
+ onYes?: () => void;
9
+ onNo?: () => void;
10
+ variant?: 'default' | 'dramatic' | 'minimal' | 'stacked';
11
+ size?: 'sm' | 'md' | 'lg';
12
+ showDivider?: boolean;
13
+ disabled?: boolean;
14
+ selectedValue?: 'yes' | 'no' | null;
15
+ }
16
+
17
+ const sizeClasses: Record<string, string> = {
18
+ sm: 'py-3 px-6 text-base',
19
+ md: 'py-6 px-12 text-2xl',
20
+ lg: 'py-8 px-16 text-3xl',
21
+ };
22
+
23
+ export const DualChoice = forwardRef<HTMLDivElement, DualChoiceProps>(
24
+ ({ yesLabel = 'OUI', noLabel = 'NON', onYes, onNo, variant = 'default', size = 'md', showDivider = true, disabled = false, selectedValue = null, className, ...props }, ref) => {
25
+ const isMinimal = variant === 'minimal';
26
+ const isStacked = variant === 'stacked';
27
+ const isDramatic = variant === 'dramatic';
28
+
29
+ const containerClasses = `
30
+ flex font-display
31
+ ${isStacked ? 'flex-col' : ''}
32
+ ${disabled ? 'opacity-50 pointer-events-none' : ''}
33
+ ${className || ''}
34
+ `;
35
+
36
+ const baseButtonClasses = `
37
+ flex-1 tracking-[0.15em] uppercase cursor-pointer transition-all relative overflow-hidden
38
+ ${sizeClasses[size]}
39
+ before:content-[''] before:absolute before:top-0 before:-left-full
40
+ before:w-full before:h-full before:transition-[left] before:duration-300 before:-z-10
41
+ hover:before:left-0
42
+ `;
43
+
44
+ const yesClasses = `
45
+ ${baseButtonClasses}
46
+ ${isMinimal
47
+ ? 'border-0 text-stone-300 after:absolute after:bottom-0 after:left-0 after:right-0 after:h-0.5 after:bg-current after:scale-x-0 after:transition-transform hover:after:scale-x-100 before:hidden'
48
+ : `border-2 ${isDramatic ? 'border-amber-500 text-amber-500 before:bg-amber-500' : 'border-gray-600 text-stone-200 before:bg-stone-200'}`
49
+ }
50
+ hover:text-black
51
+ ${selectedValue === 'yes' ? 'bg-current text-black' : ''}
52
+ `;
53
+
54
+ const noClasses = `
55
+ ${baseButtonClasses}
56
+ ${isMinimal
57
+ ? 'border-0 text-red-500 after:absolute after:bottom-0 after:left-0 after:right-0 after:h-0.5 after:bg-current after:scale-x-0 after:transition-transform hover:after:scale-x-100 before:hidden'
58
+ : `border-2 border-red-500 text-red-500 before:bg-red-500 ${isStacked ? 'border-t-0' : 'border-l-0'}`
59
+ }
60
+ hover:text-black
61
+ ${selectedValue === 'no' ? 'bg-red-500 text-black' : ''}
62
+ `;
63
+
64
+ return (
65
+ <div ref={ref} className={containerClasses} {...props}>
66
+ <button className={yesClasses} onClick={onYes} disabled={disabled}>
67
+ {yesLabel}
68
+ </button>
69
+
70
+ {showDivider && !isMinimal && (
71
+ <div className={`
72
+ flex items-center justify-center relative
73
+ ${isStacked ? 'w-full h-0.5' : 'w-0.5'}
74
+ bg-gray-700
75
+ `}>
76
+ <span className="absolute text-[0.6rem] text-gray-500 bg-black px-1 tracking-widest">OU</span>
77
+ </div>
78
+ )}
79
+
80
+ <button className={noClasses} onClick={onNo} disabled={disabled}>
81
+ {noLabel}
82
+ </button>
83
+ </div>
84
+ );
85
+ }
86
+ );
87
+
88
+ DualChoice.displayName = 'DualChoice';
89
+ export default DualChoice;
@@ -0,0 +1,96 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './tension-bar.module.css';
5
+
6
+ export interface TensionBarProps extends HTMLAttributes<HTMLDivElement> {
7
+ value: number;
8
+ max?: number;
9
+ labelLeft?: string;
10
+ labelRight?: string;
11
+ showPercentage?: boolean;
12
+ showMarkers?: boolean;
13
+ markerCount?: number;
14
+ variant?: 'default' | 'gold' | 'danger' | 'segmented' | 'dramatic';
15
+ size?: 'sm' | 'md' | 'lg';
16
+ innerText?: string;
17
+ animated?: boolean;
18
+ dangerThreshold?: number;
19
+ }
20
+
21
+ export const TensionBar = forwardRef<HTMLDivElement, TensionBarProps>(
22
+ ({ value, max = 100, labelLeft, labelRight, showPercentage = false, showMarkers = false, markerCount = 10, variant = 'default', size = 'md', innerText, animated = false, dangerThreshold = 80, className, ...props }, ref) => {
23
+ const percentage = Math.min(100, Math.max(0, (value / max) * 100));
24
+ const isHigh = variant === 'danger' && percentage >= dangerThreshold;
25
+
26
+ const containerClasses = [
27
+ styles.container,
28
+ variant === 'gold' && styles.variantGold,
29
+ variant === 'danger' && styles.variantDanger,
30
+ variant === 'segmented' && styles.variantSegmented,
31
+ variant === 'dramatic' && styles.variantDramatic,
32
+ size === 'sm' && styles.sizeSm,
33
+ size === 'lg' && styles.sizeLg,
34
+ innerText && styles.withText,
35
+ animated && styles.animated,
36
+ isHigh && styles.high,
37
+ className,
38
+ ].filter(Boolean).join(' ');
39
+
40
+ const renderSegmented = () => {
41
+ const segments = [];
42
+ const filledCount = Math.floor((percentage / 100) * markerCount);
43
+ for (let i = 0; i < markerCount; i++) {
44
+ segments.push(
45
+ <div
46
+ key={i}
47
+ className={`${styles.segment} ${i < filledCount ? styles.segmentFilled : ''}`}
48
+ />
49
+ );
50
+ }
51
+ return segments;
52
+ };
53
+
54
+ return (
55
+ <div ref={ref} className={containerClasses} {...props}>
56
+ {(labelLeft || labelRight) && (
57
+ <div className={styles.labels}>
58
+ <span className={styles.labelLeft}>{labelLeft}</span>
59
+ <span className={styles.labelRight}>{labelRight}</span>
60
+ </div>
61
+ )}
62
+
63
+ <div className={styles.track}>
64
+ {innerText && <span className={styles.innerText}>{innerText}</span>}
65
+
66
+ {variant === 'segmented' ? (
67
+ renderSegmented()
68
+ ) : (
69
+ <div className={styles.fill} style={{ width: `${percentage}%` }} />
70
+ )}
71
+
72
+ {showPercentage && (
73
+ <span className={styles.percentage}>{Math.round(percentage)}%</span>
74
+ )}
75
+ </div>
76
+
77
+ {showMarkers && variant !== 'segmented' && (
78
+ <div className={styles.markers}>
79
+ {Array.from({ length: markerCount + 1 }).map((_, i) => {
80
+ const markerPercent = (i / markerCount) * 100;
81
+ return (
82
+ <div
83
+ key={i}
84
+ className={`${styles.marker} ${markerPercent <= percentage ? styles.markerActive : ''}`}
85
+ />
86
+ );
87
+ })}
88
+ </div>
89
+ )}
90
+ </div>
91
+ );
92
+ }
93
+ );
94
+
95
+ TensionBar.displayName = 'TensionBar';
96
+ export default TensionBar;