create-velocity-astro 1.5.3 → 1.6.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.
@@ -1,154 +1,154 @@
1
- ---
2
- import type { HTMLAttributes } from 'astro/types';
3
- import { cn } from '@/lib/cn';
4
-
5
- interface Props extends HTMLAttributes<'section'> {
6
- /** Layout mode: centered single column or split two-column */
7
- layout?: 'centered' | 'split';
8
- /** Vertical padding size */
9
- size?: 'sm' | 'md' | 'lg' | 'xl';
10
- /** Show background grid pattern */
11
- showGrid?: boolean;
12
- /** Show decorative gradient blob */
13
- showBlob?: boolean;
14
- /** Blob position (only applies when showBlob is true) */
15
- blobPosition?: 'left' | 'right' | 'center';
16
- }
17
-
18
- const {
19
- layout = 'centered',
20
- size = 'lg',
21
- showGrid = false,
22
- showBlob = false,
23
- blobPosition = 'right',
24
- class: className,
25
- ...attrs
26
- } = Astro.props;
27
-
28
- // Check which slots are provided
29
- const hasBadgeSlot = Astro.slots.has('badge');
30
- const hasTitleSlot = Astro.slots.has('title');
31
- const hasDescriptionSlot = Astro.slots.has('description');
32
- const hasActionsSlot = Astro.slots.has('actions');
33
- const hasAsideSlot = Astro.slots.has('aside');
34
-
35
- // Size (padding) variants - asymmetric: more top padding for header clearance
36
- const sizes: Record<NonNullable<Props['size']>, string> = {
37
- sm: 'pt-[var(--space-page-top-sm)] pb-[var(--space-section-sm)]',
38
- md: 'pt-[var(--space-page-top)] pb-[var(--space-section-md)]',
39
- lg: 'pt-[calc(var(--space-page-top)+var(--space-8))] pb-[var(--space-section-lg)]',
40
- xl: 'pt-[calc(var(--space-page-top)+var(--space-16))] pb-[var(--space-section-xl)]',
41
- };
42
-
43
- // Blob position variants
44
- const blobPositions: Record<NonNullable<Props['blobPosition']>, string> = {
45
- left: '-left-20 -top-20',
46
- right: '-right-20 -top-20',
47
- center: 'left-1/2 -translate-x-1/2 -top-20',
48
- };
49
-
50
- // Compute alignment based on layout
51
- const alignment = layout === 'centered' ? 'text-center items-center' : 'text-left items-start';
52
-
53
- // Compute classes
54
- const sectionClasses = cn(
55
- 'relative overflow-hidden bg-background',
56
- sizes[size],
57
- className
58
- );
59
-
60
- const contentClasses = cn(
61
- 'z-10 flex flex-col',
62
- alignment
63
- );
64
-
65
- const gridClasses = cn(
66
- 'mx-auto grid max-w-6xl grid-cols-1 items-center gap-[var(--space-section-gap)] px-6',
67
- layout === 'split' && 'lg:grid-cols-2 lg:gap-[var(--space-section-gap)]'
68
- );
69
- ---
70
-
71
- <section class={sectionClasses} {...attrs}>
72
- {/* Background grid pattern */}
73
- {showGrid && (
74
- <div
75
- class="bg-grid-pattern pointer-events-none absolute inset-0 [mask-image:linear-gradient(to_bottom,white,transparent)] opacity-40"
76
- aria-hidden="true"
77
- />
78
- )}
79
-
80
- <div class={gridClasses}>
81
- {/* Content Column */}
82
- <div class={cn(contentClasses, 'order-1 lg:order-none')}>
83
- {/* Badge Slot */}
84
- {hasBadgeSlot && (
85
- <div class="mb-[var(--space-heading-gap)]">
86
- <slot name="badge" />
87
- </div>
88
- )}
89
-
90
- {/* Title Slot */}
91
- {hasTitleSlot && (
92
- <div class={cn(
93
- 'mb-[var(--space-heading-gap)]',
94
- '[&>h1]:font-display [&>h1]:text-5xl [&>h1]:leading-[1.1] [&>h1]:font-bold [&>h1]:tracking-tight [&>h1]:text-balance [&>h1]:text-foreground md:[&>h1]:text-6xl lg:[&>h1]:text-7xl',
95
- '[&>h2]:font-display [&>h2]:text-4xl [&>h2]:leading-[1.1] [&>h2]:font-bold [&>h2]:tracking-tight [&>h2]:text-balance [&>h2]:text-foreground md:[&>h2]:text-5xl lg:[&>h2]:text-6xl'
96
- )}>
97
- <slot name="title" />
98
- </div>
99
- )}
100
-
101
- {/* Description Slot */}
102
- {hasDescriptionSlot && (
103
- <div class={cn(
104
- 'mb-[var(--space-stack-lg)] max-w-xl text-lg leading-relaxed text-foreground-muted',
105
- '[&>p]:text-lg [&>p]:leading-relaxed [&>p]:text-foreground-muted',
106
- layout === 'centered' && 'mx-auto'
107
- )}>
108
- <slot name="description" />
109
- </div>
110
- )}
111
-
112
- {/* Actions Slot */}
113
- {hasActionsSlot && (
114
- <div class={cn(
115
- 'flex w-full flex-col gap-4 sm:w-auto sm:flex-row',
116
- layout === 'centered' && 'justify-center'
117
- )}>
118
- <slot name="actions" />
119
- </div>
120
- )}
121
-
122
- {/* Default slot for additional content (social proof, etc.) */}
123
- <slot />
124
- </div>
125
-
126
- {/* Aside Column (for split layout) */}
127
- {layout === 'split' && hasAsideSlot && (
128
- <div class="relative z-10 w-full order-2 lg:order-none">
129
- <slot name="aside" />
130
- {/* Decorative blob for split layout */}
131
- {showBlob && (
132
- <div
133
- class={cn(
134
- 'bg-brand-200/20 pointer-events-none absolute h-64 w-64 rounded-full blur-3xl',
135
- blobPositions[blobPosition]
136
- )}
137
- aria-hidden="true"
138
- />
139
- )}
140
- </div>
141
- )}
142
-
143
- {/* Decorative blob for centered layout */}
144
- {layout === 'centered' && showBlob && (
145
- <div
146
- class={cn(
147
- 'bg-brand-200/20 pointer-events-none absolute h-64 w-64 rounded-full blur-3xl',
148
- blobPositions[blobPosition]
149
- )}
150
- aria-hidden="true"
151
- />
152
- )}
153
- </div>
154
- </section>
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types';
3
+ import { cn } from '@/lib/cn';
4
+
5
+ interface Props extends HTMLAttributes<'section'> {
6
+ /** Layout mode: centered single column or split two-column */
7
+ layout?: 'centered' | 'split';
8
+ /** Vertical padding size */
9
+ size?: 'sm' | 'md' | 'lg' | 'xl';
10
+ /** Show background grid pattern */
11
+ showGrid?: boolean;
12
+ /** Show decorative gradient blob */
13
+ showBlob?: boolean;
14
+ /** Blob position (only applies when showBlob is true) */
15
+ blobPosition?: 'left' | 'right' | 'center';
16
+ }
17
+
18
+ const {
19
+ layout = 'centered',
20
+ size = 'lg',
21
+ showGrid = false,
22
+ showBlob = false,
23
+ blobPosition = 'right',
24
+ class: className,
25
+ ...attrs
26
+ } = Astro.props;
27
+
28
+ // Check which slots are provided
29
+ const hasBadgeSlot = Astro.slots.has('badge');
30
+ const hasTitleSlot = Astro.slots.has('title');
31
+ const hasDescriptionSlot = Astro.slots.has('description');
32
+ const hasActionsSlot = Astro.slots.has('actions');
33
+ const hasAsideSlot = Astro.slots.has('aside');
34
+
35
+ // Size (padding) variants - asymmetric: more top padding for header clearance
36
+ const sizes: Record<NonNullable<Props['size']>, string> = {
37
+ sm: 'pt-[var(--space-page-top-sm)] pb-[var(--space-section-sm)]',
38
+ md: 'pt-[var(--space-page-top)] pb-[var(--space-section-md)]',
39
+ lg: 'pt-[calc(var(--space-page-top)+var(--space-8))] pb-[var(--space-section-lg)]',
40
+ xl: 'pt-[calc(var(--space-page-top)+var(--space-16))] pb-[var(--space-section-xl)]',
41
+ };
42
+
43
+ // Blob position variants
44
+ const blobPositions: Record<NonNullable<Props['blobPosition']>, string> = {
45
+ left: '-left-20 -top-20',
46
+ right: '-right-20 -top-20',
47
+ center: 'left-1/2 -translate-x-1/2 -top-20',
48
+ };
49
+
50
+ // Compute alignment based on layout
51
+ const alignment = layout === 'centered' ? 'text-center items-center' : 'text-left items-start';
52
+
53
+ // Compute classes
54
+ const sectionClasses = cn(
55
+ 'relative overflow-hidden bg-background',
56
+ sizes[size],
57
+ className
58
+ );
59
+
60
+ const contentClasses = cn(
61
+ 'z-10 flex flex-col',
62
+ alignment
63
+ );
64
+
65
+ const gridClasses = cn(
66
+ 'mx-auto grid max-w-6xl grid-cols-1 items-center gap-[var(--space-section-gap)] px-6',
67
+ layout === 'split' && 'lg:grid-cols-2 lg:gap-[var(--space-section-gap)]'
68
+ );
69
+ ---
70
+
71
+ <section class={sectionClasses} {...attrs}>
72
+ {/* Background grid pattern */}
73
+ {showGrid && (
74
+ <div
75
+ class="bg-grid-pattern pointer-events-none absolute inset-0 [mask-image:linear-gradient(to_bottom,white,transparent)] opacity-40"
76
+ aria-hidden="true"
77
+ />
78
+ )}
79
+
80
+ <div class={gridClasses}>
81
+ {/* Content Column */}
82
+ <div class={cn(contentClasses, 'order-1 lg:order-none')}>
83
+ {/* Badge Slot */}
84
+ {hasBadgeSlot && (
85
+ <div class="mb-[var(--space-heading-gap)]">
86
+ <slot name="badge" />
87
+ </div>
88
+ )}
89
+
90
+ {/* Title Slot */}
91
+ {hasTitleSlot && (
92
+ <div class={cn(
93
+ 'mb-[var(--space-heading-gap)]',
94
+ '[&>h1]:font-display [&>h1]:text-5xl [&>h1]:leading-[1.1] [&>h1]:font-bold [&>h1]:tracking-tight [&>h1]:text-balance [&>h1]:text-foreground md:[&>h1]:text-6xl lg:[&>h1]:text-7xl',
95
+ '[&>h2]:font-display [&>h2]:text-4xl [&>h2]:leading-[1.1] [&>h2]:font-bold [&>h2]:tracking-tight [&>h2]:text-balance [&>h2]:text-foreground md:[&>h2]:text-5xl lg:[&>h2]:text-6xl'
96
+ )}>
97
+ <slot name="title" />
98
+ </div>
99
+ )}
100
+
101
+ {/* Description Slot */}
102
+ {hasDescriptionSlot && (
103
+ <div class={cn(
104
+ 'mb-[var(--space-stack-lg)] max-w-xl text-lg leading-relaxed text-foreground-muted',
105
+ '[&>p]:text-lg [&>p]:leading-relaxed [&>p]:text-foreground-muted',
106
+ layout === 'centered' && 'mx-auto'
107
+ )}>
108
+ <slot name="description" />
109
+ </div>
110
+ )}
111
+
112
+ {/* Actions Slot */}
113
+ {hasActionsSlot && (
114
+ <div class={cn(
115
+ 'flex w-full flex-col gap-4 sm:w-auto sm:flex-row',
116
+ layout === 'centered' && 'justify-center'
117
+ )}>
118
+ <slot name="actions" />
119
+ </div>
120
+ )}
121
+
122
+ {/* Default slot for additional content (social proof, etc.) */}
123
+ <slot />
124
+ </div>
125
+
126
+ {/* Aside Column (for split layout) */}
127
+ {layout === 'split' && hasAsideSlot && (
128
+ <div class="relative z-10 w-full order-2 lg:order-none">
129
+ <slot name="aside" />
130
+ {/* Decorative blob for split layout */}
131
+ {showBlob && (
132
+ <div
133
+ class={cn(
134
+ 'bg-brand-200/20 pointer-events-none absolute h-64 w-64 rounded-full blur-3xl',
135
+ blobPositions[blobPosition]
136
+ )}
137
+ aria-hidden="true"
138
+ />
139
+ )}
140
+ </div>
141
+ )}
142
+
143
+ {/* Decorative blob for centered layout */}
144
+ {layout === 'centered' && showBlob && (
145
+ <div
146
+ class={cn(
147
+ 'bg-brand-200/20 pointer-events-none absolute h-64 w-64 rounded-full blur-3xl',
148
+ blobPositions[blobPosition]
149
+ )}
150
+ aria-hidden="true"
151
+ />
152
+ )}
153
+ </div>
154
+ </section>