@gigo-ui/components 1.0.0-alpha

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 (96) hide show
  1. package/README.md +49 -0
  2. package/dist/assets/favicon.svg +1 -0
  3. package/dist/components/chaos/CatchSubmit.svelte +144 -0
  4. package/dist/components/chaos/CatchSubmit.svelte.d.ts +11 -0
  5. package/dist/components/chaos/ChaosButton.svelte +132 -0
  6. package/dist/components/chaos/ChaosButton.svelte.d.ts +4 -0
  7. package/dist/components/chaos/ChaosForm.svelte +206 -0
  8. package/dist/components/chaos/ChaosForm.svelte.d.ts +10 -0
  9. package/dist/components/chaos/ColorPickerWrong.svelte +141 -0
  10. package/dist/components/chaos/ColorPickerWrong.svelte.d.ts +11 -0
  11. package/dist/components/chaos/DropdownCalc.svelte +195 -0
  12. package/dist/components/chaos/DropdownCalc.svelte.d.ts +8 -0
  13. package/dist/components/chaos/GhostCard.svelte +157 -0
  14. package/dist/components/chaos/GhostCard.svelte.d.ts +13 -0
  15. package/dist/components/chaos/GravityInput.svelte +161 -0
  16. package/dist/components/chaos/GravityInput.svelte.d.ts +13 -0
  17. package/dist/components/chaos/PasswordPeekhole.svelte +141 -0
  18. package/dist/components/chaos/PasswordPeekhole.svelte.d.ts +11 -0
  19. package/dist/components/chaos/ProgressDoom.svelte +139 -0
  20. package/dist/components/chaos/ProgressDoom.svelte.d.ts +12 -0
  21. package/dist/components/chaos/RotaryDial.svelte +221 -0
  22. package/dist/components/chaos/RotaryDial.svelte.d.ts +10 -0
  23. package/dist/components/chaos/SliderPhone.svelte +92 -0
  24. package/dist/components/chaos/SliderPhone.svelte.d.ts +10 -0
  25. package/dist/components/chaos/TermsSidescroll.svelte +121 -0
  26. package/dist/components/chaos/TermsSidescroll.svelte.d.ts +12 -0
  27. package/dist/components/chaos/VolumeSlider.svelte +116 -0
  28. package/dist/components/chaos/VolumeSlider.svelte.d.ts +14 -0
  29. package/dist/components/ui/Button.svelte +162 -0
  30. package/dist/components/ui/Button.svelte.d.ts +4 -0
  31. package/dist/components/ui/Card.svelte +141 -0
  32. package/dist/components/ui/Card.svelte.d.ts +4 -0
  33. package/dist/components/ui/Carousel.svelte +164 -0
  34. package/dist/components/ui/Carousel.svelte.d.ts +4 -0
  35. package/dist/components/ui/Form.svelte +178 -0
  36. package/dist/components/ui/Form.svelte.d.ts +4 -0
  37. package/dist/components/ui/Input.svelte +135 -0
  38. package/dist/components/ui/Input.svelte.d.ts +4 -0
  39. package/dist/components/ui/Modal.svelte +173 -0
  40. package/dist/components/ui/Modal.svelte.d.ts +4 -0
  41. package/dist/components/ui/Navigation.svelte +91 -0
  42. package/dist/components/ui/Navigation.svelte.d.ts +4 -0
  43. package/dist/docs/categories.d.ts +13 -0
  44. package/dist/docs/categories.js +17 -0
  45. package/dist/docs/component-data.d.ts +6 -0
  46. package/dist/docs/component-data.js +49 -0
  47. package/dist/docs/components/badui/catch-submit.d.ts +2 -0
  48. package/dist/docs/components/badui/catch-submit.js +49 -0
  49. package/dist/docs/components/badui/color-picker-wrong.d.ts +2 -0
  50. package/dist/docs/components/badui/color-picker-wrong.js +40 -0
  51. package/dist/docs/components/badui/dropdown-calc.d.ts +2 -0
  52. package/dist/docs/components/badui/dropdown-calc.js +28 -0
  53. package/dist/docs/components/badui/ghost-card.d.ts +2 -0
  54. package/dist/docs/components/badui/ghost-card.js +54 -0
  55. package/dist/docs/components/badui/gravity-input.d.ts +2 -0
  56. package/dist/docs/components/badui/gravity-input.js +64 -0
  57. package/dist/docs/components/badui/password-peekhole.d.ts +2 -0
  58. package/dist/docs/components/badui/password-peekhole.js +51 -0
  59. package/dist/docs/components/badui/progress-doom.d.ts +2 -0
  60. package/dist/docs/components/badui/progress-doom.js +48 -0
  61. package/dist/docs/components/badui/rotary-dial.d.ts +2 -0
  62. package/dist/docs/components/badui/rotary-dial.js +40 -0
  63. package/dist/docs/components/badui/slider-phone.d.ts +2 -0
  64. package/dist/docs/components/badui/slider-phone.js +40 -0
  65. package/dist/docs/components/badui/terms-sidescroll.d.ts +2 -0
  66. package/dist/docs/components/badui/terms-sidescroll.js +46 -0
  67. package/dist/docs/components/badui/volume-slider.d.ts +2 -0
  68. package/dist/docs/components/badui/volume-slider.js +52 -0
  69. package/dist/docs/components/chaos/chaos-button.d.ts +2 -0
  70. package/dist/docs/components/chaos/chaos-button.js +48 -0
  71. package/dist/docs/components/chaos/chaos-form.d.ts +2 -0
  72. package/dist/docs/components/chaos/chaos-form.js +68 -0
  73. package/dist/docs/components/standard/button.d.ts +2 -0
  74. package/dist/docs/components/standard/button.js +66 -0
  75. package/dist/docs/components/standard/card.d.ts +2 -0
  76. package/dist/docs/components/standard/card.js +55 -0
  77. package/dist/docs/components/standard/carousel.d.ts +2 -0
  78. package/dist/docs/components/standard/carousel.js +63 -0
  79. package/dist/docs/components/standard/form.d.ts +2 -0
  80. package/dist/docs/components/standard/form.js +57 -0
  81. package/dist/docs/components/standard/input.d.ts +2 -0
  82. package/dist/docs/components/standard/input.js +65 -0
  83. package/dist/docs/components/standard/modal.d.ts +2 -0
  84. package/dist/docs/components/standard/modal.js +63 -0
  85. package/dist/docs/components/standard/navigation.d.ts +2 -0
  86. package/dist/docs/components/standard/navigation.js +51 -0
  87. package/dist/docs/types.d.ts +16 -0
  88. package/dist/docs/types.js +1 -0
  89. package/dist/index.d.ts +22 -0
  90. package/dist/index.js +21 -0
  91. package/dist/styles/globals.css +569 -0
  92. package/dist/types/index.d.ts +177 -0
  93. package/dist/types/index.js +1 -0
  94. package/dist/utils/cn.d.ts +9 -0
  95. package/dist/utils/cn.js +90 -0
  96. package/package.json +61 -0
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # 🌀 @gigo-ui/components
2
+
3
+ ### _High-Performance Components for Low-Performance Experiences._
4
+
5
+ [![Svelte](https://img.shields.io/badge/Svelte-4.0+-FF3E00?logo=svelte&logoColor=white)](https://svelte.dev)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
7
+ [![NPM](https://img.shields.io/badge/NPM-@gigo--ui/components-cb3837?logo=npm)](https://www.npmjs.com/package/@gigo-ui/components)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ **@gigo-ui/components** (GIGO: Garbage In, Garbage Out) is a SvelteKit-native component library dedicated to the art of **User Hostility**. While modern web standards prioritize accessibility and speed, this library prioritizes psychological warfare.
11
+
12
+ Designed for developers building prank sites, experimental art pieces, or "UX from Hell" challenges, @gigo-ui provides typed, performant, and deeply annoying UI primitives.
13
+
14
+ ---
15
+
16
+ ## 📦 Core Modules
17
+
18
+ The library is architected into two distinct spirits: **Chaos** (The Weaponry) and **UI** (The Camouflage).
19
+
20
+ ### 🌪 The Chaos Suite (User Hostility)
21
+
22
+ These components are technically functional but emotionally devastating.
23
+
24
+ | Component | Interaction Model | Technical Chaos |
25
+ | :--------------------- | :----------------- | :------------------------------------------------------------------------------- |
26
+ | **`RotaryDial`** | Sequential Input | Replaces a 10ms text input with a 30s manual rotation per digit. |
27
+ | **`GravityInput`** | Physics-based | Characters use CSS transforms or physics to pool at the bottom of the input box. |
28
+ | **`PasswordPeekhole`** | Obfuscation | Uses a dynamic `clip-path` mask to limit visibility to a tiny, moving radius. |
29
+ | **`ProgressDoom`** | Stochastic Logic | Progress bars that regress or reset based on non-linear, unpredictable math. |
30
+ | **`CatchSubmit`** | Kinetic Avoidance | A button that calculates the cursor trajectory to actively flee from the user. |
31
+ | **`SliderPhone`** | Granular Selection | A single range slider mapping 0 to 10. |
32
+
33
+ ### 🛠 The Standard Suite (The Bait)
34
+
35
+ Standard, accessible components to build your layout before introducing friction:
36
+
37
+ - **Buttons & Inputs:** Styled with Tailwind-compatible utility classes.
38
+ - **Modals & Carousels:** Fully responsive, accessible, and deceptively "normal."
39
+ - **Navigation:** Clean, semantic Svelte components for site-wide structure.
40
+
41
+ ---
42
+
43
+ ## 🚀 Quick Start
44
+
45
+ ### 1. Installation
46
+
47
+ ```bash
48
+ pnpm add @gigo-ui/components
49
+ ```
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
@@ -0,0 +1,144 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ let {
5
+ label = 'Submit',
6
+ onsubmit,
7
+ arenaHeight = 256,
8
+ tauntsArray = [
9
+ 'Too slow!', 'Almost!', 'Nope!', 'Try harder!', 'Not today!',
10
+ '🏃💨', "Can't catch me!", 'Haha!', 'Over here!', 'Nice try!',
11
+ 'So close... not.', 'LOL', 'Git gud', 'Skill issue',
12
+ ],
13
+ class: className,
14
+ ...restProps
15
+ }: {
16
+ label?: string;
17
+ onsubmit?: () => void;
18
+ arenaHeight?: number;
19
+ tauntsArray?: string[];
20
+ class?: string;
21
+ [key: string]: unknown;
22
+ } = $props();
23
+
24
+ let x = $state(0);
25
+ let y = $state(0);
26
+ let escapeCount = $state(0);
27
+ let caught = $state(false);
28
+ let containerEl: HTMLDivElement | undefined = $state();
29
+ let btnEl: HTMLButtonElement | undefined = $state();
30
+ let taunts = $state('');
31
+ let trailDots = $state<Array<{ x: number; y: number }>>([]);
32
+
33
+ function flee() {
34
+ if (caught || !containerEl || !btnEl) return;
35
+
36
+ const container = containerEl.getBoundingClientRect();
37
+ const btn = btnEl.getBoundingClientRect();
38
+ const maxX = container.width - btn.width - 8;
39
+ const maxY = container.height - btn.height - 8;
40
+
41
+ // Random position away from current
42
+ let newX: number, newY: number;
43
+ let attempts = 0;
44
+ do {
45
+ newX = Math.random() * maxX;
46
+ newY = Math.random() * maxY;
47
+ attempts++;
48
+ } while (
49
+ Math.abs(newX - x) < 60 &&
50
+ Math.abs(newY - y) < 60 &&
51
+ attempts < 10
52
+ );
53
+
54
+ x = newX;
55
+ y = newY;
56
+ escapeCount++;
57
+ taunts = tauntsArray[Math.floor(Math.random() * tauntsArray.length)];
58
+ trailDots = [...trailDots, { x: newX + 20, y: newY + 10 }].slice(-20);
59
+ }
60
+
61
+ function handleClick() {
62
+ // Only reachable if they somehow click it
63
+ caught = true;
64
+ taunts = 'Fine, you win. 😤';
65
+ onsubmit?.();
66
+ }
67
+
68
+ // Place button randomly on mount
69
+ $effect(() => {
70
+ if (containerEl && btnEl) {
71
+ const container = containerEl.getBoundingClientRect();
72
+ const btn = btnEl.getBoundingClientRect();
73
+ x = Math.random() * (container.width - btn.width - 8);
74
+ y = Math.random() * (container.height - btn.height - 8);
75
+ }
76
+ });
77
+ </script>
78
+
79
+ <div class={cn('space-y-3', className)} {...restProps}>
80
+ <!-- Stats bar -->
81
+ <div class="flex items-center justify-between rounded-lg border border-(--border) bg-(--secondary) px-3 py-1.5 text-xs font-mono">
82
+ <span class="text-(--muted-foreground)">Escape count: <span class="text-gigo-magenta font-bold">{escapeCount}</span></span>
83
+ {#if taunts}
84
+ <span class="text-gigo-cyan animate-pulse">{taunts}</span>
85
+ {/if}
86
+ <span class="text-(--muted-foreground)">
87
+ Status: {caught ? '✅ Caught' : '🏃 Fleeing'}
88
+ </span>
89
+ </div>
90
+
91
+ <!-- Chase arena -->
92
+ <div
93
+ bind:this={containerEl}
94
+ class="relative w-full rounded-2xl border border-(--border) bg-(--card) overflow-hidden"
95
+ style:height="{arenaHeight}px"
96
+ >
97
+ <!-- Grid background -->
98
+ <div class="absolute inset-0 opacity-[0.03]" style="background-image: linear-gradient(var(--muted-foreground) 1px, transparent 1px), linear-gradient(90deg, var(--muted-foreground) 1px, transparent 1px); background-size: 24px 24px;"></div>
99
+
100
+ <!-- Trail dots -->
101
+ {#if trailDots.length > 0}
102
+ <div class="absolute inset-0 pointer-events-none overflow-hidden">
103
+ {#each trailDots as dot, i}
104
+ <div
105
+ class="absolute w-1.5 h-1.5 rounded-full bg-gigo-magenta/20"
106
+ style:left="{dot.x}px"
107
+ style:top="{dot.y}px"
108
+ style:opacity={0.1 + (i / trailDots.length) * 0.4}
109
+ ></div>
110
+ {/each}
111
+ </div>
112
+ {/if}
113
+
114
+ <!-- The elusive button -->
115
+ <button
116
+ bind:this={btnEl}
117
+ class="absolute px-6 py-2.5 rounded-xl font-mono text-sm font-bold
118
+ transition-all duration-150 ease-out select-none cursor-pointer"
119
+ style:left="{x}px"
120
+ style:top="{y}px"
121
+ style:background={caught ? 'rgba(118, 255, 3, 0.2)' : 'rgba(224, 64, 251, 0.15)'}
122
+ style:border="1px solid {caught ? '#76ff03' : '#e040fb'}"
123
+ style:color={caught ? '#76ff03' : '#e040fb'}
124
+ style:box-shadow={caught ? '0 0 20px rgba(118, 255, 3, 0.3)' : '0 0 12px rgba(224, 64, 251, 0.2)'}
125
+ onpointerenter={caught ? undefined : flee}
126
+ onclick={handleClick}
127
+ disabled={caught}
128
+ >
129
+ {caught ? '✓ Submitted!' : label}
130
+ </button>
131
+
132
+ {#if !caught && escapeCount > 5}
133
+ <div class="absolute bottom-3 left-0 right-0 text-center">
134
+ <span class="text-[10px] font-mono text-(--muted-foreground) opacity-50">
135
+ Hint: Try using Tab key 😏
136
+ </span>
137
+ </div>
138
+ {/if}
139
+ </div>
140
+
141
+ <p class="text-[10px] font-mono text-(--muted-foreground) text-center">
142
+ Hover over the button to submit. Should be easy... right?
143
+ </p>
144
+ </div>
@@ -0,0 +1,11 @@
1
+ type $$ComponentProps = {
2
+ label?: string;
3
+ onsubmit?: () => void;
4
+ arenaHeight?: number;
5
+ tauntsArray?: string[];
6
+ class?: string;
7
+ [key: string]: unknown;
8
+ };
9
+ declare const CatchSubmit: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type CatchSubmit = ReturnType<typeof CatchSubmit>;
11
+ export default CatchSubmit;
@@ -0,0 +1,132 @@
1
+ <script lang="ts">
2
+ import { cn, chaosClasses, chaosRandom, randomGarbageText } from '../../utils/cn.js';
3
+ import type { GigoChaosButtonProps } from '../../types/index.js';
4
+
5
+ let {
6
+ label = 'Click me',
7
+ chaos = true,
8
+ chaosLevel = 8,
9
+ educational = false,
10
+ a11yWarning = false,
11
+ onclick,
12
+ class: className,
13
+ ...restProps
14
+ }: GigoChaosButtonProps = $props();
15
+
16
+ let posX = $state(0);
17
+ let posY = $state(0);
18
+ let currentLabel = $state('Click me');
19
+ let requiredClicks = $state(1);
20
+
21
+ $effect(() => { currentLabel = label; });
22
+ let clicksSoFar = $state(0);
23
+ let fakeButtons = $state<Array<{ id: number; x: number; y: number; text: string }>>([]);
24
+ let nextFakeId = $state(0);
25
+
26
+ // Teleport every 2 seconds
27
+ $effect(() => {
28
+ const timer = setInterval(() => {
29
+ posX = (Math.random() - 0.5) * 400;
30
+ posY = (Math.random() - 0.5) * 300;
31
+ }, 2000);
32
+ return () => clearInterval(timer);
33
+ });
34
+
35
+ // Randomize text every 1 second
36
+ $effect(() => {
37
+ const timer = setInterval(() => {
38
+ currentLabel = randomGarbageText();
39
+ }, 1000);
40
+ return () => clearInterval(timer);
41
+ });
42
+
43
+ // Set random click requirement
44
+ $effect(() => {
45
+ requiredClicks = chaosRandom(10) + 1;
46
+ });
47
+
48
+ function spawnFakeButton() {
49
+ const id = nextFakeId++;
50
+ fakeButtons = [
51
+ ...fakeButtons,
52
+ {
53
+ id,
54
+ x: (Math.random() - 0.5) * 600,
55
+ y: (Math.random() - 0.5) * 400,
56
+ text: randomGarbageText()
57
+ }
58
+ ];
59
+
60
+ // Auto-remove after 3s
61
+ setTimeout(() => {
62
+ fakeButtons = fakeButtons.filter((b) => b.id !== id);
63
+ }, 3000);
64
+ }
65
+
66
+ function handleClick(e: MouseEvent) {
67
+ clicksSoFar++;
68
+
69
+ // Spawn a fake button on every click
70
+ spawnFakeButton();
71
+
72
+ if (clicksSoFar < requiredClicks) return;
73
+
74
+ // Success! Reset and invoke callback
75
+ clicksSoFar = 0;
76
+ requiredClicks = chaosRandom(10) + 1;
77
+ onclick?.(e as MouseEvent & { currentTarget: EventTarget & HTMLButtonElement });
78
+ }
79
+
80
+ let buttonClass = $derived(
81
+ cn(
82
+ 'absolute rounded-xl px-6 py-3 text-lg font-bold text-white shadow-lg transition-all duration-300 cursor-pointer',
83
+ 'bg-linear-to-r from-gigo-magenta to-gigo-pink',
84
+ 'hover:shadow-[0_0_30px_rgba(224,64,251,0.5)] hover:scale-110 active:scale-95',
85
+ chaosClasses(chaosLevel),
86
+ className
87
+ )
88
+ );
89
+ </script>
90
+
91
+ <div class="relative h-96 w-full overflow-hidden rounded-2xl border border-(--surface-border-hover) bg-(--surface-0) p-4">
92
+ <!-- Arena background glow -->
93
+ <div class="absolute inset-0 bg-[radial-gradient(circle_at_50%_50%,rgba(224,64,251,0.05),transparent_60%)] pointer-events-none"></div>
94
+ <!-- Grid pattern -->
95
+ <div class="absolute inset-0 opacity-[0.03] pointer-events-none" style="background-image: linear-gradient(var(--surface-3) 1px, transparent 1px), linear-gradient(90deg, var(--surface-3) 1px, transparent 1px); background-size: 40px 40px;"></div>
96
+
97
+ {#if educational}
98
+ <div class="relative z-10 mb-2 rounded-lg bg-gigo-magenta/10 border border-gigo-magenta/20 px-3 py-1.5 text-xs text-gigo-magenta font-mono inline-block">
99
+ Clicks: {clicksSoFar}/{requiredClicks} | Decoys: {fakeButtons.length}
100
+ </div>
101
+ {/if}
102
+
103
+ <!-- Real button -->
104
+ <button
105
+ class={buttonClass}
106
+ style:transform="translate({posX}px, {posY}px)"
107
+ style:transition="transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)"
108
+ onclick={handleClick}
109
+ {...restProps}
110
+ >
111
+ <!-- Pulse ring -->
112
+ <span class="absolute inset-0 rounded-xl animate-gigo-pulse-glow" style="box-shadow: 0 0 15px rgba(224, 64, 251, 0.4);"></span>
113
+ {currentLabel}
114
+ </button>
115
+
116
+ <!-- Fake decoy buttons -->
117
+ {#each fakeButtons as fake (fake.id)}
118
+ <button
119
+ class="absolute rounded-xl bg-(--surface-2) border border-(--border) px-4 py-2 text-sm text-(--muted-foreground) transition-all duration-300 hover:border-(--surface-border-hover) hover:text-(--foreground) animate-gigo-entrance"
120
+ style:transform="translate({fake.x}px, {fake.y}px)"
121
+ onclick={() => spawnFakeButton()}
122
+ >
123
+ {fake.text}
124
+ </button>
125
+ {/each}
126
+
127
+ {#if educational}
128
+ <p class="absolute bottom-3 left-3 right-3 text-xs text-(--muted-foreground) italic font-mono">
129
+ teleports + text mutates + random click threshold + decoy spawns
130
+ </p>
131
+ {/if}
132
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { GigoChaosButtonProps } from '../../types/index.js';
2
+ declare const ChaosButton: import("svelte").Component<GigoChaosButtonProps, {}, "">;
3
+ type ChaosButton = ReturnType<typeof ChaosButton>;
4
+ export default ChaosButton;
@@ -0,0 +1,206 @@
1
+ <script lang="ts">
2
+ import { cn, chaosClasses, chaosRandom, chaosPickOne, randomGarbageText } from '../../utils/cn.js';
3
+ import type { GigoChaosFormProps } from '../../types/index.js';
4
+
5
+ let {
6
+ chaos = true,
7
+ chaosLevel = 8,
8
+ educational = false,
9
+ a11yWarning = false,
10
+ autoSubmit = false,
11
+ autoSubmitInterval = 12000,
12
+ corruptionInterval = 3000,
13
+ shuffleInterval = 4000,
14
+ onsubmit,
15
+ class: className,
16
+ ...restProps
17
+ }: GigoChaosFormProps & {
18
+ autoSubmit?: boolean;
19
+ autoSubmitInterval?: number;
20
+ corruptionInterval?: number;
21
+ shuffleInterval?: number;
22
+ } = $props();
23
+
24
+ interface ChaosField {
25
+ id: string;
26
+ label: string;
27
+ value: string;
28
+ type: string;
29
+ }
30
+
31
+ let fields = $state<ChaosField[]>([
32
+ { id: 'name', label: 'Name', value: '', type: 'text' },
33
+ { id: 'email', label: 'Email', value: '', type: 'email' },
34
+ { id: 'message', label: 'Message', value: '', type: 'text' }
35
+ ]);
36
+
37
+ let submitButtons = $state(1);
38
+ let nestedForms = $state(0);
39
+ let validationMessages = $state<string[]>([]);
40
+ let isSubmitting = $state(false);
41
+ let corruptionLog = $state<string[]>([]);
42
+
43
+ const LYING_VALIDATIONS = [
44
+ '✓ Looks great! (it doesn\'t)',
45
+ '✓ Perfect format! (we didn\'t check)',
46
+ '✓ Field validated! (lie)',
47
+ '✓ Excellent input! (it\'s garbage)',
48
+ '✓ Approved! (by nobody)'
49
+ ];
50
+
51
+ $effect(() => {
52
+ const timer = setInterval(() => {
53
+ if (fields.length === 0) return;
54
+ const idx = chaosRandom(fields.length);
55
+ const field = fields[idx];
56
+ if (field.value.length > 0) {
57
+ const corruption = randomGarbageText();
58
+ const pos = chaosRandom(field.value.length + 1);
59
+ fields[idx] = {
60
+ ...field,
61
+ value: field.value.slice(0, pos) + corruption + field.value.slice(pos)
62
+ };
63
+ corruptionLog = [...corruptionLog.slice(-4), `Corrupted "${field.label}" with "${corruption}"`];
64
+ }
65
+ }, corruptionInterval);
66
+ return () => clearInterval(timer);
67
+ });
68
+
69
+ $effect(() => {
70
+ const timer = setInterval(() => {
71
+ fields = [...fields].sort(() => Math.random() - 0.5);
72
+ }, shuffleInterval);
73
+ return () => clearInterval(timer);
74
+ });
75
+
76
+ // Multiply submit buttons
77
+ $effect(() => {
78
+ const timer = setInterval(() => {
79
+ if (submitButtons < 8) submitButtons++;
80
+ }, 6000);
81
+ return () => clearInterval(timer);
82
+ });
83
+
84
+ // Lying validation on every input change
85
+ function handleInput(fieldId: string, value: string) {
86
+ const idx = fields.findIndex((f) => f.id === fieldId);
87
+ if (idx >= 0) {
88
+ fields[idx] = { ...fields[idx], value };
89
+ }
90
+ validationMessages = fields.map(() => chaosPickOne(LYING_VALIDATIONS));
91
+ }
92
+
93
+ $effect(() => {
94
+ if (!autoSubmit) return;
95
+ const timer = setInterval(() => {
96
+ if (Math.random() > 0.7) {
97
+ handleSubmit();
98
+ }
99
+ }, autoSubmitInterval);
100
+ return () => clearInterval(timer);
101
+ });
102
+
103
+ async function handleSubmit() {
104
+ isSubmitting = true;
105
+ await new Promise((r) => setTimeout(r, 1000));
106
+
107
+ // Spawn nested form
108
+ if (nestedForms < 3) nestedForms++;
109
+
110
+ const data: Record<string, string> = {};
111
+ for (const f of fields) {
112
+ data[f.id] = f.value;
113
+ }
114
+ onsubmit?.(data);
115
+ isSubmitting = false;
116
+ }
117
+
118
+ let formClass = $derived(
119
+ cn(
120
+ 'relative space-y-5 rounded-2xl border border-(--surface-border-hover) bg-(--surface-1) p-6 overflow-hidden',
121
+ chaosClasses(chaosLevel),
122
+ className
123
+ )
124
+ );
125
+ </script>
126
+
127
+ <form
128
+ class={formClass}
129
+ onsubmit={(e) => {
130
+ e.preventDefault();
131
+ handleSubmit();
132
+ }}
133
+ {...restProps}
134
+ >
135
+ <!-- Scanline CRT effect -->
136
+ <div class="gigo-scanlines pointer-events-none absolute inset-0 z-30 opacity-20"></div>
137
+
138
+ <h3 class="relative z-10 text-lg font-bold text-(--foreground)">
139
+ <span class="bg-linear-to-r from-gigo-magenta via-gigo-red to-gigo-pink bg-clip-text text-transparent">Chaos Form</span>
140
+ </h3>
141
+
142
+ {#each fields as field, i (field.id)}
143
+ <div class="relative z-10 space-y-2 animate-gigo-entrance" style:animation-delay="{i * 0.05}s">
144
+ <label for={field.id} class="text-sm font-medium text-(--foreground)">
145
+ {field.label}
146
+ </label>
147
+ <input
148
+ id={field.id}
149
+ type={field.type}
150
+ value={field.value}
151
+ oninput={(e) => handleInput(field.id, (e.target as HTMLInputElement).value)}
152
+ class="flex h-10 w-full rounded-lg border border-transparent bg-(--secondary) px-4 py-2 text-sm text-(--foreground) transition-all duration-300 placeholder:text-(--muted-foreground) focus:outline-none focus:border-gigo-magenta focus:shadow-[0_0_15px_rgba(224,64,251,0.15)]"
153
+ />
154
+ {#if validationMessages[i]}
155
+ <p class="text-xs text-gigo-lime font-mono animate-gigo-entrance">{validationMessages[i]}</p>
156
+ {/if}
157
+ </div>
158
+ {/each}
159
+
160
+ <!-- Multiple submit buttons that multiply over time -->
161
+ <div class="relative z-10 flex flex-wrap gap-2">
162
+ {#each Array(submitButtons) as _, i}
163
+ <button
164
+ type="submit"
165
+ disabled={isSubmitting}
166
+ class={cn(
167
+ 'rounded-xl px-4 py-2 text-sm font-medium text-white transition-all duration-200 hover:scale-[1.03] active:scale-[0.97] disabled:opacity-40 animate-gigo-entrance',
168
+ i === 0
169
+ ? 'bg-linear-to-r from-gigo-magenta to-gigo-pink hover:shadow-[0_0_20px_rgba(224,64,251,0.4)]'
170
+ : 'bg-(--surface-2) text-(--muted-foreground) border border-(--border)'
171
+ )}
172
+ style:animation-delay="{i * 0.08}s"
173
+ >
174
+ {i === 0 ? 'Submit' : randomGarbageText()}
175
+ </button>
176
+ {/each}
177
+ </div>
178
+
179
+ <!-- Corruption log -->
180
+ {#if educational && corruptionLog.length > 0}
181
+ <div class="relative z-10 rounded-xl bg-gigo-red/5 border border-gigo-red/20 p-4 text-xs font-mono">
182
+ <p class="font-bold text-gigo-red mb-2">Corruption Log:</p>
183
+ {#each corruptionLog as log}
184
+ <p class="text-gigo-red/80 animate-gigo-entrance">{log}</p>
185
+ {/each}
186
+ </div>
187
+ {/if}
188
+
189
+ <!-- Nested spawned forms -->
190
+ {#each Array(nestedForms) as _, i}
191
+ <div class="relative z-10 ml-4 rounded-xl border border-dashed border-gigo-magenta/30 bg-gigo-magenta/5 p-4 animate-gigo-entrance" style:animation-delay="{i * 0.1}s">
192
+ <p class="text-xs font-bold text-gigo-magenta">Spawned Form #{i + 1}</p>
193
+ <input
194
+ type="text"
195
+ placeholder="More data to corrupt..."
196
+ class="mt-2 flex h-9 w-full rounded-lg bg-(--secondary) border border-transparent px-3 text-xs text-(--foreground) focus:outline-none focus:border-gigo-magenta"
197
+ />
198
+ </div>
199
+ {/each}
200
+
201
+ {#if educational}
202
+ <p class="relative z-10 text-xs text-(--muted-foreground) italic font-mono">
203
+ corrupts + shuffles + multiplies + lies + auto-submits + spawns
204
+ </p>
205
+ {/if}
206
+ </form>
@@ -0,0 +1,10 @@
1
+ import type { GigoChaosFormProps } from '../../types/index.js';
2
+ type $$ComponentProps = GigoChaosFormProps & {
3
+ autoSubmit?: boolean;
4
+ autoSubmitInterval?: number;
5
+ corruptionInterval?: number;
6
+ shuffleInterval?: number;
7
+ };
8
+ declare const ChaosForm: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type ChaosForm = ReturnType<typeof ChaosForm>;
10
+ export default ChaosForm;