@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
@@ -0,0 +1,91 @@
1
+ /* progress-dots - Indicateur de section (dots) */
2
+ .container {
3
+ position: fixed;
4
+ right: 2rem;
5
+ top: 50%;
6
+ transform: translateY(-50%);
7
+ z-index: 100;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 1.5rem;
11
+ }
12
+
13
+ .dot {
14
+ width: 12px;
15
+ height: 12px;
16
+ border-radius: 50%;
17
+ background: transparent;
18
+ border: 2px solid var(--chaos-bone-dark, #666);
19
+ cursor: pointer;
20
+ transition: all 0.3s ease;
21
+ position: relative;
22
+ }
23
+
24
+ .dot::before {
25
+ content: '';
26
+ position: absolute;
27
+ inset: 2px;
28
+ border-radius: 50%;
29
+ background: var(--chaos-bone-dark, #666);
30
+ transform: scale(0);
31
+ transition: transform 0.3s ease;
32
+ }
33
+
34
+ .dot:hover { border-color: var(--chaos-blood, #ff0040); }
35
+ .dot:hover::before { transform: scale(0.5); background: var(--chaos-blood, #ff0040); }
36
+
37
+ .dotActive {
38
+ border-color: var(--chaos-blood, #ff0040);
39
+ box-shadow: 0 0 10px rgba(255, 0, 64, 0.5);
40
+ }
41
+
42
+ .dotActive::before { transform: scale(1); background: var(--chaos-blood, #ff0040); }
43
+
44
+ .dotWithLabel { position: relative; }
45
+
46
+ .label {
47
+ position: absolute;
48
+ right: 100%;
49
+ top: 50%;
50
+ transform: translateY(-50%);
51
+ padding-right: 1rem;
52
+ font-size: 0.6rem;
53
+ letter-spacing: 0.2em;
54
+ text-transform: uppercase;
55
+ color: var(--chaos-bone-dark, #666);
56
+ opacity: 0;
57
+ transition: all 0.3s;
58
+ white-space: nowrap;
59
+ font-family: var(--chaos-font-mono, monospace);
60
+ }
61
+
62
+ .dot:hover .label { opacity: 1; }
63
+ .dotActive .label { opacity: 1; color: var(--chaos-blood, #ff0040); }
64
+
65
+ .withConnector .dot:not(:last-child)::after {
66
+ content: '';
67
+ position: absolute;
68
+ top: 100%;
69
+ left: 50%;
70
+ width: 1px;
71
+ height: 1.5rem;
72
+ background: var(--chaos-bone-dark, #333);
73
+ transform: translateX(-50%);
74
+ }
75
+
76
+ .variantGold .dot { border-color: var(--chaos-gold-dark, #8b7017); }
77
+ .variantGold .dot:hover { border-color: var(--chaos-gold, #c9a227); }
78
+ .variantGold .dot:hover::before { background: var(--chaos-gold, #c9a227); }
79
+ .variantGold .dotActive { border-color: var(--chaos-gold, #c9a227); box-shadow: 0 0 10px rgba(201, 162, 39, 0.5); }
80
+ .variantGold .dotActive::before { background: var(--chaos-gold, #c9a227); }
81
+ .variantGold .dotActive .label { color: var(--chaos-gold, #c9a227); }
82
+
83
+ .variantMinimal .dot { width: 8px; height: 8px; border-width: 1px; }
84
+
85
+ .horizontal {
86
+ flex-direction: row;
87
+ top: auto;
88
+ bottom: 2rem;
89
+ right: 50%;
90
+ transform: translateX(50%);
91
+ }
@@ -0,0 +1,59 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, ReactNode } from 'react';
4
+ import styles from './scattered-nav.module.css';
5
+
6
+ export interface ScatteredNavItemProps {
7
+ href?: string;
8
+ children: ReactNode;
9
+ variant?: 'default' | 'logo' | 'corrupt' | 'status' | 'blink';
10
+ scattered?: 1 | 2 | 3 | 4 | 5;
11
+ glitchText?: string;
12
+ }
13
+
14
+ export interface ScatteredNavProps extends HTMLAttributes<HTMLElement> {
15
+ items?: ScatteredNavItemProps[];
16
+ children?: ReactNode;
17
+ }
18
+
19
+ export const ScatteredNavItem = forwardRef<HTMLAnchorElement | HTMLDivElement, ScatteredNavItemProps>(
20
+ ({ href, children, variant = 'default', scattered, glitchText, ...props }, ref) => {
21
+ const classes = [
22
+ styles.item,
23
+ variant === 'logo' && styles.logo,
24
+ variant === 'corrupt' && styles.corrupt,
25
+ variant === 'status' && styles.status,
26
+ variant === 'blink' && styles.blink,
27
+ scattered && styles[`scattered${scattered}`],
28
+ ].filter(Boolean).join(' ');
29
+
30
+ const content = glitchText ? (
31
+ <span className={styles.glitch} data-text={glitchText}>{children}</span>
32
+ ) : variant === 'status' ? (
33
+ <><span className={styles.statusDot} />{children}</>
34
+ ) : children;
35
+
36
+ if (href) {
37
+ return <a ref={ref as any} href={href} className={classes} {...props}>{content}</a>;
38
+ }
39
+ return <div ref={ref as any} className={classes} {...props}>{content}</div>;
40
+ }
41
+ );
42
+
43
+ ScatteredNavItem.displayName = 'ScatteredNavItem';
44
+
45
+ export const ScatteredNav = forwardRef<HTMLElement, ScatteredNavProps>(
46
+ ({ items, children, className, ...props }, ref) => {
47
+ return (
48
+ <nav ref={ref} className={`${styles.nav} ${className || ''}`} {...props}>
49
+ {items?.map((item, i) => (
50
+ <ScatteredNavItem key={i} {...item} />
51
+ ))}
52
+ {children}
53
+ </nav>
54
+ );
55
+ }
56
+ );
57
+
58
+ ScatteredNav.displayName = 'ScatteredNav';
59
+ export default ScatteredNav;
@@ -0,0 +1,113 @@
1
+ /* scattered-nav - Nav éclatée/fragments positionnés aléatoirement */
2
+ .nav {
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ right: 0;
7
+ z-index: 1000;
8
+ height: 60px;
9
+ background: var(--chaos-void, #0a0a0a);
10
+ border-bottom: 1px solid var(--chaos-blood, #ff0040);
11
+ display: flex;
12
+ align-items: center;
13
+ padding: 0 1rem;
14
+ }
15
+
16
+ .item {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ padding: 0 1rem;
21
+ height: 100%;
22
+ font-size: 0.7rem;
23
+ letter-spacing: 0.1em;
24
+ text-transform: uppercase;
25
+ font-family: var(--chaos-font-mono, 'Space Mono', monospace);
26
+ border-right: 1px solid rgba(255, 255, 255, 0.1);
27
+ transition: all 0.2s;
28
+ color: var(--chaos-bone-dark, #888);
29
+ text-decoration: none;
30
+ }
31
+
32
+ .item:hover {
33
+ background: var(--chaos-blood, #ff0040);
34
+ color: var(--chaos-void, #0a0a0a);
35
+ }
36
+
37
+ .scattered1 { transform: translateY(3px); }
38
+ .scattered2 { transform: rotate(-2deg); border-left: 1px solid var(--chaos-blood, #ff0040); }
39
+ .scattered3 { transform: translateY(-2px) rotate(1deg); }
40
+ .scattered4 { transform: translateX(-3px); }
41
+ .scattered5 { transform: rotate(1.5deg) translateY(1px); }
42
+
43
+ .logo {
44
+ background: var(--chaos-blood, #ff0040);
45
+ color: var(--chaos-void, #0a0a0a);
46
+ font-family: var(--chaos-font-display, 'Bebas Neue', sans-serif);
47
+ font-size: 1.3rem;
48
+ padding: 0 1.5rem;
49
+ }
50
+
51
+ .glitch { position: relative; }
52
+
53
+ .glitch::before {
54
+ content: attr(data-text);
55
+ position: absolute;
56
+ left: 2px;
57
+ top: 0;
58
+ color: var(--chaos-bone, #e8e8e8);
59
+ opacity: 0.5;
60
+ animation: navGlitch 0.3s infinite;
61
+ }
62
+
63
+ @keyframes navGlitch {
64
+ 0%, 100% { clip-path: inset(0 0 80% 0); }
65
+ 25% { clip-path: inset(20% 0 60% 0); }
66
+ 50% { clip-path: inset(40% 0 40% 0); }
67
+ 75% { clip-path: inset(60% 0 20% 0); }
68
+ }
69
+
70
+ .corrupt {
71
+ font-family: var(--chaos-font-mono, monospace);
72
+ color: var(--chaos-blood, #ff0040);
73
+ font-size: 0.6rem;
74
+ opacity: 0.5;
75
+ animation: flickerText 3s infinite;
76
+ }
77
+
78
+ @keyframes flickerText {
79
+ 0%, 90%, 100% { opacity: 0.5; }
80
+ 92%, 94%, 96% { opacity: 0; }
81
+ }
82
+
83
+ .status {
84
+ margin-left: auto;
85
+ gap: 0.5rem;
86
+ font-size: 0.6rem;
87
+ background: rgba(0, 0, 0, 0.3);
88
+ padding: 0 1.5rem;
89
+ }
90
+
91
+ .statusDot {
92
+ width: 6px;
93
+ height: 6px;
94
+ background: var(--chaos-blood, #ff0040);
95
+ border-radius: 50%;
96
+ animation: pulse 2s ease-in-out infinite;
97
+ display: inline-block;
98
+ margin-right: 0.5rem;
99
+ }
100
+
101
+ @keyframes pulse {
102
+ 0%, 100% { opacity: 1; transform: scale(1); }
103
+ 50% { opacity: 0.5; transform: scale(0.8); }
104
+ }
105
+
106
+ .blink {
107
+ animation: blink 1s step-start infinite;
108
+ color: var(--chaos-blood, #ff0040);
109
+ }
110
+
111
+ @keyframes blink {
112
+ 50% { opacity: 0; }
113
+ }
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState } from 'react';
4
+ import styles from './scroll-indicator.module.css';
5
+
6
+ export interface ScrollIndicatorProps extends HTMLAttributes<HTMLDivElement> {
7
+ text?: string;
8
+ showArrow?: boolean;
9
+ showPercentage?: boolean;
10
+ variant?: 'default' | 'blood' | 'minimal';
11
+ position?: 'right' | 'left';
12
+ trackHeight?: number;
13
+ }
14
+
15
+ export const ScrollIndicator = forwardRef<HTMLDivElement, ScrollIndicatorProps>(
16
+ ({ text = 'SCROLL', showArrow = false, showPercentage = false, variant = 'default', position = 'right', trackHeight = 100, className, ...props }, ref) => {
17
+ const [scrollProgress, setScrollProgress] = useState(0);
18
+
19
+ useEffect(() => {
20
+ const handleScroll = () => {
21
+ const scrollTop = window.scrollY;
22
+ const docHeight = document.documentElement.scrollHeight - window.innerHeight;
23
+ const progress = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0;
24
+ setScrollProgress(Math.min(100, Math.max(0, progress)));
25
+ };
26
+
27
+ window.addEventListener('scroll', handleScroll, { passive: true });
28
+ handleScroll();
29
+ return () => window.removeEventListener('scroll', handleScroll);
30
+ }, []);
31
+
32
+ const thumbTop = (scrollProgress / 100) * (trackHeight - 20);
33
+
34
+ const containerClasses = [
35
+ styles.container,
36
+ variant === 'blood' && styles.variantBlood,
37
+ variant === 'minimal' && styles.variantMinimal,
38
+ position === 'left' && styles.positionLeft,
39
+ className,
40
+ ].filter(Boolean).join(' ');
41
+
42
+ return (
43
+ <div ref={ref} className={containerClasses} {...props}>
44
+ <div className={styles.inner}>
45
+ {showArrow && <span className={styles.arrow}>↓</span>}
46
+ <div className={styles.track} style={{ height: trackHeight }}>
47
+ <div className={styles.thumb} style={{ top: thumbTop }} />
48
+ </div>
49
+ {text && <span className={styles.text}>{text}</span>}
50
+ {showPercentage && <span className={styles.percentage}>{Math.round(scrollProgress)}%</span>}
51
+ </div>
52
+ </div>
53
+ );
54
+ }
55
+ );
56
+
57
+ ScrollIndicator.displayName = 'ScrollIndicator';
58
+ export default ScrollIndicator;
@@ -0,0 +1,82 @@
1
+ /* scroll-indicator - Indicateur de scroll vertical avec texte */
2
+ .container {
3
+ position: fixed;
4
+ right: 0;
5
+ top: 0;
6
+ bottom: 0;
7
+ width: 60px;
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ z-index: 100;
12
+ }
13
+
14
+ .inner {
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ gap: 1rem;
19
+ }
20
+
21
+ .track {
22
+ width: 2px;
23
+ height: 100px;
24
+ background: var(--chaos-iron, #2a2820);
25
+ position: relative;
26
+ border-radius: 1px;
27
+ }
28
+
29
+ .thumb {
30
+ width: 6px;
31
+ height: 20px;
32
+ background: var(--chaos-gold, #c9a227);
33
+ position: absolute;
34
+ left: -2px;
35
+ top: 0;
36
+ border-radius: 3px;
37
+ transition: top 0.3s ease-out;
38
+ box-shadow: 0 0 10px rgba(201, 162, 39, 0.3);
39
+ }
40
+
41
+ .text {
42
+ writing-mode: vertical-rl;
43
+ text-orientation: mixed;
44
+ font-family: var(--chaos-font-serif, 'Cinzel', serif);
45
+ font-size: 0.5rem;
46
+ letter-spacing: 0.4em;
47
+ color: var(--chaos-bone-dark, #9a8b6f);
48
+ transform: rotate(180deg);
49
+ text-transform: uppercase;
50
+ }
51
+
52
+ .arrow {
53
+ animation: bounce 2s ease-in-out infinite;
54
+ color: var(--chaos-gold, #c9a227);
55
+ font-size: 0.8rem;
56
+ }
57
+
58
+ @keyframes bounce {
59
+ 0%, 100% { transform: translateY(0); opacity: 1; }
60
+ 50% { transform: translateY(5px); opacity: 0.5; }
61
+ }
62
+
63
+ .variantBlood .track { background: rgba(255, 0, 64, 0.2); }
64
+ .variantBlood .thumb { background: var(--chaos-blood, #ff0040); box-shadow: 0 0 10px rgba(255, 0, 64, 0.5); }
65
+ .variantBlood .text { color: var(--chaos-bone-dark, #666); }
66
+ .variantBlood .arrow { color: var(--chaos-blood, #ff0040); }
67
+
68
+ .variantMinimal .track { width: 1px; height: 60px; }
69
+ .variantMinimal .thumb { width: 3px; height: 10px; left: -1px; }
70
+ .variantMinimal .text { display: none; }
71
+
72
+ .positionLeft { left: 0; right: auto; }
73
+ .positionLeft .text { transform: rotate(0deg); }
74
+
75
+ .percentage {
76
+ font-family: var(--chaos-font-mono, monospace);
77
+ font-size: 0.6rem;
78
+ color: var(--chaos-gold, #c9a227);
79
+ margin-top: 0.5rem;
80
+ }
81
+
82
+ .variantBlood .percentage { color: var(--chaos-blood, #ff0040); }
@@ -0,0 +1,59 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, ReactNode } from 'react';
4
+ import styles from './vertical-nav.module.css';
5
+
6
+ export interface VerticalNavItemProps {
7
+ href?: string;
8
+ glyph: string;
9
+ label?: string;
10
+ active?: boolean;
11
+ onClick?: () => void;
12
+ }
13
+
14
+ export interface VerticalNavProps extends HTMLAttributes<HTMLElement> {
15
+ items?: VerticalNavItemProps[];
16
+ runeTop?: string;
17
+ runeBottom?: string;
18
+ variant?: 'default' | 'dark';
19
+ size?: 'default' | 'compact';
20
+ children?: ReactNode;
21
+ }
22
+
23
+ export const VerticalNavItem = forwardRef<HTMLAnchorElement, VerticalNavItemProps>(
24
+ ({ href, glyph, label, active, onClick, ...props }, ref) => {
25
+ const classes = [styles.glyph, active && styles.glyphActive].filter(Boolean).join(' ');
26
+
27
+ if (href) {
28
+ return <a ref={ref} href={href} className={classes} data-label={label} onClick={onClick} {...props}>{glyph}</a>;
29
+ }
30
+ return <button className={classes} data-label={label} onClick={onClick} {...props}>{glyph}</button>;
31
+ }
32
+ );
33
+
34
+ VerticalNavItem.displayName = 'VerticalNavItem';
35
+
36
+ export const VerticalNav = forwardRef<HTMLElement, VerticalNavProps>(
37
+ ({ items, runeTop = 'ᛟ', runeBottom = 'ᛞ', variant = 'default', size = 'default', children, className, ...props }, ref) => {
38
+ const classes = [
39
+ styles.nav,
40
+ variant === 'dark' && styles.variantDark,
41
+ size === 'compact' && styles.sizeCompact,
42
+ className,
43
+ ].filter(Boolean).join(' ');
44
+
45
+ return (
46
+ <nav ref={ref} className={classes} {...props}>
47
+ <div className={styles.rune}>{runeTop}</div>
48
+ <div className={styles.items}>
49
+ {items?.map((item, i) => <VerticalNavItem key={i} {...item} />)}
50
+ {children}
51
+ </div>
52
+ <div className={styles.rune}>{runeBottom}</div>
53
+ </nav>
54
+ );
55
+ }
56
+ );
57
+
58
+ VerticalNav.displayName = 'VerticalNav';
59
+ export default VerticalNav;
@@ -0,0 +1,98 @@
1
+ /* vertical-nav - Nav latérale avec glyphes/runes */
2
+ .nav {
3
+ position: fixed;
4
+ left: 0;
5
+ top: 0;
6
+ bottom: 0;
7
+ width: 80px;
8
+ background: linear-gradient(to right, var(--chaos-obsidian, #121210) 0%, transparent 100%);
9
+ border-right: 1px solid var(--chaos-iron, #2a2820);
10
+ display: flex;
11
+ flex-direction: column;
12
+ align-items: center;
13
+ justify-content: space-between;
14
+ padding: 2rem 0;
15
+ z-index: 100;
16
+ }
17
+
18
+ .rune {
19
+ font-size: 1.5rem;
20
+ color: var(--chaos-gold-dark, #8b7017);
21
+ opacity: 0.5;
22
+ animation: runeGlow 4s ease-in-out infinite;
23
+ }
24
+
25
+ @keyframes runeGlow {
26
+ 0%, 100% { opacity: 0.3; text-shadow: none; }
27
+ 50% { opacity: 0.8; text-shadow: 0 0 10px var(--chaos-gold, #c9a227); }
28
+ }
29
+
30
+ .items {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 3rem;
34
+ }
35
+
36
+ .glyph {
37
+ width: 40px;
38
+ height: 40px;
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ font-family: var(--chaos-font-display, 'Cinzel Decorative', serif);
43
+ font-size: 1rem;
44
+ color: var(--chaos-bone-dark, #9a8b6f);
45
+ text-decoration: none;
46
+ border: 1px solid var(--chaos-iron, #2a2820);
47
+ position: relative;
48
+ transition: all 0.4s;
49
+ }
50
+
51
+ .glyph::before {
52
+ content: attr(data-label);
53
+ position: absolute;
54
+ left: 60px;
55
+ font-family: var(--chaos-font-serif, 'Cinzel', serif);
56
+ font-size: 0.6rem;
57
+ letter-spacing: 0.3em;
58
+ color: var(--chaos-bone-dark, #9a8b6f);
59
+ opacity: 0;
60
+ transform: translateX(-10px);
61
+ transition: all 0.3s;
62
+ white-space: nowrap;
63
+ text-transform: uppercase;
64
+ }
65
+
66
+ .glyph:hover {
67
+ color: var(--chaos-gold, #c9a227);
68
+ border-color: var(--chaos-gold, #c9a227);
69
+ background: rgba(201, 162, 39, 0.1);
70
+ }
71
+
72
+ .glyph:hover::before {
73
+ opacity: 1;
74
+ transform: translateX(0);
75
+ }
76
+
77
+ .glyphActive {
78
+ color: var(--chaos-gold, #c9a227);
79
+ border-color: var(--chaos-gold, #c9a227);
80
+ box-shadow: 0 0 15px rgba(201, 162, 39, 0.3);
81
+ }
82
+
83
+ .variantDark {
84
+ background: linear-gradient(to right, var(--chaos-void, #0a0a0a) 0%, transparent 100%);
85
+ border-right-color: var(--chaos-blood, #ff0040);
86
+ }
87
+
88
+ .variantDark .rune { color: var(--chaos-blood, #ff0040); }
89
+ .variantDark .glyph { border-color: rgba(255, 0, 64, 0.3); }
90
+ .variantDark .glyph:hover {
91
+ color: var(--chaos-blood, #ff0040);
92
+ border-color: var(--chaos-blood, #ff0040);
93
+ background: rgba(255, 0, 64, 0.1);
94
+ }
95
+
96
+ .sizeCompact { width: 50px; padding: 1rem 0; }
97
+ .sizeCompact .glyph { width: 30px; height: 30px; font-size: 0.8rem; }
98
+ .sizeCompact .rune { font-size: 1rem; }
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './neon-alert.module.css';
5
+
6
+ export interface NeonAlertProps extends HTMLAttributes<HTMLDivElement> {
7
+ /** Alert type */
8
+ variant?: 'info' | 'success' | 'warning' | 'error';
9
+ /** Alert title */
10
+ title?: string;
11
+ /** Dismissible with close button */
12
+ dismissible?: boolean;
13
+ /** Close callback */
14
+ onDismiss?: () => void;
15
+ }
16
+
17
+ export const NeonAlert = forwardRef<HTMLDivElement, NeonAlertProps>(
18
+ (
19
+ {
20
+ children,
21
+ variant = 'info',
22
+ title,
23
+ dismissible = false,
24
+ onDismiss,
25
+ className,
26
+ ...props
27
+ },
28
+ ref
29
+ ) => {
30
+ return (
31
+ <div
32
+ ref={ref}
33
+ className={`${styles.alert} ${styles[variant]} ${className || ''}`}
34
+ role="alert"
35
+ {...props}
36
+ >
37
+ <div className={styles.content}>
38
+ {title && <div className={styles.title}>{title}</div>}
39
+ <div className={styles.message}>{children}</div>
40
+ </div>
41
+ {dismissible && (
42
+ <button className={styles.dismiss} onClick={onDismiss} aria-label="Dismiss">
43
+
44
+ </button>
45
+ )}
46
+ </div>
47
+ );
48
+ }
49
+ );
50
+
51
+ NeonAlert.displayName = 'NeonAlert';
52
+
53
+ export default NeonAlert;
@@ -0,0 +1,60 @@
1
+ .alert {
2
+ --alert-color: #00f0ff;
3
+
4
+ display: flex;
5
+ align-items: flex-start;
6
+ gap: 0.75rem;
7
+ padding: 1rem;
8
+ background: rgba(0, 0, 0, 0.5);
9
+ border: 1px solid var(--alert-color);
10
+ border-left: 4px solid var(--alert-color);
11
+ box-shadow: 0 0 10px rgba(0, 240, 255, 0.2);
12
+ animation: alert-slide 0.3s ease-out;
13
+ }
14
+
15
+ @keyframes alert-slide {
16
+ from { opacity: 0; transform: translateX(-20px); }
17
+ to { opacity: 1; transform: translateX(0); }
18
+ }
19
+
20
+ .info { --alert-color: #00f0ff; }
21
+ .success { --alert-color: #00ff88; }
22
+ .warning { --alert-color: #ffaa00; }
23
+ .error { --alert-color: #ff0040; }
24
+
25
+ .content {
26
+ flex: 1;
27
+ min-width: 0;
28
+ }
29
+
30
+ .title {
31
+ margin-bottom: 0.25rem;
32
+ font-family: var(--font-display, 'Orbitron', sans-serif);
33
+ font-size: 0.8125rem;
34
+ font-weight: 700;
35
+ text-transform: uppercase;
36
+ letter-spacing: 0.0625em;
37
+ color: var(--alert-color);
38
+ }
39
+
40
+ .message {
41
+ font-family: var(--font-body, 'Rajdhani', sans-serif);
42
+ font-size: 0.875rem;
43
+ color: rgba(255, 255, 255, 0.8);
44
+ line-height: 1.5;
45
+ }
46
+
47
+ .dismiss {
48
+ flex-shrink: 0;
49
+ padding: 0.25rem 0.5rem;
50
+ font-size: 0.875rem;
51
+ color: rgba(255, 255, 255, 0.5);
52
+ background: transparent;
53
+ border: none;
54
+ cursor: pointer;
55
+ transition: color 0.2s ease;
56
+ }
57
+
58
+ .dismiss:hover {
59
+ color: var(--alert-color);
60
+ }