@fluid-theme/core 0.4.3

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 (48) hide show
  1. package/AGENTS.md +32 -0
  2. package/CHANGELOG.md +159 -0
  3. package/CLAUDE.md +104 -0
  4. package/GEMINI.md +48 -0
  5. package/LICENSE +21 -0
  6. package/README.md +253 -0
  7. package/dist/fluid.min.css +2 -0
  8. package/fluid-agent-kit/README.md +57 -0
  9. package/fluid-agent-kit/skills/accessibility.md +41 -0
  10. package/fluid-agent-kit/skills/css-conventions.md +51 -0
  11. package/fluid-agent-kit/skills/image-optimization.md +115 -0
  12. package/fluid-agent-kit/skills/ux-rules.md +43 -0
  13. package/package.json +68 -0
  14. package/src/animations/scroll-reveal.css +224 -0
  15. package/src/components/accordion.css +250 -0
  16. package/src/components/alert.css +453 -0
  17. package/src/components/avatar.css +192 -0
  18. package/src/components/badge.css +182 -0
  19. package/src/components/button.css +387 -0
  20. package/src/components/card.css +650 -0
  21. package/src/components/chip.css +143 -0
  22. package/src/components/dropdown.css +335 -0
  23. package/src/components/empty-state.css +105 -0
  24. package/src/components/form.css +508 -0
  25. package/src/components/hero.css +293 -0
  26. package/src/components/indicator.css +455 -0
  27. package/src/components/loading.css +274 -0
  28. package/src/components/modal.css +364 -0
  29. package/src/components/nav.css +726 -0
  30. package/src/components/scroll-progress.css +104 -0
  31. package/src/components/search-input.css +125 -0
  32. package/src/components/settings-panel.css +603 -0
  33. package/src/components/step-indicator.css +188 -0
  34. package/src/components/table.css +350 -0
  35. package/src/components/themed-images.css +229 -0
  36. package/src/components/tooltip.css +247 -0
  37. package/src/components/version-badge.css +70 -0
  38. package/src/core/base.css +884 -0
  39. package/src/core/reset.css +159 -0
  40. package/src/core/tokens.css +2416 -0
  41. package/src/fluid.css +242 -0
  42. package/src/layouts/positioning.css +626 -0
  43. package/src/sections/cta.css +210 -0
  44. package/src/sections/features.css +356 -0
  45. package/src/sections/footer.css +283 -0
  46. package/src/sections/stats.css +234 -0
  47. package/src/sections/team.css +181 -0
  48. package/src/sections/testimonials.css +251 -0
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Fluid Framework - Button Component
3
+ * @layer components
4
+ *
5
+ * Buttons should invite interaction, not demand it.
6
+ * Clear affordance, gentle feedback, natural transitions.
7
+ *
8
+ * Usage:
9
+ * <button>Default</button>
10
+ * <button data-variant="secondary">Secondary</button>
11
+ * <button data-variant="ghost">Ghost</button>
12
+ * <button data-variant="danger">Danger</button>
13
+ * <button data-size="sm">Small</button>
14
+ * <button data-size="lg">Large</button>
15
+ */
16
+
17
+ @layer components {
18
+ /* Base button - the foundation */
19
+ button,
20
+ [role="button"],
21
+ .button {
22
+ /* Reset */
23
+ appearance: none;
24
+ border: none;
25
+ background: none;
26
+
27
+ /* Layout */
28
+ display: inline-flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ gap: var(--space-2);
32
+
33
+ /* Sizing */
34
+ padding-block: var(--_btn-padding-block, var(--space-3));
35
+ padding-inline: var(--_btn-padding-inline, var(--space-5));
36
+ min-block-size: var(--_btn-min-height, 2.75rem);
37
+
38
+ /* Typography */
39
+ font-family: inherit;
40
+ font-size: var(--_btn-font-size, var(--font-size-base));
41
+ font-weight: var(--weight-medium);
42
+ line-height: var(--leading-none);
43
+ text-decoration: none;
44
+ white-space: nowrap;
45
+
46
+ /* Visual */
47
+ color: var(--_btn-color, var(--white));
48
+ background-color: var(--_btn-bg, var(--accent));
49
+ border: var(--border-width) solid var(--_btn-border, transparent);
50
+ border-radius: var(--radius-button, var(--radius-md));
51
+ box-shadow: var(--_btn-shadow, var(--shadow-xs)); /* Subtle depth at rest */
52
+ cursor: pointer;
53
+
54
+ /* Transitions - smooth like flowing water */
55
+ transition:
56
+ background-color var(--duration-normal) var(--ease-fluid),
57
+ border-color var(--duration-normal) var(--ease-fluid),
58
+ color var(--duration-normal) var(--ease-fluid),
59
+ box-shadow var(--duration-normal) var(--ease-fluid),
60
+ transform var(--duration-fast) var(--ease-settle);
61
+ }
62
+
63
+ /* Hover state - gentle lift */
64
+ button:hover:not(:disabled),
65
+ [role="button"]:hover:not(:disabled),
66
+ .button:hover:not(:disabled) {
67
+ background-color: var(--_btn-bg-hover, var(--accent-hover));
68
+ box-shadow: var(--shadow-sm);
69
+ }
70
+
71
+ /* Active state - subtle press */
72
+ button:active:not(:disabled),
73
+ [role="button"]:active:not(:disabled),
74
+ .button:active:not(:disabled) {
75
+ transform: scale(0.98);
76
+ box-shadow: none;
77
+ }
78
+
79
+ /* Focus state - clear ring */
80
+ button:focus-visible,
81
+ [role="button"]:focus-visible,
82
+ .button:focus-visible {
83
+ outline: 2px solid transparent;
84
+ box-shadow:
85
+ var(--shadow-glow),
86
+ 0 0 0 2px var(--color-background),
87
+ 0 0 0 4px var(--accent);
88
+ }
89
+
90
+ /* Disabled state */
91
+ button:disabled,
92
+ [role="button"][aria-disabled="true"],
93
+ .button:disabled {
94
+ opacity: 0.5;
95
+ cursor: not-allowed;
96
+ pointer-events: none;
97
+ }
98
+
99
+
100
+ /* ========================================
101
+ VARIANTS
102
+ Each with its own personality
103
+ ======================================== */
104
+
105
+ /* Secondary - understated, supportive */
106
+ button[data-variant="secondary"],
107
+ .button[data-variant="secondary"] {
108
+ --_btn-color: var(--color-text);
109
+ --_btn-bg: var(--color-surface);
110
+ --_btn-bg-hover: var(--color-surface-raised);
111
+ --_btn-border: var(--color-border);
112
+ }
113
+
114
+ button[data-variant="secondary"]:hover:not(:disabled),
115
+ .button[data-variant="secondary"]:hover:not(:disabled) {
116
+ --_btn-border: var(--color-text-subtle);
117
+ }
118
+
119
+ /* Ghost - minimal, text-like */
120
+ button[data-variant="ghost"],
121
+ .button[data-variant="ghost"] {
122
+ --_btn-color: var(--color-text);
123
+ --_btn-bg: transparent;
124
+ --_btn-bg-hover: var(--color-surface);
125
+ --_btn-border: transparent;
126
+ --_btn-shadow: none; /* No shadow for ghost buttons */
127
+ }
128
+
129
+ /* Outline - bordered, no fill */
130
+ button[data-variant="outline"],
131
+ .button[data-variant="outline"] {
132
+ --_btn-color: var(--accent);
133
+ --_btn-bg: transparent;
134
+ --_btn-bg-hover: var(--accent-subtle);
135
+ --_btn-border: var(--accent);
136
+ --_btn-shadow: none; /* Border provides visual boundary */
137
+ }
138
+
139
+ /* Danger - for destructive actions */
140
+ button[data-variant="danger"],
141
+ .button[data-variant="danger"] {
142
+ --_btn-color: var(--white);
143
+ --_btn-bg: var(--color-error);
144
+ --_btn-bg-hover: color-mix(in oklch, var(--color-error) 85%, var(--black));
145
+ }
146
+
147
+ /* Subtle danger - less alarming */
148
+ button[data-variant="danger-subtle"],
149
+ .button[data-variant="danger-subtle"] {
150
+ --_btn-color: var(--color-error);
151
+ --_btn-bg: var(--color-error-subtle);
152
+ --_btn-bg-hover: color-mix(in oklch, var(--color-error-subtle) 80%, var(--color-error));
153
+ }
154
+
155
+
156
+ /* ========================================
157
+ SIZES
158
+ Small for tight spaces, large for emphasis
159
+ ======================================== */
160
+
161
+ /* Extra small */
162
+ button[data-size="xs"],
163
+ .button[data-size="xs"] {
164
+ --_btn-padding-block: var(--space-1);
165
+ --_btn-padding-inline: var(--space-2);
166
+ --_btn-font-size: var(--font-size-xs);
167
+ --_btn-min-height: 1.75rem;
168
+ border-radius: var(--radius-sm);
169
+ }
170
+
171
+ /* Small */
172
+ button[data-size="sm"],
173
+ .button[data-size="sm"] {
174
+ --_btn-padding-block: var(--space-2);
175
+ --_btn-padding-inline: var(--space-4);
176
+ --_btn-font-size: var(--font-size-sm);
177
+ --_btn-min-height: 2.25rem;
178
+ }
179
+
180
+ /* Large */
181
+ button[data-size="lg"],
182
+ .button[data-size="lg"] {
183
+ --_btn-padding-block: var(--space-4);
184
+ --_btn-padding-inline: var(--space-6);
185
+ --_btn-font-size: var(--font-size-lg);
186
+ --_btn-min-height: 3.25rem;
187
+ border-radius: var(--radius-lg);
188
+ }
189
+
190
+ /* Extra large */
191
+ button[data-size="xl"],
192
+ .button[data-size="xl"] {
193
+ --_btn-padding-block: var(--space-5);
194
+ --_btn-padding-inline: var(--space-8);
195
+ --_btn-font-size: var(--font-size-xl);
196
+ --_btn-min-height: 4rem;
197
+ border-radius: var(--radius-lg);
198
+ }
199
+
200
+
201
+ /* ========================================
202
+ MODIFIERS
203
+ Additional behavioral variations
204
+ ======================================== */
205
+
206
+ /* Full width */
207
+ button[data-full],
208
+ .button[data-full] {
209
+ inline-size: 100%;
210
+ }
211
+
212
+ /* Icon only - square aspect */
213
+ button[data-icon],
214
+ .button[data-icon] {
215
+ --_btn-padding-inline: var(--_btn-padding-block);
216
+ aspect-ratio: 1;
217
+ }
218
+
219
+ /* Pill shape */
220
+ button[data-pill],
221
+ .button[data-pill] {
222
+ border-radius: var(--radius-full);
223
+ }
224
+
225
+ /* Loading state */
226
+ button[data-loading],
227
+ .button[data-loading] {
228
+ position: relative;
229
+ color: transparent;
230
+ pointer-events: none;
231
+ }
232
+
233
+ button[data-loading]::after,
234
+ .button[data-loading]::after {
235
+ content: "";
236
+ position: absolute;
237
+ inset-inline-start: 50%;
238
+ inset-block-start: 50%;
239
+ inline-size: 1.25em;
240
+ block-size: 1.25em;
241
+ margin-inline-start: -0.625em;
242
+ margin-block-start: -0.625em;
243
+ border: 2px solid currentColor;
244
+ border-inline-end-color: transparent;
245
+ border-radius: var(--radius-full);
246
+ animation: button-spin 0.6s linear infinite;
247
+ color: var(--_btn-color, var(--white));
248
+ opacity: 0.8;
249
+ }
250
+
251
+ @keyframes button-spin {
252
+ to {
253
+ transform: rotate(360deg);
254
+ }
255
+ }
256
+
257
+
258
+ /* ========================================
259
+ TSUNAMI MOTION EFFECTS
260
+ Bold, wave-like interactions
261
+ Uses transform only to avoid CLS
262
+ ======================================== */
263
+
264
+ /* Tsunami button hover - noticeable growth like a wave rising */
265
+ [data-motion="tsunami"] button:hover:not(:disabled),
266
+ [data-motion="tsunami"] [role="button"]:hover:not(:disabled),
267
+ [data-motion="tsunami"] .button:hover:not(:disabled) {
268
+ transform: scale(1.08);
269
+ box-shadow: var(--shadow-lg);
270
+ transition:
271
+ transform var(--duration-normal) var(--ease-fluid),
272
+ box-shadow var(--duration-normal) var(--ease-fluid),
273
+ background-color var(--duration-normal) var(--ease-fluid);
274
+ }
275
+
276
+ /* Tsunami button active/click - crash down effect */
277
+ [data-motion="tsunami"] button:active:not(:disabled),
278
+ [data-motion="tsunami"] [role="button"]:active:not(:disabled),
279
+ [data-motion="tsunami"] .button:active:not(:disabled) {
280
+ transform: scale(0.92) translateY(4px);
281
+ box-shadow: var(--shadow-xs);
282
+ transition:
283
+ transform 150ms var(--ease-settle),
284
+ box-shadow 150ms var(--ease-settle);
285
+ }
286
+
287
+ /* Tsunami button leave - wave crash with bounce settle */
288
+ @keyframes tsunami-button-settle {
289
+ 0% { transform: scale(1.08); }
290
+ 40% { transform: scale(0.95) translateY(3px); }
291
+ 70% { transform: scale(1.02) translateY(-1px); }
292
+ 100% { transform: scale(1) translateY(0); }
293
+ }
294
+
295
+ [data-motion="tsunami"] button:not(:hover):not(:active):not(:disabled),
296
+ [data-motion="tsunami"] [role="button"]:not(:hover):not(:active):not([aria-disabled="true"]),
297
+ [data-motion="tsunami"] .button:not(:hover):not(:active):not(:disabled) {
298
+ animation: tsunami-button-settle var(--duration-normal) var(--ease-settle);
299
+ }
300
+
301
+
302
+ /* ========================================
303
+ BUTTON GROUPS
304
+ Multiple buttons together
305
+ ======================================== */
306
+
307
+ .button-group {
308
+ display: inline-flex;
309
+ isolation: isolate;
310
+ }
311
+
312
+ .button-group > button,
313
+ .button-group > .button {
314
+ border-radius: 0;
315
+ position: relative;
316
+ }
317
+
318
+ .button-group > button:first-child,
319
+ .button-group > .button:first-child {
320
+ border-start-start-radius: var(--radius-md);
321
+ border-end-start-radius: var(--radius-md);
322
+ }
323
+
324
+ .button-group > button:last-child,
325
+ .button-group > .button:last-child {
326
+ border-start-end-radius: var(--radius-md);
327
+ border-end-end-radius: var(--radius-md);
328
+ }
329
+
330
+ .button-group > button:not(:first-child),
331
+ .button-group > .button:not(:first-child) {
332
+ margin-inline-start: -1px;
333
+ }
334
+
335
+ .button-group > button:hover,
336
+ .button-group > button:focus-visible,
337
+ .button-group > .button:hover,
338
+ .button-group > .button:focus-visible {
339
+ z-index: 1;
340
+ }
341
+
342
+
343
+ /* ========================================
344
+ REDUCED MOTION
345
+ ======================================== */
346
+
347
+ @media (prefers-reduced-motion: reduce) {
348
+ button,
349
+ [role="button"],
350
+ .button {
351
+ transition: none;
352
+ }
353
+
354
+ button:active:not(:disabled),
355
+ [role="button"]:active:not(:disabled),
356
+ .button:active:not(:disabled) {
357
+ transform: none;
358
+ }
359
+
360
+ button[data-loading]::after,
361
+ .button[data-loading]::after {
362
+ animation: none;
363
+ /* Static indicator instead of spinning */
364
+ opacity: 0.6;
365
+ }
366
+
367
+ [data-motion="tsunami"] button:hover:not(:disabled),
368
+ [data-motion="tsunami"] [role="button"]:hover:not(:disabled),
369
+ [data-motion="tsunami"] .button:hover:not(:disabled) {
370
+ transform: none;
371
+ transition: none;
372
+ }
373
+
374
+ [data-motion="tsunami"] button:active:not(:disabled),
375
+ [data-motion="tsunami"] [role="button"]:active:not(:disabled),
376
+ [data-motion="tsunami"] .button:active:not(:disabled) {
377
+ transform: none;
378
+ transition: none;
379
+ }
380
+
381
+ [data-motion="tsunami"] button:not(:hover):not(:active):not(:disabled),
382
+ [data-motion="tsunami"] [role="button"]:not(:hover):not(:active):not([aria-disabled="true"]),
383
+ [data-motion="tsunami"] .button:not(:hover):not(:active):not(:disabled) {
384
+ animation: none;
385
+ }
386
+ }
387
+ }