@indirecttek/essentials-engine 1.0.0 → 1.1.1
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.
- package/README.md +397 -0
- package/dist/components/Footer.astro +160 -0
- package/dist/components/Hero.astro +49 -16
- package/dist/components/Navbar.astro +7 -3
- package/dist/components/ServicesGrid.astro +13 -8
- package/dist/index.ts +3 -0
- package/dist/layouts/EssentialsLayout.astro +4 -9
- package/dist/types.ts +16 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# @indirecttek/essentials-engine
|
|
2
|
+
|
|
3
|
+
A **white-label, config-driven Astro component library** for rapidly building beautiful marketing websites for small businesses. Define your content and branding in a single configuration file, and Essentials Engine handles the rest.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@indirecttek/essentials-engine)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- **Config-Driven Architecture** – One configuration file powers your entire site
|
|
13
|
+
- **Zero Client-Side JavaScript** – 100% static output for blazing-fast performance
|
|
14
|
+
- **CSS Variable Theming** – Full control over colors with automatic propagation
|
|
15
|
+
- **Responsive by Default** – Mobile-first design that looks great on all devices
|
|
16
|
+
- **Modern Aesthetics** – Polished shadows, hover effects, and smooth transitions
|
|
17
|
+
- **Flexible Layouts** – Multiple hero styles, sticky navigation, and more
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 📦 Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @indirecttek/essentials-engine
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Peer Dependencies
|
|
28
|
+
|
|
29
|
+
Ensure your project has Astro and Tailwind CSS installed:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install astro tailwindcss
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Compatibility:**
|
|
36
|
+
- Astro 4.x or 5.x
|
|
37
|
+
- Tailwind CSS 3.x or 4.x
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🚀 Quick Start
|
|
42
|
+
|
|
43
|
+
### 1. Create Your Site Configuration
|
|
44
|
+
|
|
45
|
+
Create a configuration file (e.g., `src/config/siteConfig.ts`):
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import type { SiteConfig } from '@indirecttek/essentials-engine';
|
|
49
|
+
|
|
50
|
+
export const siteConfig: SiteConfig = {
|
|
51
|
+
businessName: "Raleigh Pro Landscaping",
|
|
52
|
+
|
|
53
|
+
theme: {
|
|
54
|
+
primary: "#2d5a27", // Forest green
|
|
55
|
+
secondary: "#e8f5e3", // Light sage
|
|
56
|
+
accent: "#f4a460", // Sandy brown
|
|
57
|
+
background: "#ffffff",
|
|
58
|
+
foreground: "#1a1a1a",
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
contactInfo: {
|
|
62
|
+
phone: "(919) 555-0123",
|
|
63
|
+
email: "hello@raleighprolandscaping.com",
|
|
64
|
+
address: "123 Oak Street, Raleigh, NC 27601",
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
heroSection: {
|
|
68
|
+
headline: "Transform Your Outdoor Space",
|
|
69
|
+
subheadline: "Professional landscaping services for homes and businesses in the Triangle area.",
|
|
70
|
+
imageUrl: "/images/hero-garden.jpg",
|
|
71
|
+
imageAlt: "Beautiful landscaped garden with flowers",
|
|
72
|
+
callToActionLabel: "Get a Free Quote",
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
services: [
|
|
76
|
+
{ name: "Lawn Care", description: "Weekly mowing, edging, and seasonal treatments to keep your lawn lush and healthy." },
|
|
77
|
+
{ name: "Garden Design", description: "Custom garden layouts with native plants, flowers, and decorative elements." },
|
|
78
|
+
{ name: "Hardscaping", description: "Patios, walkways, retaining walls, and outdoor living spaces." },
|
|
79
|
+
],
|
|
80
|
+
|
|
81
|
+
seo: {
|
|
82
|
+
title: "Raleigh Pro Landscaping | Professional Lawn & Garden Services",
|
|
83
|
+
description: "Transform your outdoor space with Raleigh's most trusted landscaping company. Lawn care, garden design, and hardscaping.",
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
analytics: {
|
|
87
|
+
enableTracking: false,
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Optional features
|
|
91
|
+
socialLinks: {
|
|
92
|
+
facebook: "https://facebook.com/raleighprolandscaping",
|
|
93
|
+
instagram: "https://instagram.com/raleighprolandscaping",
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
layoutOptions: {
|
|
97
|
+
heroLayout: "image-right", // "image-left" | "image-right" | "full-width"
|
|
98
|
+
stickyNav: true,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 2. Use the Layout in Your Page
|
|
104
|
+
|
|
105
|
+
Create your homepage (`src/pages/index.astro`):
|
|
106
|
+
|
|
107
|
+
```astro
|
|
108
|
+
---
|
|
109
|
+
import { EssentialsLayout } from '@indirecttek/essentials-engine';
|
|
110
|
+
import { siteConfig } from '../config/siteConfig';
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
<EssentialsLayout config={siteConfig} />
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
That's it! You now have a complete, professional marketing website.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 🧩 Components
|
|
121
|
+
|
|
122
|
+
Essentials Engine provides both a full-page layout and individual components for flexibility.
|
|
123
|
+
|
|
124
|
+
### Full-Page Layout
|
|
125
|
+
|
|
126
|
+
```astro
|
|
127
|
+
---
|
|
128
|
+
import { EssentialsLayout } from '@indirecttek/essentials-engine';
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
<EssentialsLayout config={siteConfig} />
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Renders a complete page with: `Navbar` → `Hero` → `ServicesGrid` → `ContactForm` → `Footer`
|
|
135
|
+
|
|
136
|
+
### Individual Components
|
|
137
|
+
|
|
138
|
+
Use components individually for custom page structures:
|
|
139
|
+
|
|
140
|
+
```astro
|
|
141
|
+
---
|
|
142
|
+
import { Navbar, Hero, ServicesGrid, ContactForm, Footer } from '@indirecttek/essentials-engine';
|
|
143
|
+
import { siteConfig } from '../config/siteConfig';
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
<html lang="en">
|
|
147
|
+
<body>
|
|
148
|
+
<Navbar config={siteConfig} />
|
|
149
|
+
<Hero config={siteConfig} />
|
|
150
|
+
|
|
151
|
+
<!-- Your custom content here -->
|
|
152
|
+
<section class="py-16">
|
|
153
|
+
<h2>Why Choose Us?</h2>
|
|
154
|
+
<!-- ... -->
|
|
155
|
+
</section>
|
|
156
|
+
|
|
157
|
+
<ServicesGrid config={siteConfig} />
|
|
158
|
+
<ContactForm config={siteConfig} />
|
|
159
|
+
<Footer config={siteConfig} />
|
|
160
|
+
</body>
|
|
161
|
+
</html>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Component Reference
|
|
165
|
+
|
|
166
|
+
| Component | Description |
|
|
167
|
+
|-----------|-------------|
|
|
168
|
+
| `EssentialsLayout` | Full-page wrapper with HTML head, all sections, and theme injection |
|
|
169
|
+
| `Navbar` | Header with business name, click-to-call, and "Contact Us" CTA |
|
|
170
|
+
| `Hero` | Above-the-fold section with headline, subheadline, image, and CTA |
|
|
171
|
+
| `ServicesGrid` | Responsive grid of service cards with hover effects |
|
|
172
|
+
| `ContactForm` | Lead capture form with name, email, phone, and message fields |
|
|
173
|
+
| `Footer` | Site footer with contact info, social icons, and copyright |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 🎨 Theming
|
|
178
|
+
|
|
179
|
+
Colors are defined in the `theme` object and automatically applied throughout the site using CSS variables.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
theme: {
|
|
183
|
+
primary: "#2d5a27", // Brand color, used for headings, nav, buttons
|
|
184
|
+
secondary: "#e8f5e3", // Section backgrounds (e.g., services grid)
|
|
185
|
+
accent: "#f4a460", // CTA buttons, highlights
|
|
186
|
+
background: "#ffffff", // Page background
|
|
187
|
+
foreground: "#1a1a1a", // Body text
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Using Theme Colors in Custom Components
|
|
192
|
+
|
|
193
|
+
The theme colors are available as CSS variables:
|
|
194
|
+
|
|
195
|
+
```css
|
|
196
|
+
.my-custom-element {
|
|
197
|
+
background-color: var(--color-primary);
|
|
198
|
+
color: var(--color-background);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Or with Tailwind's arbitrary value syntax:
|
|
203
|
+
|
|
204
|
+
```html
|
|
205
|
+
<div class="bg-[color:var(--color-accent)] text-[color:var(--color-foreground)]">
|
|
206
|
+
Custom styled element
|
|
207
|
+
</div>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 🖼️ Hero Layouts
|
|
213
|
+
|
|
214
|
+
The Hero component supports three layout modes:
|
|
215
|
+
|
|
216
|
+
### Image Right (Default)
|
|
217
|
+
```typescript
|
|
218
|
+
layoutOptions: {
|
|
219
|
+
heroLayout: "image-right",
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
Text on the left, image on the right – great for text-focused messaging.
|
|
223
|
+
|
|
224
|
+
### Image Left
|
|
225
|
+
```typescript
|
|
226
|
+
layoutOptions: {
|
|
227
|
+
heroLayout: "image-left",
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
Image on the left, text on the right – ideal for showcasing visuals.
|
|
231
|
+
|
|
232
|
+
### Full Width
|
|
233
|
+
```typescript
|
|
234
|
+
layoutOptions: {
|
|
235
|
+
heroLayout: "full-width",
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
Full-bleed background image with centered text overlay – high-impact hero style.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 📱 Responsive Design
|
|
243
|
+
|
|
244
|
+
All components are mobile-first and responsive:
|
|
245
|
+
|
|
246
|
+
- **Navbar**: Stacks phone number, hides "Contact Us" button on mobile
|
|
247
|
+
- **Hero**: Single column on mobile, side-by-side on desktop
|
|
248
|
+
- **ServicesGrid**: 1 column → 2 columns → 3 columns as screen grows
|
|
249
|
+
- **ContactForm**: Fields stack on mobile
|
|
250
|
+
- **Footer**: 3-column layout collapses to single column on mobile
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 📋 Configuration Reference
|
|
255
|
+
|
|
256
|
+
### SiteConfig Interface
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
interface SiteConfig {
|
|
260
|
+
businessName: string;
|
|
261
|
+
theme: Theme;
|
|
262
|
+
contactInfo: ContactInfo;
|
|
263
|
+
heroSection: HeroSection;
|
|
264
|
+
services: Service[];
|
|
265
|
+
analytics: AnalyticsConfig;
|
|
266
|
+
seo: SEOConfig;
|
|
267
|
+
imageSearchHints?: ImageSearchHints; // Optional
|
|
268
|
+
socialLinks?: SocialLinks; // Optional
|
|
269
|
+
layoutOptions?: LayoutOptions; // Optional
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Theme
|
|
274
|
+
|
|
275
|
+
| Property | Type | Description |
|
|
276
|
+
|----------|------|-------------|
|
|
277
|
+
| `primary` | `string` | Brand color for headings, nav, primary buttons |
|
|
278
|
+
| `secondary` | `string` | Section backgrounds, subtle accents |
|
|
279
|
+
| `accent` | `string` | CTA buttons, highlights |
|
|
280
|
+
| `background` | `string` | Page background color |
|
|
281
|
+
| `foreground` | `string` | Body text color |
|
|
282
|
+
|
|
283
|
+
### ContactInfo
|
|
284
|
+
|
|
285
|
+
| Property | Type | Description |
|
|
286
|
+
|----------|------|-------------|
|
|
287
|
+
| `phone` | `string` | Phone number (auto-linked as `tel:`) |
|
|
288
|
+
| `email` | `string` | Email address (auto-linked as `mailto:`) |
|
|
289
|
+
| `address` | `string` | Physical address |
|
|
290
|
+
|
|
291
|
+
### HeroSection
|
|
292
|
+
|
|
293
|
+
| Property | Type | Description |
|
|
294
|
+
|----------|------|-------------|
|
|
295
|
+
| `headline` | `string` | Main headline text |
|
|
296
|
+
| `subheadline` | `string` | Supporting text below headline |
|
|
297
|
+
| `imageUrl` | `string` | Path or URL to hero image |
|
|
298
|
+
| `imageAlt` | `string` | Alt text for hero image |
|
|
299
|
+
| `callToActionLabel` | `string` | Text for CTA button |
|
|
300
|
+
|
|
301
|
+
### Service
|
|
302
|
+
|
|
303
|
+
| Property | Type | Description |
|
|
304
|
+
|----------|------|-------------|
|
|
305
|
+
| `name` | `string` | Service name |
|
|
306
|
+
| `description` | `string` | Service description |
|
|
307
|
+
|
|
308
|
+
### SEOConfig
|
|
309
|
+
|
|
310
|
+
| Property | Type | Description |
|
|
311
|
+
|----------|------|-------------|
|
|
312
|
+
| `title` | `string` | Page title (appears in browser tab) |
|
|
313
|
+
| `description` | `string` | Meta description for search engines |
|
|
314
|
+
|
|
315
|
+
### AnalyticsConfig
|
|
316
|
+
|
|
317
|
+
| Property | Type | Description |
|
|
318
|
+
|----------|------|-------------|
|
|
319
|
+
| `enableTracking` | `boolean` | Enable/disable analytics |
|
|
320
|
+
| `mixpanelToken` | `string` | Optional Mixpanel project token |
|
|
321
|
+
|
|
322
|
+
### SocialLinks (Optional)
|
|
323
|
+
|
|
324
|
+
| Property | Type | Description |
|
|
325
|
+
|----------|------|-------------|
|
|
326
|
+
| `facebook` | `string` | Facebook page URL |
|
|
327
|
+
| `instagram` | `string` | Instagram profile URL |
|
|
328
|
+
| `twitter` | `string` | Twitter/X profile URL |
|
|
329
|
+
| `linkedin` | `string` | LinkedIn page URL |
|
|
330
|
+
| `youtube` | `string` | YouTube channel URL |
|
|
331
|
+
| `tiktok` | `string` | TikTok profile URL |
|
|
332
|
+
|
|
333
|
+
### LayoutOptions (Optional)
|
|
334
|
+
|
|
335
|
+
| Property | Type | Default | Description |
|
|
336
|
+
|----------|------|---------|-------------|
|
|
337
|
+
| `heroLayout` | `"image-left"` \| `"image-right"` \| `"full-width"` | `"image-right"` | Hero section layout style |
|
|
338
|
+
| `stickyNav` | `boolean` | `false` | Make navbar stick to top on scroll |
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 📁 Project Structure
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
@indirecttek/essentials-engine/
|
|
346
|
+
├── dist/ # Built package (published to npm)
|
|
347
|
+
│ ├── components/
|
|
348
|
+
│ │ ├── ContactForm.astro
|
|
349
|
+
│ │ ├── Footer.astro
|
|
350
|
+
│ │ ├── Hero.astro
|
|
351
|
+
│ │ ├── Navbar.astro
|
|
352
|
+
│ │ └── ServicesGrid.astro
|
|
353
|
+
│ ├── layouts/
|
|
354
|
+
│ │ └── EssentialsLayout.astro
|
|
355
|
+
│ ├── index.ts # Main exports
|
|
356
|
+
│ └── types.ts # TypeScript interfaces
|
|
357
|
+
├── src/ # Source files
|
|
358
|
+
├── package.json
|
|
359
|
+
└── README.md
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 🔧 Development
|
|
365
|
+
|
|
366
|
+
Clone and install:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
git clone https://github.com/indirecttek/essentials-engine.git
|
|
370
|
+
cd essentials-engine
|
|
371
|
+
npm install
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Build the package:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
npm run build
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## 📄 License
|
|
383
|
+
|
|
384
|
+
MIT © [IndirectTek](https://indirecttek.com)
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 🤝 Contributing
|
|
389
|
+
|
|
390
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## 💬 Support
|
|
395
|
+
|
|
396
|
+
- **Issues**: [GitHub Issues](https://github.com/indirecttek/essentials-engine/issues)
|
|
397
|
+
- **Email**: support@indirecttek.com
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SiteConfig } from "../types";
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
config: SiteConfig;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { config } = Astro.props;
|
|
9
|
+
const { contactInfo, socialLinks } = config;
|
|
10
|
+
const currentYear = new Date().getFullYear();
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<footer class="bg-[color:var(--color-primary)] text-[color:var(--color-background)]">
|
|
14
|
+
<div class="max-w-7xl mx-auto px-4 md:px-8 py-12 md:py-16">
|
|
15
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12">
|
|
16
|
+
<!-- Brand & Description -->
|
|
17
|
+
<div class="space-y-4">
|
|
18
|
+
<h3 class="text-2xl font-bold">{config.businessName}</h3>
|
|
19
|
+
<p class="opacity-80 leading-relaxed">
|
|
20
|
+
Serving our community with excellence and dedication.
|
|
21
|
+
</p>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Contact Info -->
|
|
25
|
+
<div class="space-y-4">
|
|
26
|
+
<h4 class="text-lg font-semibold">Contact Us</h4>
|
|
27
|
+
<ul class="space-y-3 opacity-80">
|
|
28
|
+
<li>
|
|
29
|
+
<a
|
|
30
|
+
href={`tel:${contactInfo.phone}`}
|
|
31
|
+
class="hover:opacity-100 transition-opacity flex items-center gap-2"
|
|
32
|
+
>
|
|
33
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
34
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"></path>
|
|
35
|
+
</svg>
|
|
36
|
+
{contactInfo.phone}
|
|
37
|
+
</a>
|
|
38
|
+
</li>
|
|
39
|
+
<li>
|
|
40
|
+
<a
|
|
41
|
+
href={`mailto:${contactInfo.email}`}
|
|
42
|
+
class="hover:opacity-100 transition-opacity flex items-center gap-2"
|
|
43
|
+
>
|
|
44
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
45
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
|
46
|
+
</svg>
|
|
47
|
+
{contactInfo.email}
|
|
48
|
+
</a>
|
|
49
|
+
</li>
|
|
50
|
+
<li class="flex items-start gap-2">
|
|
51
|
+
<svg class="w-5 h-5 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
52
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
53
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
54
|
+
</svg>
|
|
55
|
+
<span>{contactInfo.address}</span>
|
|
56
|
+
</li>
|
|
57
|
+
</ul>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<!-- Social Links -->
|
|
61
|
+
{socialLinks && Object.keys(socialLinks).length > 0 && (
|
|
62
|
+
<div class="space-y-4">
|
|
63
|
+
<h4 class="text-lg font-semibold">Follow Us</h4>
|
|
64
|
+
<div class="flex gap-4">
|
|
65
|
+
{socialLinks.facebook && (
|
|
66
|
+
<a
|
|
67
|
+
href={socialLinks.facebook}
|
|
68
|
+
target="_blank"
|
|
69
|
+
rel="noopener noreferrer"
|
|
70
|
+
class="opacity-80 hover:opacity-100 transition-opacity"
|
|
71
|
+
aria-label="Facebook"
|
|
72
|
+
>
|
|
73
|
+
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
|
74
|
+
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
|
|
75
|
+
</svg>
|
|
76
|
+
</a>
|
|
77
|
+
)}
|
|
78
|
+
{socialLinks.instagram && (
|
|
79
|
+
<a
|
|
80
|
+
href={socialLinks.instagram}
|
|
81
|
+
target="_blank"
|
|
82
|
+
rel="noopener noreferrer"
|
|
83
|
+
class="opacity-80 hover:opacity-100 transition-opacity"
|
|
84
|
+
aria-label="Instagram"
|
|
85
|
+
>
|
|
86
|
+
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
|
87
|
+
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
|
|
88
|
+
</svg>
|
|
89
|
+
</a>
|
|
90
|
+
)}
|
|
91
|
+
{socialLinks.twitter && (
|
|
92
|
+
<a
|
|
93
|
+
href={socialLinks.twitter}
|
|
94
|
+
target="_blank"
|
|
95
|
+
rel="noopener noreferrer"
|
|
96
|
+
class="opacity-80 hover:opacity-100 transition-opacity"
|
|
97
|
+
aria-label="Twitter"
|
|
98
|
+
>
|
|
99
|
+
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
|
100
|
+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
|
101
|
+
</svg>
|
|
102
|
+
</a>
|
|
103
|
+
)}
|
|
104
|
+
{socialLinks.linkedin && (
|
|
105
|
+
<a
|
|
106
|
+
href={socialLinks.linkedin}
|
|
107
|
+
target="_blank"
|
|
108
|
+
rel="noopener noreferrer"
|
|
109
|
+
class="opacity-80 hover:opacity-100 transition-opacity"
|
|
110
|
+
aria-label="LinkedIn"
|
|
111
|
+
>
|
|
112
|
+
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
|
113
|
+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
|
|
114
|
+
</svg>
|
|
115
|
+
</a>
|
|
116
|
+
)}
|
|
117
|
+
{socialLinks.youtube && (
|
|
118
|
+
<a
|
|
119
|
+
href={socialLinks.youtube}
|
|
120
|
+
target="_blank"
|
|
121
|
+
rel="noopener noreferrer"
|
|
122
|
+
class="opacity-80 hover:opacity-100 transition-opacity"
|
|
123
|
+
aria-label="YouTube"
|
|
124
|
+
>
|
|
125
|
+
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
|
126
|
+
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
|
|
127
|
+
</svg>
|
|
128
|
+
</a>
|
|
129
|
+
)}
|
|
130
|
+
{socialLinks.tiktok && (
|
|
131
|
+
<a
|
|
132
|
+
href={socialLinks.tiktok}
|
|
133
|
+
target="_blank"
|
|
134
|
+
rel="noopener noreferrer"
|
|
135
|
+
class="opacity-80 hover:opacity-100 transition-opacity"
|
|
136
|
+
aria-label="TikTok"
|
|
137
|
+
>
|
|
138
|
+
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
|
139
|
+
<path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z"/>
|
|
140
|
+
</svg>
|
|
141
|
+
</a>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<!-- Bottom Bar -->
|
|
149
|
+
<div class="mt-12 pt-8 border-t border-[color:var(--color-background)]/20">
|
|
150
|
+
<div class="flex flex-col md:flex-row justify-between items-center gap-4">
|
|
151
|
+
<p class="opacity-80 text-sm">
|
|
152
|
+
© {currentYear} {config.businessName}. All rights reserved.
|
|
153
|
+
</p>
|
|
154
|
+
<p class="opacity-60 text-sm">
|
|
155
|
+
Built with ❤️ by Essentials Engine
|
|
156
|
+
</p>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</footer>
|
|
@@ -7,33 +7,66 @@ export interface Props {
|
|
|
7
7
|
|
|
8
8
|
const { config } = Astro.props;
|
|
9
9
|
const { heroSection } = config;
|
|
10
|
+
const layout = config.layoutOptions?.heroLayout || "image-right";
|
|
10
11
|
---
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
{layout === "full-width" ? (
|
|
14
|
+
<section class="relative overflow-hidden">
|
|
15
|
+
<!-- Full-width background image -->
|
|
16
|
+
<div class="absolute inset-0">
|
|
17
|
+
<img
|
|
18
|
+
src={heroSection.imageUrl}
|
|
19
|
+
alt={heroSection.imageAlt}
|
|
20
|
+
class="w-full h-full object-cover"
|
|
21
|
+
loading="eager"
|
|
22
|
+
/>
|
|
23
|
+
<div class="absolute inset-0 bg-[color:var(--color-primary)]/70"></div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="relative max-w-7xl mx-auto px-4 md:px-8 py-24 md:py-32 lg:py-40">
|
|
27
|
+
<div class="max-w-3xl mx-auto text-center space-y-8">
|
|
28
|
+
<h1 class="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold text-[color:var(--color-background)] leading-tight">
|
|
17
29
|
{heroSection.headline}
|
|
18
30
|
</h1>
|
|
19
|
-
<p class="text-
|
|
31
|
+
<p class="text-xl md:text-2xl text-[color:var(--color-background)] opacity-90 leading-relaxed">
|
|
20
32
|
{heroSection.subheadline}
|
|
21
33
|
</p>
|
|
22
34
|
<a
|
|
23
35
|
href="#contact"
|
|
24
|
-
class="inline-block bg-[color:var(--color-accent)] text-[color:var(--color-foreground)] px-
|
|
36
|
+
class="inline-block bg-[color:var(--color-accent)] text-[color:var(--color-foreground)] px-8 py-4 md:px-10 md:py-5 rounded-xl font-semibold text-lg md:text-xl hover:scale-105 hover:shadow-lg transition-all duration-300"
|
|
25
37
|
>
|
|
26
38
|
{heroSection.callToActionLabel}
|
|
27
39
|
</a>
|
|
28
40
|
</div>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
</div>
|
|
42
|
+
</section>
|
|
43
|
+
) : (
|
|
44
|
+
<section class="relative overflow-hidden">
|
|
45
|
+
<div class="max-w-7xl mx-auto px-4 md:px-8 py-20 md:py-28 lg:py-32">
|
|
46
|
+
<div class={`grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center ${layout === "image-left" ? "lg:flex-row-reverse" : ""}`}>
|
|
47
|
+
<div class={`space-y-8 ${layout === "image-left" ? "lg:order-2" : ""}`}>
|
|
48
|
+
<h1 class="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold text-[color:var(--color-primary)] leading-tight">
|
|
49
|
+
{heroSection.headline}
|
|
50
|
+
</h1>
|
|
51
|
+
<p class="text-lg md:text-xl lg:text-2xl text-[color:var(--color-foreground)] opacity-80 leading-relaxed">
|
|
52
|
+
{heroSection.subheadline}
|
|
53
|
+
</p>
|
|
54
|
+
<a
|
|
55
|
+
href="#contact"
|
|
56
|
+
class="inline-block bg-[color:var(--color-accent)] text-[color:var(--color-foreground)] px-8 py-4 md:px-10 md:py-5 rounded-xl font-semibold text-lg md:text-xl hover:scale-105 hover:shadow-lg transition-all duration-300"
|
|
57
|
+
>
|
|
58
|
+
{heroSection.callToActionLabel}
|
|
59
|
+
</a>
|
|
60
|
+
</div>
|
|
61
|
+
<div class={`relative ${layout === "image-left" ? "lg:order-1" : ""}`}>
|
|
62
|
+
<img
|
|
63
|
+
src={heroSection.imageUrl}
|
|
64
|
+
alt={heroSection.imageAlt}
|
|
65
|
+
class="w-full h-auto rounded-2xl shadow-2xl object-cover aspect-[4/3]"
|
|
66
|
+
loading="eager"
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
36
69
|
</div>
|
|
37
70
|
</div>
|
|
38
|
-
</
|
|
39
|
-
|
|
71
|
+
</section>
|
|
72
|
+
)}
|
|
@@ -6,9 +6,10 @@ export interface Props {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
const { config } = Astro.props;
|
|
9
|
+
const isSticky = config.layoutOptions?.stickyNav ?? false;
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
<nav class=
|
|
12
|
+
<nav class={`bg-[color:var(--color-primary)] text-[color:var(--color-background)] px-4 py-4 md:px-8 ${isSticky ? 'sticky top-0 z-50 shadow-lg' : ''}`}>
|
|
12
13
|
<div class="max-w-7xl mx-auto flex items-center justify-between">
|
|
13
14
|
<a href="/" class="text-xl md:text-2xl font-bold hover:opacity-90 transition-opacity">
|
|
14
15
|
{config.businessName}
|
|
@@ -16,13 +17,16 @@ const { config } = Astro.props;
|
|
|
16
17
|
<div class="flex items-center gap-4 md:gap-6">
|
|
17
18
|
<a
|
|
18
19
|
href={`tel:${config.contactInfo.phone}`}
|
|
19
|
-
class="text-sm md:text-base font-medium hover:opacity-90 transition-opacity"
|
|
20
|
+
class="text-sm md:text-base font-medium hover:opacity-90 transition-opacity flex items-center gap-2"
|
|
20
21
|
>
|
|
22
|
+
<svg class="w-4 h-4 hidden sm:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
23
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"></path>
|
|
24
|
+
</svg>
|
|
21
25
|
{config.contactInfo.phone}
|
|
22
26
|
</a>
|
|
23
27
|
<a
|
|
24
28
|
href="#contact"
|
|
25
|
-
class="hidden md:inline-block bg-[color:var(--color-accent)] text-[color:var(--color-foreground)] px-
|
|
29
|
+
class="hidden md:inline-block bg-[color:var(--color-accent)] text-[color:var(--color-foreground)] px-5 py-2.5 rounded-xl font-medium hover:scale-105 hover:shadow-md transition-all duration-300"
|
|
26
30
|
>
|
|
27
31
|
Contact Us
|
|
28
32
|
</a>
|
|
@@ -9,18 +9,23 @@ const { config } = Astro.props;
|
|
|
9
9
|
const { services } = config;
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
<section class="px-4 md:px-8 py-
|
|
12
|
+
<section class="px-4 md:px-8 py-20 md:py-28 lg:py-32 bg-[color:var(--color-secondary)]">
|
|
13
13
|
<div class="max-w-7xl mx-auto">
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
<div class="text-center mb-16">
|
|
15
|
+
<h2 class="text-3xl md:text-4xl lg:text-5xl font-bold text-[color:var(--color-primary)] mb-4">
|
|
16
|
+
Our Services
|
|
17
|
+
</h2>
|
|
18
|
+
<p class="text-lg md:text-xl text-[color:var(--color-foreground)] opacity-70 max-w-2xl mx-auto">
|
|
19
|
+
Professional solutions tailored to your needs
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 md:gap-10">
|
|
18
23
|
{services.map((service) => (
|
|
19
|
-
<div class="bg-[color:var(--color-background)] p-
|
|
20
|
-
<h3 class="text-xl md:text-2xl font-semibold text-[color:var(--color-primary)] mb-
|
|
24
|
+
<div class="bg-[color:var(--color-background)] p-8 md:p-10 rounded-2xl shadow-md hover:shadow-xl hover:scale-[1.02] transition-all duration-300 cursor-default">
|
|
25
|
+
<h3 class="text-xl md:text-2xl font-semibold text-[color:var(--color-primary)] mb-4">
|
|
21
26
|
{service.name}
|
|
22
27
|
</h3>
|
|
23
|
-
<p class="text-[color:var(--color-foreground)] opacity-80 leading-relaxed">
|
|
28
|
+
<p class="text-[color:var(--color-foreground)] opacity-80 leading-relaxed text-lg">
|
|
24
29
|
{service.description}
|
|
25
30
|
</p>
|
|
26
31
|
</div>
|
package/dist/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type {
|
|
|
7
7
|
AnalyticsConfig,
|
|
8
8
|
SEOConfig,
|
|
9
9
|
ImageSearchHints,
|
|
10
|
+
SocialLinks,
|
|
11
|
+
LayoutOptions,
|
|
10
12
|
SiteConfig,
|
|
11
13
|
} from "./types";
|
|
12
14
|
|
|
@@ -18,3 +20,4 @@ export { default as Navbar } from "./components/Navbar.astro";
|
|
|
18
20
|
export { default as Hero } from "./components/Hero.astro";
|
|
19
21
|
export { default as ServicesGrid } from "./components/ServicesGrid.astro";
|
|
20
22
|
export { default as ContactForm } from "./components/ContactForm.astro";
|
|
23
|
+
export { default as Footer } from "./components/Footer.astro";
|
|
@@ -4,6 +4,7 @@ import Navbar from "../components/Navbar.astro";
|
|
|
4
4
|
import Hero from "../components/Hero.astro";
|
|
5
5
|
import ServicesGrid from "../components/ServicesGrid.astro";
|
|
6
6
|
import ContactForm from "../components/ContactForm.astro";
|
|
7
|
+
import Footer from "../components/Footer.astro";
|
|
7
8
|
|
|
8
9
|
export interface Props {
|
|
9
10
|
config: SiteConfig;
|
|
@@ -23,20 +24,14 @@ const { config } = Astro.props;
|
|
|
23
24
|
</head>
|
|
24
25
|
<body
|
|
25
26
|
style={`--color-primary: ${config.theme.primary}; --color-secondary: ${config.theme.secondary}; --color-accent: ${config.theme.accent}; --color-background: ${config.theme.background}; --color-foreground: ${config.theme.foreground};`}
|
|
26
|
-
class="bg-[color:var(--color-background)] text-[color:var(--color-foreground)] min-h-screen"
|
|
27
|
+
class="bg-[color:var(--color-background)] text-[color:var(--color-foreground)] min-h-screen leading-relaxed"
|
|
27
28
|
>
|
|
28
29
|
<Navbar config={config} />
|
|
29
|
-
<main
|
|
30
|
+
<main>
|
|
30
31
|
<Hero config={config} />
|
|
31
32
|
<ServicesGrid config={config} />
|
|
32
33
|
<ContactForm config={config} />
|
|
33
34
|
</main>
|
|
34
|
-
<
|
|
35
|
-
<div class="max-w-7xl mx-auto text-center">
|
|
36
|
-
<p class="opacity-90">
|
|
37
|
-
© {new Date().getFullYear()} {config.businessName}. All rights reserved.
|
|
38
|
-
</p>
|
|
39
|
-
</div>
|
|
40
|
-
</footer>
|
|
35
|
+
<Footer config={config} />
|
|
41
36
|
</body>
|
|
42
37
|
</html>
|
package/dist/types.ts
CHANGED
|
@@ -40,6 +40,20 @@ export interface ImageSearchHints {
|
|
|
40
40
|
services: string[];
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export interface SocialLinks {
|
|
44
|
+
facebook?: string;
|
|
45
|
+
instagram?: string;
|
|
46
|
+
twitter?: string;
|
|
47
|
+
linkedin?: string;
|
|
48
|
+
youtube?: string;
|
|
49
|
+
tiktok?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface LayoutOptions {
|
|
53
|
+
heroLayout?: "image-left" | "image-right" | "full-width";
|
|
54
|
+
stickyNav?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
43
57
|
export interface SiteConfig {
|
|
44
58
|
businessName: string;
|
|
45
59
|
theme: Theme;
|
|
@@ -49,4 +63,6 @@ export interface SiteConfig {
|
|
|
49
63
|
analytics: AnalyticsConfig;
|
|
50
64
|
seo: SEOConfig;
|
|
51
65
|
imageSearchHints?: ImageSearchHints;
|
|
66
|
+
socialLinks?: SocialLinks;
|
|
67
|
+
layoutOptions?: LayoutOptions;
|
|
52
68
|
}
|