Skip to content

Commit c000a30

Browse files
authored
Merge pull request #22 from svelte-atoms/feat-n-fixes
bunch of feats & fixes
2 parents 93d6d74 + 2c56b7c commit c000a30

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3312
-239
lines changed

README.md

Lines changed: 213 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Our comprehensive collection of UI components with implementation status:
7575
| **Switch** | Toggle controls ||
7676
| [**Textarea**](docs/components/textarea.md) | Multi-line text inputs ||
7777
| [**Form**](docs/components/form.md) | Form validation and state management ||
78-
| **DatePicker** | Date selection component | |
78+
| **DatePicker** | Date selection component | |
7979
| **TimePicker** | Time selection component ||
8080
| **FileUpload** | File upload component ||
8181
| **ColorPicker** | Color selection component ||
@@ -99,7 +99,8 @@ Our comprehensive collection of UI components with implementation status:
9999
| **Progress** | Progress indicators ||
100100
| **Skeleton** | Loading placeholders ||
101101
| **Timeline** | Event timeline display ||
102-
| **Calendar** | Date display component ||
102+
| **Calendar** | Date display component ||
103+
| **QRCode** | QR code generator ||
103104

104105
### Overlays & Feedback
105106

@@ -351,6 +352,216 @@ This example demonstrates the power of component composition by combining `Dropd
351352
- **Smooth Animations**: Using Svelte's `flip` animation for seamless list transitions
352353
- **Multi-Select State**: Managing complex selection state through the Bond pattern
353354

355+
### Creating Custom Variants
356+
357+
@svelte-atoms/core provides a powerful variant system using `defineVariants()` that allows you to create type-safe, reusable component variations with support for compound variants, defaults, and bond state integration.
358+
359+
#### Basic Variant Definition
360+
361+
```typescript
362+
import { defineVariants, type VariantPropsType } from '@svelte-atoms/core/utils';
363+
364+
const buttonVariants = defineVariants({
365+
class: 'inline-flex items-center justify-center rounded-md font-medium transition-colors',
366+
variants: {
367+
variant: {
368+
primary: 'bg-blue-500 text-white hover:bg-blue-600',
369+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
370+
ghost: 'hover:bg-gray-100'
371+
},
372+
size: {
373+
sm: 'h-8 px-3 text-sm',
374+
md: 'h-10 px-4',
375+
lg: 'h-12 px-6 text-lg'
376+
}
377+
},
378+
compounds: [
379+
{
380+
variant: 'primary',
381+
size: 'lg',
382+
class: 'shadow-md font-semibold'
383+
}
384+
],
385+
defaults: {
386+
variant: 'primary',
387+
size: 'md'
388+
}
389+
});
390+
391+
// Extract type-safe props
392+
type ButtonVariantProps = VariantPropsType<typeof buttonVariants>;
393+
```
394+
395+
#### Local vs Global Variants
396+
397+
**Local Variants** - Define variants directly in your component:
398+
399+
```svelte
400+
<script lang="ts">
401+
import { HtmlAtom } from '@svelte-atoms/core';
402+
import { defineVariants, type VariantPropsType } from '@svelte-atoms/core/utils';
403+
404+
const buttonVariants = defineVariants({
405+
class: 'rounded-md font-medium',
406+
variants: {
407+
variant: {
408+
primary: 'bg-blue-500 text-white',
409+
secondary: 'bg-gray-500 text-white'
410+
},
411+
size: {
412+
sm: 'px-2 py-1 text-sm',
413+
md: 'px-4 py-2 text-base'
414+
}
415+
},
416+
defaults: {
417+
variant: 'primary',
418+
size: 'md'
419+
}
420+
});
421+
422+
type ButtonProps = VariantPropsType<typeof buttonVariants> & {
423+
disabled?: boolean;
424+
class?: string;
425+
};
426+
427+
let { variant, size, disabled = false, class: klass = '', ...props }: ButtonProps = $props();
428+
429+
const variantProps = $derived(buttonVariants(null, { variant, size }));
430+
</script>
431+
432+
<HtmlAtom
433+
as="button"
434+
variants={variantProps}
435+
{disabled}
436+
class={[variantProps.class, klass]}
437+
{...props}
438+
>
439+
{@render children?.()}
440+
</HtmlAtom>
441+
```
442+
443+
**Global Variants** - Define variants in your theme/preset configuration:
444+
445+
```typescript
446+
// +layout.svelte or theme configuration
447+
import { setPreset } from '@svelte-atoms/core/context';
448+
449+
setPreset({
450+
button: () => ({
451+
class: 'inline-flex items-center justify-center rounded-md font-medium transition-colors',
452+
variants: {
453+
variant: {
454+
default: {
455+
class: 'bg-primary text-primary-foreground hover:bg-primary/90'
456+
},
457+
destructive: {
458+
class: 'bg-destructive text-destructive-foreground hover:bg-destructive/90'
459+
},
460+
outline: {
461+
class: 'border border-input bg-background hover:bg-accent'
462+
}
463+
},
464+
size: {
465+
default: 'h-10 px-4 py-2',
466+
sm: 'h-9 px-3',
467+
lg: 'h-11 px-8'
468+
}
469+
},
470+
compounds: [
471+
{
472+
variant: 'default',
473+
size: 'lg',
474+
class: 'text-base font-semibold'
475+
}
476+
],
477+
defaults: {
478+
variant: 'default',
479+
size: 'default'
480+
}
481+
})
482+
});
483+
```
484+
485+
#### Extending Global Variants
486+
487+
Combine global presets with local extensions:
488+
489+
```svelte
490+
<script lang="ts">
491+
import { HtmlAtom } from '@svelte-atoms/core';
492+
import { defineVariants } from '@svelte-atoms/core/utils';
493+
494+
// Extend preset variants with local additions
495+
const extendedVariants = defineVariants({
496+
variants: {
497+
variant: {
498+
// Add new variants not in preset
499+
gradient: {
500+
class: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white'
501+
},
502+
neon: {
503+
class: 'bg-black text-green-400 border-2 border-green-400'
504+
}
505+
},
506+
// Add new variant dimension
507+
animated: {
508+
true: 'animate-pulse',
509+
false: ''
510+
}
511+
},
512+
defaults: {
513+
animated: false
514+
}
515+
});
516+
517+
let { variant, size, animated, ...props } = $props();
518+
</script>
519+
520+
<HtmlAtom
521+
preset="button"
522+
variants={extendedVariants}
523+
as="button"
524+
{variant}
525+
{size}
526+
{animated}
527+
{...props}
528+
>
529+
{@render children?.()}
530+
</HtmlAtom>
531+
```
532+
533+
#### Bond-Reactive Variants
534+
535+
Variants can react to component state through the Bond pattern:
536+
537+
```typescript
538+
const accordionVariants = defineVariants({
539+
class: 'border rounded-md transition-all',
540+
variants: {
541+
state: {
542+
open: (bond) => ({
543+
class: bond?.state?.isOpen ? 'bg-blue-50 border-blue-200' : 'bg-white',
544+
'aria-expanded': bond?.state?.isOpen,
545+
'data-state': bond?.state?.isOpen ? 'open' : 'closed'
546+
})
547+
}
548+
}
549+
});
550+
551+
// Usage with bond
552+
const bond = AccordionBond.get();
553+
const variantProps = $derived(accordionVariants(bond, { state: 'open' }));
554+
```
555+
556+
**Variant Features:**
557+
558+
-**Type Safety** - Automatic TypeScript inference
559+
-**Compound Variants** - Apply styles when multiple conditions match
560+
-**Default Values** - Specify fallback variant values
561+
-**Bond Integration** - Access component state for reactive styling
562+
-**Return Attributes** - Not just classes, any HTML attributes
563+
-**Extensible** - Combine global presets with local variants
564+
354565
---
355566

356567
## 📖 Documentation

bun.lock

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
{
22
"lockfileVersion": 1,
3+
"configVersion": 0,
34
"workspaces": {
45
"": {
56
"name": "@svelte-atoms/core",
67
"dependencies": {
78
"@floating-ui/dom": "^1.7.0",
89
"clsx": "^2.1.1",
10+
"date-fns": "^4.1.0",
911
"es-toolkit": "^1.37.2",
1012
"gsap": "^3.13.0",
1113
"motion": "^12.23.22",
1214
"nanoid": "^5.1.5",
1315
"tailwind-merge": "^3.2.0",
16+
"uqr": "^0.1.2",
1417
},
1518
"devDependencies": {
1619
"@chromatic-com/storybook": "^4.1.1",
@@ -455,6 +458,8 @@
455458

456459
"csstype": ["[email protected]", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
457460

461+
"date-fns": ["[email protected]", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
462+
458463
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
459464

460465
"dedent": ["[email protected]", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ=="],
@@ -593,7 +598,7 @@
593598

594599
"jiti": ["[email protected]", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
595600

596-
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
601+
"js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
597602

598603
"js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
599604

@@ -899,6 +904,8 @@
899904

900905
"unplugin": ["[email protected]", "", { "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" } }, "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w=="],
901906

907+
"uqr": ["[email protected]", "", {}, "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA=="],
908+
902909
"uri-js": ["[email protected]", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
903910

904911
"util-deprecate": ["[email protected]", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
@@ -943,6 +950,8 @@
943950

944951
"zwitch": ["[email protected]", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
945952

953+
"@babel/code-frame/js-tokens": ["[email protected]", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
954+
946955
"@eslint-community/eslint-utils/eslint-visitor-keys": ["[email protected]", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
947956

948957
"@eslint/eslintrc/globals": ["[email protected]", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
@@ -989,8 +998,6 @@
989998

990999
"rollup/fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
9911000

992-
"strip-literal/js-tokens": ["[email protected]", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
993-
9941001
"svelte/esrap": ["[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="],
9951002

9961003
"svelte-ast-print/esrap": ["[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1" } }, "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw=="],

0 commit comments

Comments
 (0)