new
BIN
src/assets/favicons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
src/assets/favicons/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
2
src/assets/favicons/favicon.svg
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
src/assets/images/africanregensummit.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
src/assets/images/app-store.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/images/bg.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
1
src/assets/images/bg.svg
Normal file
After Width: | Height: | Size: 143 KiB |
BIN
src/assets/images/default.png
Normal file
After Width: | Height: | Size: 563 KiB |
BIN
src/assets/images/digitalfreezonezanzibar.png
Normal file
After Width: | Height: | Size: 490 KiB |
BIN
src/assets/images/google-play.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/hero-image.png
Normal file
After Width: | Height: | Size: 539 KiB |
BIN
src/assets/images/inthenews.png
Normal file
After Width: | Height: | Size: 683 KiB |
1
src/assets/images/logo.svg
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
src/assets/images/newchapter.png
Normal file
After Width: | Height: | Size: 965 KiB |
1
src/assets/images/ow.svg
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
src/assets/images/photo-1504384308090-c894fdcc538d.avif
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
src/assets/images/simple.png
Normal file
After Width: | Height: | Size: 1.9 MiB |
BIN
src/assets/images/zanzibar.png
Normal file
After Width: | Height: | Size: 3.2 MiB |
97
src/assets/styles/tailwind.css
Normal file
@@ -0,0 +1,97 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer utilities {
|
||||
.bg-page {
|
||||
background-color: var(--aw-color-bg-page);
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: var(--aw-color-bg-page-dark);
|
||||
}
|
||||
.bg-light {
|
||||
background-color: var(--aw-color-bg-page);
|
||||
}
|
||||
.bg-grad {
|
||||
--tw-bg-opacity: 1;
|
||||
background: rgb(255,255,255);
|
||||
background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(182,189,255,0.15312062324929976) 100%);
|
||||
}
|
||||
.text-page {
|
||||
color: var(--aw-color-text-page);
|
||||
}
|
||||
.text-muted {
|
||||
color: var(--aw-color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply inline-flex items-center justify-center rounded-full border-gray-400 border bg-transparent font-medium text-center text-base text-page leading-snug transition py-3.5 px-6 md:px-8 ease-in duration-200 focus:ring-blue-500 focus:ring-offset-blue-200 focus:ring-2 focus:ring-offset-2 hover:bg-gray-100 hover:border-gray-600 dark:text-slate-300 dark:border-slate-500 dark:hover:bg-slate-800 dark:hover:border-slate-800 cursor-pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply btn font-semibold bg-primary text-white border-primary hover:bg-secondary hover:border-secondary hover:text-white dark:text-white dark:bg-primary dark:border-primary dark:hover:border-secondary dark:hover:bg-secondary;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply btn;
|
||||
}
|
||||
|
||||
.btn-tertiary {
|
||||
@apply btn border-none shadow-none text-muted hover:text-gray-900 dark:text-gray-400 dark:hover:text-white;
|
||||
}
|
||||
}
|
||||
|
||||
#header.scroll > div:first-child {
|
||||
@apply bg-page md:bg-white/90 md:backdrop-blur-md;
|
||||
box-shadow: 0 0.375rem 1.5rem 0 rgb(140 152 164 / 13%);
|
||||
}
|
||||
.dark #header.scroll > div:first-child,
|
||||
#header.scroll.dark > div:first-child {
|
||||
@apply bg-page md:bg-[#030621e6] border-b border-gray-500/20;
|
||||
box-shadow: none;
|
||||
}
|
||||
/* #header.scroll > div:last-child {
|
||||
@apply py-3;
|
||||
} */
|
||||
|
||||
#header.expanded nav {
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 70px !important;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.dropdown:focus .dropdown-menu,
|
||||
.dropdown:focus-within .dropdown-menu,
|
||||
.dropdown:hover .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
[astro-icon].icon-light > * {
|
||||
stroke-width: 1.2;
|
||||
}
|
||||
|
||||
[astro-icon].icon-bold > * {
|
||||
stroke-width: 2.4;
|
||||
}
|
||||
|
||||
[data-aw-toggle-menu] path {
|
||||
@apply transition;
|
||||
}
|
||||
[data-aw-toggle-menu].expanded g > path:first-child {
|
||||
@apply -rotate-45 translate-y-[15px] translate-x-[-3px];
|
||||
}
|
||||
|
||||
[data-aw-toggle-menu].expanded g > path:last-child {
|
||||
@apply rotate-45 translate-y-[-8px] translate-x-[14px];
|
||||
}
|
||||
|
||||
/* To deprecated */
|
||||
|
||||
.dd *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
63
src/components/CustomStyles.astro
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
import '@fontsource-variable/inter';
|
||||
|
||||
// 'DM Sans'
|
||||
// Nunito
|
||||
// Dosis
|
||||
// Outfit
|
||||
// Roboto
|
||||
// Literata
|
||||
// 'IBM Plex Sans'
|
||||
// Karla
|
||||
// Poppins
|
||||
// 'Fira Sans'
|
||||
// 'Libre Franklin'
|
||||
// Inconsolata
|
||||
// Raleway
|
||||
// Oswald
|
||||
// 'Space Grotesk'
|
||||
// Urbanist
|
||||
---
|
||||
|
||||
<style is:inline>
|
||||
:root {
|
||||
--aw-font-sans: 'Poppins';
|
||||
--aw-font-serif: 'Poppins';
|
||||
--aw-font-heading: 'Poppins';
|
||||
|
||||
--aw-color-primary: rgb(1 97 239);
|
||||
--aw-color-secondary: rgb(1 84 207);
|
||||
--aw-color-accent: rgb(109 40 217);
|
||||
|
||||
--aw-color-text-heading: rgb(0 0 0);
|
||||
--aw-color-text-default: rgb(16 16 16);
|
||||
--aw-color-text-muted: rgb(16 16 16 / 66%);
|
||||
--aw-color-bg-page: rgb(255 255 255);
|
||||
|
||||
--aw-color-bg-page-dark: rgb(3 6 32);
|
||||
|
||||
::selection {
|
||||
background-color: lavender;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
--aw-font-sans: 'Poppins';
|
||||
--aw-font-serif: 'Poppins';
|
||||
--aw-font-heading: 'Poppins';
|
||||
|
||||
--aw-color-primary: rgb(1 97 239);
|
||||
--aw-color-secondary: rgb(1 84 207);
|
||||
--aw-color-accent: rgb(109 40 217);
|
||||
|
||||
--aw-color-text-heading: rgb(247, 248, 248);
|
||||
--aw-color-text-default: rgb(229 236 246);
|
||||
--aw-color-text-muted: rgb(229 236 246 / 66%);
|
||||
--aw-color-bg-page: rgb(3 6 32);
|
||||
|
||||
::selection {
|
||||
background-color: black;
|
||||
color: snow;
|
||||
}
|
||||
}
|
||||
</style>
|
10
src/components/Favicons.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
import favIcon from '~/assets/favicons/favicon.ico';
|
||||
import favIconSvg from '~/assets/favicons/favicon.svg';
|
||||
import appleTouchIcon from '~/assets/favicons/apple-touch-icon.png';
|
||||
---
|
||||
|
||||
<link rel="shortcut icon" href={favIcon} />
|
||||
<link rel="icon" type="image/svg+xml" href={favIconSvg.src} />
|
||||
<link rel="mask-icon" href={favIconSvg.src} color="#8D46E7" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href={appleTouchIcon.src} />
|
8
src/components/Logo.astro
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
import { SITE } from 'astrowind:config';
|
||||
---
|
||||
|
||||
<span class="self-center ml-2 rtl:ml-0 rtl:mr-2 text-2xl md:text-xl font-bold text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<img src="/src/assets/images/logo.svg" alt="Logo" class="inline-block w-6 h-6 mr-2">
|
||||
{SITE?.name}
|
||||
</span>
|
14
src/components/blog/Grid.astro
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
import Item from '~/components/blog/GridItem.astro';
|
||||
import type { Post } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
posts: Array<Post>;
|
||||
}
|
||||
|
||||
const { posts } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="grid gap-6 row-gap-5 md:grid-cols-2 lg:grid-cols-4 -mb-6">
|
||||
{posts.map((post) => <Item post={post} />)}
|
||||
</div>
|
69
src/components/blog/GridItem.astro
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
import { APP_BLOG } from 'astrowind:config';
|
||||
import type { Post } from '~/types';
|
||||
|
||||
import Image from '~/components/common/Image.astro';
|
||||
|
||||
import { findImage } from '~/utils/images';
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
|
||||
export interface Props {
|
||||
post: Post;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
const image = await findImage(post.image);
|
||||
|
||||
const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') : '';
|
||||
---
|
||||
|
||||
<article class="mb-6 transition">
|
||||
<div class="relative md:h-64 bg-gray-400 dark:bg-slate-700 rounded shadow-lg mb-6">
|
||||
{
|
||||
image &&
|
||||
(link ? (
|
||||
<a href={link}>
|
||||
<Image
|
||||
src={image}
|
||||
class="w-full md:h-full rounded shadow-lg bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 900]}
|
||||
width={400}
|
||||
sizes="(max-width: 900px) 400px, 900px"
|
||||
alt={post.title}
|
||||
aspectRatio="16:9"
|
||||
layout="cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</a>
|
||||
) : (
|
||||
<Image
|
||||
src={image}
|
||||
class="w-full md:h-full rounded shadow-lg bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 900]}
|
||||
width={400}
|
||||
sizes="(max-width: 900px) 400px, 900px"
|
||||
alt={post.title}
|
||||
aspectRatio="16:9"
|
||||
layout="cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<h3 class="text-xl sm:text-2xl font-bold leading-tight mb-2 font-heading dark:text-slate-300">
|
||||
{
|
||||
link ? (
|
||||
<a class="inline-block hover:text-primary dark:hover:text-blue-700 transition ease-in duration-200" href={link}>
|
||||
{post.title}
|
||||
</a>
|
||||
) : (
|
||||
post.title
|
||||
)
|
||||
}
|
||||
</h3>
|
||||
|
||||
<p class="text-muted dark:text-slate-400 text-lg">{post.excerpt}</p>
|
||||
</article>
|
12
src/components/blog/Headline.astro
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
const { title = await Astro.slots.render('default'), subtitle = await Astro.slots.render('subtitle') } = Astro.props;
|
||||
---
|
||||
|
||||
<header class="mb-8 md:mb-16 text-center max-w-3xl mx-auto">
|
||||
<h1 class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter font-heading" set:html={title} />
|
||||
{
|
||||
subtitle && (
|
||||
<div class="mt-2 md:mt-3 mx-auto text-xl text-gray-500 dark:text-slate-400 font-medium" set:html={subtitle} />
|
||||
)
|
||||
}
|
||||
</header>
|
23
src/components/blog/List.astro
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
import Item from '~/components/blog/ListItem.astro';
|
||||
import type { Post } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
posts: Array<Post>;
|
||||
}
|
||||
|
||||
const { posts } = Astro.props;
|
||||
---
|
||||
|
||||
<ul>
|
||||
{
|
||||
posts.map((post) => (
|
||||
<li class="mb-12 md:mb-20">
|
||||
<Item post={post} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<div class="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]" aria-hidden="true">
|
||||
<div class="aspect-[1108/632] w-[69.25rem] bg-[#80caff] opacity-10" style="clip-path: polygon(26.4% 51.7%, 8.3% 11.8%, 0% 46.4%, 2.6% 82.2%, 7.5% 84.9%, 24.3% 64%, 44.7% 47.5%, 53.5% 49.4%, 55% 62.9%, 49.7% 87.2%, 78.7% 64.1%, 99.9% 100%, 94.6% 51.1%, 78.6% 63.9%, 41.1% 0.2%, 26.4% 51.7%)"></div>
|
||||
</div>
|
118
src/components/blog/ListItem.astro
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
import type { ImageMetadata } from 'astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import PostTags from '~/components/blog/Tags.astro';
|
||||
|
||||
import { APP_BLOG } from 'astrowind:config';
|
||||
import type { Post } from '~/types';
|
||||
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
import { findImage } from '~/utils/images';
|
||||
import { getFormattedDate } from '~/utils/utils';
|
||||
|
||||
export interface Props {
|
||||
post: Post;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
const image = (await findImage(post.image)) as ImageMetadata | undefined;
|
||||
|
||||
const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') : '';
|
||||
---
|
||||
|
||||
<article class={`max-w-md mx-auto md:max-w-none grid gap-6 md:gap-8 ${image ? 'md:grid-cols-2' : ''}`}>
|
||||
{
|
||||
image &&
|
||||
(link ? (
|
||||
<a class="relative block group" href={link ?? 'javascript:void(0)'}>
|
||||
<div class="relative h-0 pb-[56.25%] md:pb-[75%] md:h-72 lg:pb-[56.25%] overflow-hidden bg-gray-400 dark:bg-slate-700 rounded shadow-lg">
|
||||
{image && (
|
||||
<Image
|
||||
src={image}
|
||||
class="absolute inset-0 object-cover w-full h-full mb-6 rounded shadow-lg bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 900]}
|
||||
width={900}
|
||||
sizes="(max-width: 900px) 400px, 900px"
|
||||
alt={post.title}
|
||||
aspectRatio="16:9"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div class="relative h-0 pb-[56.25%] md:pb-[75%] md:h-72 lg:pb-[56.25%] overflow-hidden bg-gray-400 dark:bg-slate-700 rounded shadow-lg">
|
||||
{image && (
|
||||
<Image
|
||||
src={image}
|
||||
class="absolute inset-0 object-cover w-full h-full mb-6 rounded shadow-lg bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 900]}
|
||||
width={900}
|
||||
sizes="(max-width: 900px) 400px, 900px"
|
||||
alt={post.title}
|
||||
aspectRatio="16:9"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
<div class="mt-2">
|
||||
<header>
|
||||
<div class="mb-1">
|
||||
<span class="text-sm">
|
||||
<Icon name="tabler:clock" class="w-3.5 h-3.5 inline-block -mt-0.5 dark:text-gray-400" />
|
||||
<time datetime={String(post.publishDate)} class="inline-block">{getFormattedDate(post.publishDate)}</time>
|
||||
{
|
||||
post.author && (
|
||||
<>
|
||||
{' '}
|
||||
· <Icon name="tabler:user" class="w-3.5 h-3.5 inline-block -mt-0.5 dark:text-gray-400" />
|
||||
<span>{post.author.replaceAll('-', ' ')}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
post.category && (
|
||||
<>
|
||||
{' '}
|
||||
·{' '}
|
||||
<a class="hover:underline" href={getPermalink(post.category.slug, 'category')}>
|
||||
{post.category.title}
|
||||
</a>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<h2 class="text-xl sm:text-2xl font-bold leading-tight mb-2 font-heading dark:text-slate-300">
|
||||
{
|
||||
link ? (
|
||||
<a
|
||||
class="inline-block hover:text-primary dark:hover:text-blue-700 transition ease-in duration-200"
|
||||
href={link}
|
||||
>
|
||||
{post.title}
|
||||
</a>
|
||||
) : (
|
||||
post.title
|
||||
)
|
||||
}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
{post.excerpt && <p class="flex-grow text-muted dark:text-slate-400 text-lg">{post.excerpt}</p>}
|
||||
{
|
||||
post.tags && Array.isArray(post.tags) ? (
|
||||
<footer class="mt-5">
|
||||
<PostTags tags={post.tags} />
|
||||
</footer>
|
||||
) : (
|
||||
<Fragment />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</article>
|
36
src/components/blog/Pagination.astro
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
export interface Props {
|
||||
prevUrl?: string;
|
||||
nextUrl?: string;
|
||||
prevText?: string;
|
||||
nextText?: string;
|
||||
}
|
||||
|
||||
const { prevUrl, nextUrl, prevText = 'Newer posts', nextText = 'Older posts' } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
(prevUrl || nextUrl) && (
|
||||
<div class="container flex">
|
||||
<div class="flex flex-row mx-auto container justify-between">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
class={`md:px-3 px-3 mr-2 ${!prevUrl ? 'invisible' : ''}`}
|
||||
href={getPermalink(prevUrl)}
|
||||
>
|
||||
<Icon name="tabler:chevron-left" class="w-6 h-6" />
|
||||
<p class="ml-2">{prevText}</p>
|
||||
</Button>
|
||||
|
||||
<Button variant="tertiary" class={`md:px-3 px-3 ${!nextUrl ? 'invisible' : ''}`} href={getPermalink(nextUrl)}>
|
||||
<span class="mr-2">{nextText}</span>
|
||||
<Icon name="tabler:chevron-right" class="w-6 h-6" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
28
src/components/blog/RelatedPosts.astro
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
import { APP_BLOG } from 'astrowind:config';
|
||||
|
||||
import { getRelatedPosts } from '~/utils/blog';
|
||||
import BlogHighlightedPosts from '../widgets/BlogHighlightedPosts.astro';
|
||||
import type { Post } from '~/types';
|
||||
import { getBlogPermalink } from '~/utils/permalinks';
|
||||
|
||||
export interface Props {
|
||||
post: Post;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
|
||||
const relatedPosts = post.tags ? await getRelatedPosts(post, 4) : [];
|
||||
---
|
||||
|
||||
{
|
||||
APP_BLOG.isRelatedPostsEnabled ? (
|
||||
<BlogHighlightedPosts
|
||||
classes={{ container: 'pt-0 lg:pt-0 md:pt-0' }}
|
||||
title="Related Posts"
|
||||
linkText="View All Posts"
|
||||
linkUrl={getBlogPermalink()}
|
||||
postIds={relatedPosts.map((post) => post.id)}
|
||||
/>
|
||||
) : null
|
||||
}
|
103
src/components/blog/SinglePost.astro
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import PostTags from '~/components/blog/Tags.astro';
|
||||
import SocialShare from '~/components/common/SocialShare.astro';
|
||||
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
import { getFormattedDate } from '~/utils/utils';
|
||||
|
||||
|
||||
import type { Post } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
post: Post;
|
||||
url: string | URL;
|
||||
}
|
||||
|
||||
const { post, url } = Astro.props;
|
||||
---
|
||||
|
||||
<section class="py-8 sm:py-16 lg:py-20 mx-auto">
|
||||
<div class="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]" aria-hidden="true">
|
||||
<div class="aspect-[1108/632] w-[69.25rem] bg-[#80caff] opacity-10" style="clip-path: polygon(26.4% 51.7%, 8.3% 11.8%, 0% 46.4%, 2.6% 82.2%, 7.5% 84.9%, 24.3% 64%, 44.7% 47.5%, 53.5% 49.4%, 55% 62.9%, 49.7% 87.2%, 78.7% 64.1%, 99.9% 100%, 94.6% 51.1%, 78.6% 63.9%, 41.1% 0.2%, 26.4% 51.7%)"></div>
|
||||
</div>
|
||||
<article>
|
||||
<header class={post.image ? '' : ''}>
|
||||
<div class="flex justify-between flex-col sm:flex-row max-w-3xl mx-auto mt-0 mb-2 px-4 sm:px-6 sm:items-center">
|
||||
<p>
|
||||
<Icon name="tabler:clock" class="w-4 h-4 inline-block -mt-0.5 dark:text-gray-400" />
|
||||
<time datetime={String(post.publishDate)} class="inline-block">{getFormattedDate(post.publishDate)}</time>
|
||||
{
|
||||
post.author && (
|
||||
<>
|
||||
{' '}
|
||||
· <Icon name="tabler:user" class="w-4 h-4 inline-block -mt-0.5 dark:text-gray-400" />
|
||||
<span class="inline-block">{post.author}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
post.category && (
|
||||
<>
|
||||
{' '}
|
||||
·{' '}
|
||||
<a class="hover:underline inline-block" href={getPermalink(post.category.slug, 'category')}>
|
||||
{post.category.title}
|
||||
</a>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
post.readingTime && (
|
||||
<>
|
||||
· <span>{post.readingTime}</span> min read
|
||||
</>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
class="px-4 sm:px-6 max-w-3xl mx-auto text-4xl md:text-5xl font-bold leading-tighter tracking-tighter font-heading"
|
||||
>
|
||||
{post.title}
|
||||
</h1>
|
||||
<p
|
||||
class="max-w-3xl mx-auto mt-4 mb-8 px-4 sm:px-6 text-xl md:text-2xl text-muted dark:text-slate-400 text-justify"
|
||||
>
|
||||
{post.excerpt}
|
||||
</p>
|
||||
|
||||
{
|
||||
post.image ? (
|
||||
<Image
|
||||
src={post.image}
|
||||
class="max-w-full lg:max-w-[900px] mx-auto mb-6 sm:rounded-md bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 900]}
|
||||
sizes="(max-width: 900px) 400px, 900px"
|
||||
alt={post?.excerpt || ''}
|
||||
width={900}
|
||||
height={506}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
) : (
|
||||
<div class="max-w-3xl mx-auto px-4 sm:px-6">
|
||||
<div class="border-t dark:border-slate-700" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</header>
|
||||
<div
|
||||
class="mx-auto px-6 sm:px-6 max-w-3xl prose prose-md lg:prose-xl dark:prose-invert dark:prose-headings:text-slate-300 prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-primary dark:prose-a:text-blue-400 prose-img:rounded-md prose-img:shadow-lg mt-8 prose-headings:scroll-mt-[80px] prose-li:my-0"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="mx-auto px-6 sm:px-6 max-w-3xl mt-8 flex justify-between flex-col sm:flex-row">
|
||||
<PostTags tags={post.tags} class="mr-5 rtl:mr-0 rtl:ml-5" />
|
||||
<SocialShare url={url} text={post.title} class="mt-5 sm:mt-1 align-middle text-gray-500 dark:text-slate-600" />
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
45
src/components/blog/Tags.astro
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
|
||||
import { APP_BLOG } from 'astrowind:config';
|
||||
import type { Post } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
tags: Post['tags'];
|
||||
class?: string;
|
||||
title?: string | undefined;
|
||||
isCategory?: boolean;
|
||||
}
|
||||
|
||||
const { tags, class: className = 'text-sm', title = undefined, isCategory = false } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
tags && Array.isArray(tags) && (
|
||||
<>
|
||||
<>
|
||||
{title !== undefined && (
|
||||
<span class="align-super font-normal underline underline-offset-4 decoration-2 dark:text-slate-400">
|
||||
{title}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
<ul class={className}>
|
||||
{tags.map((tag) => (
|
||||
<li class="bg-gray-100 dark:bg-slate-700 inline-block mr-2 rtl:mr-0 rtl:ml-2 mb-2 py-0.5 px-2 lowercase font-medium">
|
||||
{!APP_BLOG?.tag?.isEnabled ? (
|
||||
tag.title
|
||||
) : (
|
||||
<a
|
||||
href={getPermalink(tag.slug, isCategory ? 'category' : 'tag')}
|
||||
class="text-muted dark:text-slate-300 hover:text-primary dark:hover:text-gray-200"
|
||||
>
|
||||
{tag.title}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
20
src/components/blog/ToBlogLink.astro
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { getBlogPermalink } from '~/utils/permalinks';
|
||||
import { I18N } from 'astrowind:config';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
const { textDirection } = I18N;
|
||||
---
|
||||
|
||||
<div class="mx-auto px-6 sm:px-6 max-w-3xl pt-8 md:pt-4 pb-12 md:pb-20">
|
||||
<Button variant="tertiary" class="px-3 md:px-3" href={getBlogPermalink()}>
|
||||
{
|
||||
textDirection === 'rtl' ? (
|
||||
<Icon name="tabler:chevron-right" class="w-5 h-5 mr-1 -ml-1.5 rtl:-mr-1.5 rtl:ml-1" />
|
||||
) : (
|
||||
<Icon name="tabler:chevron-left" class="w-5 h-5 mr-1 -ml-1.5 rtl:-mr-1.5 rtl:ml-1" />
|
||||
)
|
||||
} Back to Blog
|
||||
</Button>
|
||||
</div>
|
13
src/components/common/Analytics.astro
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
import { GoogleAnalytics } from '@astrolib/analytics';
|
||||
import { ANALYTICS } from 'astrowind:config';
|
||||
---
|
||||
|
||||
{
|
||||
ANALYTICS?.vendors?.googleAnalytics?.id ? (
|
||||
<GoogleAnalytics
|
||||
id={String(ANALYTICS.vendors.googleAnalytics.id)}
|
||||
partytown={ANALYTICS?.vendors?.googleAnalytics?.partytown}
|
||||
/>
|
||||
) : null
|
||||
}
|
33
src/components/common/ApplyColorMode.astro
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
import { UI } from 'astrowind:config';
|
||||
|
||||
// TODO: This code is temporary
|
||||
---
|
||||
|
||||
<script is:inline define:vars={{ defaultTheme: UI.theme || 'system' }}>
|
||||
function applyTheme(theme) {
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
const matches = document.querySelectorAll('[data-aw-toggle-color-scheme] > input');
|
||||
|
||||
if (matches && matches.length) {
|
||||
matches.forEach((elem) => {
|
||||
elem.checked = theme !== 'dark';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ((defaultTheme && defaultTheme.endsWith(':only')) || (!localStorage.theme && defaultTheme !== 'system')) {
|
||||
applyTheme(defaultTheme.replace(':only', ''));
|
||||
} else if (
|
||||
localStorage.theme === 'dark' ||
|
||||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
applyTheme('dark');
|
||||
} else {
|
||||
applyTheme('light');
|
||||
}
|
||||
</script>
|
162
src/components/common/BasicScripts.astro
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
import { UI } from 'astrowind:config';
|
||||
---
|
||||
|
||||
<script is:inline define:vars={{ defaultTheme: UI.theme }}>
|
||||
if (window.basic_script) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.basic_script = true;
|
||||
|
||||
function applyTheme(theme) {
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
const initTheme = function () {
|
||||
if ((defaultTheme && defaultTheme.endsWith(':only')) || (!localStorage.theme && defaultTheme !== 'system')) {
|
||||
applyTheme(defaultTheme.replace(':only', ''));
|
||||
} else if (
|
||||
localStorage.theme === 'dark' ||
|
||||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
applyTheme('dark');
|
||||
} else {
|
||||
applyTheme('light');
|
||||
}
|
||||
};
|
||||
initTheme();
|
||||
|
||||
function attachEvent(selector, event, fn) {
|
||||
const matches = typeof selector === 'string' ? document.querySelectorAll(selector) : selector;
|
||||
if (matches && matches.length) {
|
||||
matches.forEach((elem) => {
|
||||
elem.addEventListener(event, (e) => fn(e, elem), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const onLoad = function () {
|
||||
let lastKnownScrollPosition = window.scrollY;
|
||||
let ticking = true;
|
||||
|
||||
attachEvent('#header nav', 'click', function () {
|
||||
document.querySelector('[data-aw-toggle-menu]')?.classList.remove('expanded');
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
document.getElementById('header')?.classList.remove('h-screen');
|
||||
document.getElementById('header')?.classList.remove('expanded');
|
||||
document.getElementById('header')?.classList.remove('bg-page');
|
||||
document.querySelector('#header nav')?.classList.add('hidden');
|
||||
document.querySelector('#header > div > div:last-child')?.classList.add('hidden');
|
||||
});
|
||||
|
||||
attachEvent('[data-aw-toggle-menu]', 'click', function (_, elem) {
|
||||
elem.classList.toggle('expanded');
|
||||
document.body.classList.toggle('overflow-hidden');
|
||||
document.getElementById('header')?.classList.toggle('h-screen');
|
||||
document.getElementById('header')?.classList.toggle('expanded');
|
||||
document.getElementById('header')?.classList.toggle('bg-page');
|
||||
document.querySelector('#header nav')?.classList.toggle('hidden');
|
||||
document.querySelector('#header > div > div:last-child')?.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
attachEvent('[data-aw-toggle-color-scheme]', 'click', function () {
|
||||
if (defaultTheme.endsWith(':only')) {
|
||||
return;
|
||||
}
|
||||
document.documentElement.classList.toggle('dark');
|
||||
localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
|
||||
});
|
||||
|
||||
attachEvent('[data-aw-social-share]', 'click', function (_, elem) {
|
||||
const network = elem.getAttribute('data-aw-social-share');
|
||||
const url = encodeURIComponent(elem.getAttribute('data-aw-url'));
|
||||
const text = encodeURIComponent(elem.getAttribute('data-aw-text'));
|
||||
|
||||
let href;
|
||||
switch (network) {
|
||||
case 'facebook':
|
||||
href = `https://www.facebook.com/sharer.php?u=${url}`;
|
||||
break;
|
||||
case 'twitter':
|
||||
href = `https://twitter.com/intent/tweet?url=${url}&text=${text}`;
|
||||
break;
|
||||
case 'linkedin':
|
||||
href = `https://www.linkedin.com/shareArticle?mini=true&url=${url}&title=${text}`;
|
||||
break;
|
||||
case 'whatsapp':
|
||||
href = `https://wa.me/?text=${text}%20${url}`;
|
||||
break;
|
||||
case 'mail':
|
||||
href = `mailto:?subject=%22${text}%22&body=${text}%20${url}`;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
const newlink = document.createElement('a');
|
||||
newlink.target = '_blank';
|
||||
newlink.href = href;
|
||||
newlink.click();
|
||||
});
|
||||
|
||||
const screenSize = window.matchMedia('(max-width: 767px)');
|
||||
screenSize.addEventListener('change', function () {
|
||||
document.querySelector('[data-aw-toggle-menu]')?.classList.remove('expanded');
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
document.getElementById('header')?.classList.remove('h-screen');
|
||||
document.getElementById('header')?.classList.remove('expanded');
|
||||
document.getElementById('header')?.classList.remove('bg-page');
|
||||
document.querySelector('#header nav')?.classList.add('hidden');
|
||||
document.querySelector('#header > div > div:last-child')?.classList.add('hidden');
|
||||
});
|
||||
|
||||
function applyHeaderStylesOnScroll() {
|
||||
const header = document.querySelector('#header[data-aw-sticky-header]');
|
||||
if (!header) return;
|
||||
if (lastKnownScrollPosition > 60 && !header.classList.contains('scroll')) {
|
||||
header.classList.add('scroll');
|
||||
} else if (lastKnownScrollPosition <= 60 && header.classList.contains('scroll')) {
|
||||
header.classList.remove('scroll');
|
||||
}
|
||||
ticking = false;
|
||||
}
|
||||
applyHeaderStylesOnScroll();
|
||||
|
||||
attachEvent([document], 'scroll', function () {
|
||||
lastKnownScrollPosition = window.scrollY;
|
||||
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
applyHeaderStylesOnScroll();
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
const onPageShow = function () {
|
||||
document.documentElement.classList.add('motion-safe:scroll-smooth');
|
||||
const elem = document.querySelector('[data-aw-toggle-menu]');
|
||||
if (elem) {
|
||||
elem.classList.remove('expanded');
|
||||
}
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
document.getElementById('header')?.classList.remove('h-screen');
|
||||
document.getElementById('header')?.classList.remove('expanded');
|
||||
document.querySelector('#header nav')?.classList.add('hidden');
|
||||
};
|
||||
|
||||
window.onload = onLoad;
|
||||
window.onpageshow = onPageShow;
|
||||
|
||||
document.addEventListener('astro:after-swap', () => {
|
||||
initTheme();
|
||||
onLoad();
|
||||
onPageShow();
|
||||
});
|
||||
</script>
|
8
src/components/common/CommonMeta.astro
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
import { getAsset } from '~/utils/permalinks';
|
||||
---
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<link rel="sitemap" href={getAsset('/sitemap-index.xml')} />
|
64
src/components/common/Image.astro
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
import { findImage } from '~/utils/images';
|
||||
import {
|
||||
getImagesOptimized,
|
||||
astroAsseetsOptimizer,
|
||||
unpicOptimizer,
|
||||
isUnpicCompatible,
|
||||
type ImageProps,
|
||||
type AttributesProps,
|
||||
} from '~/utils/images-optimization';
|
||||
|
||||
type Props = ImageProps;
|
||||
type ImageType = {
|
||||
src: string;
|
||||
attributes: AttributesProps;
|
||||
};
|
||||
|
||||
const props = Astro.props;
|
||||
|
||||
if (props.alt === undefined || props.alt === null) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if (typeof props.width === 'string') {
|
||||
props.width = parseInt(props.width);
|
||||
}
|
||||
|
||||
if (typeof props.height === 'string') {
|
||||
props.height = parseInt(props.height);
|
||||
}
|
||||
|
||||
if (!props.loading) {
|
||||
props.loading = 'lazy';
|
||||
}
|
||||
|
||||
if (!props.decoding) {
|
||||
props.decoding = 'async';
|
||||
}
|
||||
|
||||
const _image = await findImage(props.src);
|
||||
|
||||
let image: ImageType | undefined = undefined;
|
||||
|
||||
if (typeof _image === 'string') {
|
||||
if ((_image.startsWith('http://') || _image.startsWith('https://')) && isUnpicCompatible(_image)) {
|
||||
image = await getImagesOptimized(_image, props, unpicOptimizer);
|
||||
} else {
|
||||
image = {
|
||||
src: _image,
|
||||
attributes: { ...props, src: undefined },
|
||||
};
|
||||
}
|
||||
} else if (_image) {
|
||||
image = await getImagesOptimized(_image, props, astroAsseetsOptimizer);
|
||||
}
|
||||
---
|
||||
|
||||
{
|
||||
!image ? (
|
||||
<Fragment />
|
||||
) : (
|
||||
<img src={image.src} crossorigin="anonymous" referrerpolicy="no-referrer" {...image.attributes} />
|
||||
)
|
||||
}
|
68
src/components/common/Metadata.astro
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
import merge from 'lodash.merge';
|
||||
import { AstroSeo } from '@astrolib/seo';
|
||||
|
||||
import type { Props as AstroSeoProps } from '@astrolib/seo';
|
||||
|
||||
import { SITE, METADATA, I18N } from 'astrowind:config';
|
||||
import type { MetaData } from '~/types';
|
||||
import { getCanonical } from '~/utils/permalinks';
|
||||
|
||||
import { adaptOpenGraphImages } from '~/utils/images';
|
||||
|
||||
export interface Props extends MetaData {
|
||||
dontUseTitleTemplate?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
ignoreTitleTemplate = false,
|
||||
canonical = String(getCanonical(String(Astro.url.pathname))),
|
||||
robots = {},
|
||||
description,
|
||||
openGraph = {},
|
||||
twitter = {},
|
||||
} = Astro.props;
|
||||
|
||||
const seoProps: AstroSeoProps = merge(
|
||||
{
|
||||
title: '',
|
||||
titleTemplate: '%s',
|
||||
canonical: canonical,
|
||||
noindex: true,
|
||||
nofollow: true,
|
||||
description: undefined,
|
||||
openGraph: {
|
||||
url: canonical,
|
||||
site_name: SITE?.name,
|
||||
images: [],
|
||||
locale: I18N?.language || 'en',
|
||||
type: 'website',
|
||||
},
|
||||
twitter: {
|
||||
cardType: openGraph?.images?.length ? 'summary_large_image' : 'summary',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: METADATA?.title?.default,
|
||||
titleTemplate: METADATA?.title?.template,
|
||||
noindex: typeof METADATA?.robots?.index !== 'undefined' ? !METADATA.robots.index : undefined,
|
||||
nofollow: typeof METADATA?.robots?.follow !== 'undefined' ? !METADATA.robots.follow : undefined,
|
||||
description: METADATA?.description,
|
||||
openGraph: METADATA?.openGraph,
|
||||
twitter: METADATA?.twitter,
|
||||
},
|
||||
{
|
||||
title: title,
|
||||
titleTemplate: ignoreTitleTemplate ? '%s' : undefined,
|
||||
canonical: canonical,
|
||||
noindex: typeof robots?.index !== 'undefined' ? !robots.index : undefined,
|
||||
nofollow: typeof robots?.follow !== 'undefined' ? !robots.follow : undefined,
|
||||
description: description,
|
||||
openGraph: { url: canonical, ...openGraph },
|
||||
twitter: twitter,
|
||||
}
|
||||
);
|
||||
---
|
||||
|
||||
<AstroSeo {...{ ...seoProps, openGraph: await adaptOpenGraphImages(seoProps?.openGraph, Astro.site) }} />
|
5
src/components/common/SiteVerification.astro
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
import { SITE } from 'astrowind:config';
|
||||
---
|
||||
|
||||
{SITE.googleSiteVerificationId && <meta name="google-site-verification" content={SITE.googleSiteVerificationId} />}
|
65
src/components/common/SocialShare.astro
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
export interface Props {
|
||||
text: string;
|
||||
url: string | URL;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const { text, url, class: className = 'inline-block' } = Astro.props;
|
||||
---
|
||||
|
||||
<div class={className}>
|
||||
<span class="align-super font-bold text-slate-500 dark:text-slate-400">Share:</span>
|
||||
<button
|
||||
class="ml-2 rtl:ml-0 rtl:mr-2"
|
||||
title="Twitter Share"
|
||||
data-aw-social-share="twitter"
|
||||
data-aw-url={url}
|
||||
data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:brand-x"
|
||||
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
|
||||
/>
|
||||
</button>
|
||||
<button class="ml-2 rtl:ml-0 rtl:mr-2" title="Facebook Share" data-aw-social-share="facebook" data-aw-url={url}
|
||||
><Icon
|
||||
name="tabler:brand-facebook"
|
||||
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="ml-2 rtl:ml-0 rtl:mr-2"
|
||||
title="Linkedin Share"
|
||||
data-aw-social-share="linkedin"
|
||||
data-aw-url={url}
|
||||
data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:brand-linkedin"
|
||||
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="ml-2 rtl:ml-0 rtl:mr-2"
|
||||
title="Whatsapp Share"
|
||||
data-aw-social-share="whatsapp"
|
||||
data-aw-url={url}
|
||||
data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:brand-whatsapp"
|
||||
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="ml-2 rtl:ml-0 rtl:mr-2"
|
||||
title="Email Share"
|
||||
data-aw-social-share="mail"
|
||||
data-aw-url={url}
|
||||
data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:mail"
|
||||
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
6
src/components/common/SplitbeeAnalytics.astro
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
const { doNotTrack = true, noCookieMode = false, url = 'https://cdn.splitbee.io/sb.js' } = Astro.props;
|
||||
---
|
||||
|
||||
<!-- Splitbee Analytics -->
|
||||
<script is:inline data-respect-dnt={doNotTrack} data-no-cookie={noCookieMode} async src={url}></script>
|
29
src/components/common/ToggleMenu.astro
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
export interface Props {
|
||||
label?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
label = 'Toggle Menu',
|
||||
class: className = 'flex flex-col h-12 w-12 rounded justify-center items-center cursor-pointer group',
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<button type="button" class={className} aria-label={label} data-aw-toggle-menu>
|
||||
<span class="sr-only">{label}</span>
|
||||
<slot>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="h-0.5 w-6 my-1 rounded-full bg-black dark:bg-white transition ease transform duration-200 opacity-80 group-[.expanded]:rotate-45 group-[.expanded]:translate-y-2.5"
|
||||
></span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="h-0.5 w-6 my-1 rounded-full bg-black dark:bg-white transition ease transform duration-200 opacity-80 group-[.expanded]:opacity-0"
|
||||
></span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="h-0.5 w-6 my-1 rounded-full bg-black dark:bg-white transition ease transform duration-200 opacity-80 group-[.expanded]:-rotate-45 group-[.expanded]:-translate-y-2.5"
|
||||
></span>
|
||||
</slot>
|
||||
</button>
|
28
src/components/common/ToggleTheme.astro
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
import { UI } from 'astrowind:config';
|
||||
|
||||
export interface Props {
|
||||
label?: string;
|
||||
class?: string;
|
||||
iconClass?: string;
|
||||
iconName?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
label = 'Toggle between Dark and Light mode',
|
||||
class:
|
||||
className = 'text-muted dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center',
|
||||
iconClass = 'w-6 h-6',
|
||||
iconName = 'tabler:sun',
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
!(UI.theme && UI.theme.endsWith(':only')) && (
|
||||
<button type="button" class={className} aria-label={label} data-aw-toggle-color-scheme>
|
||||
<Icon name={iconName} class={iconClass} />
|
||||
</button>
|
||||
)
|
||||
}
|
11
src/components/ui/Background.astro
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
export interface Props {
|
||||
isDark?: boolean;
|
||||
}
|
||||
|
||||
const { isDark = false } = Astro.props;
|
||||
---
|
||||
|
||||
<div class:list={['absolute inset-0', { 'bg-dark dark:bg-transparent': isDark }]}>
|
||||
<slot />
|
||||
</div>
|
40
src/components/ui/Button.astro
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { CallToAction as Props } from '~/types';
|
||||
|
||||
const {
|
||||
variant = 'secondary',
|
||||
target,
|
||||
text = Astro.slots.render('default'),
|
||||
icon = '',
|
||||
class: className = '',
|
||||
type,
|
||||
...rest
|
||||
} = Astro.props;
|
||||
|
||||
const variants = {
|
||||
primary: 'btn-primary',
|
||||
secondary: 'btn-secondary',
|
||||
tertiary: 'btn btn-tertiary',
|
||||
link: 'cursor-pointer hover:text-primary',
|
||||
};
|
||||
---
|
||||
|
||||
{
|
||||
type === 'button' || type === 'submit' || type === 'reset' ? (
|
||||
<button type={type} class={twMerge(variants[variant] || '', className)} {...rest}>
|
||||
<Fragment set:html={text} />
|
||||
{icon && <Icon name={icon} class="w-5 h-5 ml-1 -mr-1.5 rtl:mr-1 rtl:-ml-1.5 inline-block" />}
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
class={twMerge(variants[variant] || '', className)}
|
||||
{...(target ? { target: target, rel: 'noopener noreferrer' } : {})}
|
||||
{...rest}
|
||||
>
|
||||
<Fragment set:html={text} />
|
||||
{icon && <Icon name={icon} class="w-5 h-5 ml-1 -mr-1.5 rtl:mr-1 rtl:-ml-1.5 inline-block" />}
|
||||
</a>
|
||||
)
|
||||
}
|
22
src/components/ui/DListItem.astro
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
// component: DListItem
|
||||
//
|
||||
// Mimics the html 'dl' (description list)
|
||||
//
|
||||
// The 'dt' item is the item 'term' and is inserted into an 'h6' tag.
|
||||
// Caller needs to style the 'h6' tag appropriately.
|
||||
//
|
||||
// You can put pretty much any content you want between the open and
|
||||
// closing tags - it's simply contained in an enclosing div with a
|
||||
// margin left. No need for 'dd' items.
|
||||
//
|
||||
const { dt } = Astro.props;
|
||||
interface Props {
|
||||
dt: string;
|
||||
}
|
||||
|
||||
const content: string = await Astro.slots.render('default');
|
||||
---
|
||||
|
||||
<h6 set:html={dt} />
|
||||
<div class="dd ml-8" set:html={content} />
|
87
src/components/ui/Form.astro
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
import type { Form as Props } from '~/types';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
const { inputs, textarea, disclaimer, button = 'Contact us', description = '' } = Astro.props;
|
||||
---
|
||||
|
||||
<form>
|
||||
{
|
||||
inputs &&
|
||||
inputs.map(
|
||||
({ type = 'text', name, label = '', autocomplete = 'on', placeholder = '' }) =>
|
||||
name && (
|
||||
<div class="mb-6">
|
||||
{label && (
|
||||
<label for={name} class="block text-sm font-medium">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<input
|
||||
type={type}
|
||||
name={name}
|
||||
id={name}
|
||||
autocomplete={autocomplete}
|
||||
placeholder={placeholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
textarea && (
|
||||
<div>
|
||||
<label for="textarea" class="block text-sm font-medium">
|
||||
{textarea.label}
|
||||
</label>
|
||||
<textarea
|
||||
id="textarea"
|
||||
name={textarea.name ? textarea.name : 'message'}
|
||||
rows={textarea.rows ? textarea.rows : 4}
|
||||
placeholder={textarea.placeholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
disclaimer && (
|
||||
<div class="mt-3 flex items-start">
|
||||
<div class="flex mt-0.5">
|
||||
<input
|
||||
id="disclaimer"
|
||||
name="disclaimer"
|
||||
type="checkbox"
|
||||
class="cursor-pointer mt-1 py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<label for="disclaimer" class="cursor-pointer select-none text-sm text-gray-600 dark:text-gray-400">
|
||||
{disclaimer.label}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
button && (
|
||||
<div class="mt-10 grid">
|
||||
<Button variant="primary" type="submit">
|
||||
{button}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
description && (
|
||||
<div class="mt-3 text-center">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</form>
|
35
src/components/ui/Headline.astro
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
import type { Headline as Props } from '~/types';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
classes = {},
|
||||
} = Astro.props;
|
||||
|
||||
const {
|
||||
container: containerClass = 'max-w-3xl',
|
||||
title: titleClass = 'text-3xl md:text-4xl ',
|
||||
subtitle: subtitleClass = 'text-xl',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
(title || subtitle || tagline) && (
|
||||
<div class={twMerge('mb-8 md:mx-auto md:mb-12 text-center', containerClass)}>
|
||||
{tagline && (
|
||||
<p class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase" set:html={tagline} />
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class={twMerge('font-bold leading-tighter tracking-tighter font-heading text-heading text-3xl', titleClass)}
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
|
||||
{subtitle && <p class={twMerge('mt-4 text-muted', subtitleClass)} set:html={subtitle} />}
|
||||
</div>
|
||||
)
|
||||
}
|
65
src/components/ui/ItemGrid.astro
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
import type { ItemGrid as Props } from '~/types';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Button from './Button.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
const { items = [], columns, defaultIcon = '', classes = {} } = Astro.props;
|
||||
|
||||
const {
|
||||
container: containerClass = '',
|
||||
panel: panelClass = '',
|
||||
title: titleClass = '',
|
||||
description: descriptionClass = '',
|
||||
icon: defaultIconClass = 'text-primary',
|
||||
action: actionClass = '',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && (
|
||||
<div
|
||||
class={twMerge(
|
||||
`grid mx-auto gap-8 md:gap-y-12 ${
|
||||
columns === 4
|
||||
? 'lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 3
|
||||
? 'lg:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 2
|
||||
? 'sm:grid-cols-2 '
|
||||
: ''
|
||||
}`,
|
||||
containerClass
|
||||
)}
|
||||
>
|
||||
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
||||
<div>
|
||||
<div class={twMerge('flex flex-row max-w-md', panelClass, itemClasses?.panel)}>
|
||||
<div class="flex justify-center">
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon
|
||||
name={icon || defaultIcon}
|
||||
class={twMerge('w-7 h-7 mr-2 rtl:mr-0 rtl:ml-2', defaultIconClass, itemClasses?.icon)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div class="mt-0.5">
|
||||
{title && <h3 class={twMerge('text-xl font-bold', titleClass, itemClasses?.title)}>{title}</h3>}
|
||||
{description && (
|
||||
<p
|
||||
class={twMerge(`${title ? 'mt-3' : ''} text-muted`, descriptionClass, itemClasses?.description)}
|
||||
set:html={description}
|
||||
/>
|
||||
)}
|
||||
{callToAction && (
|
||||
<div class={twMerge(`${title || description ? 'mt-3' : ''}`, actionClass, itemClasses?.actionClass)}>
|
||||
<Button variant="link" {...callToAction} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
53
src/components/ui/ItemGrid2.astro
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
import type { ItemGrid as Props } from '~/types';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Button from './Button.astro';
|
||||
|
||||
const { items = [], columns, defaultIcon = '', classes = {} } = Astro.props;
|
||||
|
||||
const {
|
||||
container: containerClass = '',
|
||||
// container: containerClass = "sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
|
||||
panel: panelClass = '',
|
||||
title: titleClass = '',
|
||||
description: descriptionClass = '',
|
||||
icon: defaultIconClass = 'text-primary',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && (
|
||||
<div
|
||||
class={twMerge(
|
||||
`grid gap-8 gap-x-12 sm:gap-y-8 ${
|
||||
columns === 4
|
||||
? 'lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 3
|
||||
? 'lg:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 2
|
||||
? 'sm:grid-cols-2 '
|
||||
: ''
|
||||
}`,
|
||||
containerClass
|
||||
)}
|
||||
>
|
||||
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
||||
<div class={twMerge('relative flex flex-col', panelClass, itemClasses?.panel)}>
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon name={icon || defaultIcon} class={twMerge('mb-2 w-10 h-10', defaultIconClass, itemClasses?.icon)} />
|
||||
)}
|
||||
<div class={twMerge('text-xl font-bold', titleClass, itemClasses?.title)}>{title}</div>
|
||||
{description && (
|
||||
<p class={twMerge('text-muted mt-2', descriptionClass, itemClasses?.description)} set:html={description} />
|
||||
)}
|
||||
{callToAction && (
|
||||
<div class="mt-2">
|
||||
<Button {...callToAction} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
54
src/components/ui/Timeline.astro
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { Item } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
items?: Array<Item>;
|
||||
defaultIcon?: string;
|
||||
classes?: Record<string, string>;
|
||||
}
|
||||
|
||||
const { items = [], classes = {}, defaultIcon } = Astro.props as Props;
|
||||
|
||||
const {
|
||||
container: containerClass = '',
|
||||
panel: panelClass = '',
|
||||
title: titleClass = '',
|
||||
description: descriptionClass = '',
|
||||
icon: defaultIconClass = 'text-primary dark:text-slate-200 border-primary dark:border-blue-700',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && items.length && (
|
||||
<div class={containerClass}>
|
||||
{items.map(({ title, description, icon, classes: itemClasses = {} }, index = 0) => (
|
||||
<div class={twMerge('flex', panelClass, itemClasses?.panel)}>
|
||||
<div class="flex flex-col items-center mr-4 rtl:mr-0 rtl:ml-4">
|
||||
<div>
|
||||
<div class="flex items-center justify-center">
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon
|
||||
name={icon || defaultIcon}
|
||||
class={twMerge('w-10 h-10 p-2 rounded-full border-2', defaultIconClass, itemClasses?.icon)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{index !== items.length - 1 && <div class="w-px h-full bg-black/10 dark:bg-slate-400/50" />}
|
||||
</div>
|
||||
<div class={`pt-1 ${index !== items.length - 1 ? 'pb-8' : ''}`}>
|
||||
{title && <p class={twMerge('text-xl font-bold', titleClass, itemClasses?.title)} set:html={title} />}
|
||||
{description && (
|
||||
<p
|
||||
class={twMerge('text-muted mt-2', descriptionClass, itemClasses?.description)}
|
||||
set:html={description}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
31
src/components/ui/WidgetWrapper.astro
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
import type { HTMLTag } from 'astro/types';
|
||||
import type { Widget } from '~/types';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Background from './Background.astro';
|
||||
|
||||
export interface Props extends Widget {
|
||||
containerClass?: string;
|
||||
['as']?: HTMLTag;
|
||||
}
|
||||
|
||||
const { id, isDark = false, containerClass = '', bg, as = 'section' } = Astro.props;
|
||||
|
||||
const WrapperTag = as;
|
||||
---
|
||||
|
||||
<WrapperTag class="relative not-prose scroll-mt-[72px]" {...id ? { id } : {}}>
|
||||
<div class="absolute inset-0 pointer-events-none -z-[1]" aria-hidden="true">
|
||||
<slot name="bg">
|
||||
{bg ? <Fragment set:html={bg} /> : <Background isDark={isDark} />}
|
||||
</slot>
|
||||
</div>
|
||||
<div
|
||||
class:list={[
|
||||
twMerge('relative mx-auto max-w-7xl px-4 md:px-6 py-12 md:py-16 lg:py-20 text-default', containerClass),
|
||||
{ dark: isDark },
|
||||
]}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</WrapperTag>
|
23
src/components/widgets/Announcement.astro
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div
|
||||
class="dark text-muted text-sm bg-black dark:bg-transparent dark:border-b dark:border-slate-800 dark:text-slate-400 hidden md:flex gap-1 overflow-hidden px-3 py-2 relative text-ellipsis whitespace-nowrap"
|
||||
>
|
||||
<span
|
||||
class="dark:bg-slate-700 bg-white/40 dark:text-slate-300 font-semibold px-1 py-0.5 text-xs mr-0.5 rtl:mr-0 rtl:ml-0.5 inline-block"
|
||||
>NEW</span
|
||||
>
|
||||
<a href="https://astro.build/blog/astro-4110/" class="text-muted hover:underline dark:text-slate-400 font-medium"
|
||||
>Astro 4.11 is now available! »</a
|
||||
>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="ltr:ml-auto rtl:mr-auto w-[5.6rem] h-[1.25rem] ml-auto bg-contain inline-block bg-[url(https://img.shields.io/github/stars/onwidget/astrowind.svg?style=social&label=Stars&maxAge=86400)]"
|
||||
title="If you like AstroWind, give us a star."
|
||||
href="https://github.com/onwidget/astrowind"
|
||||
>
|
||||
</a>
|
||||
</div>
|
64
src/components/widgets/BlogHighlightedPosts.astro
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
import { APP_BLOG } from 'astrowind:config';
|
||||
|
||||
import Grid from '~/components/blog/Grid.astro';
|
||||
|
||||
import { getBlogPermalink } from '~/utils/permalinks';
|
||||
import { findPostsByIds } from '~/utils/blog';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import type { Widget } from '~/types';
|
||||
|
||||
export interface Props extends Widget {
|
||||
title?: string;
|
||||
linkText?: string;
|
||||
linkUrl?: string | URL;
|
||||
information?: string;
|
||||
postIds: string[];
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
linkText = 'View all posts',
|
||||
linkUrl = getBlogPermalink(),
|
||||
information = await Astro.slots.render('information'),
|
||||
postIds = [],
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
|
||||
const posts = APP_BLOG.isEnabled ? await findPostsByIds(postIds) : [];
|
||||
---
|
||||
|
||||
{
|
||||
APP_BLOG.isEnabled ? (
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={classes?.container as string} bg={bg}>
|
||||
<div class="flex flex-col lg:justify-between lg:flex-row mb-8">
|
||||
{title && (
|
||||
<div class="md:max-w-sm">
|
||||
<h2
|
||||
class="text-3xl font-bold tracking-tight sm:text-4xl sm:leading-none group font-heading mb-2"
|
||||
set:html={title}
|
||||
/>
|
||||
{APP_BLOG.list.isEnabled && linkText && linkUrl && (
|
||||
<a
|
||||
class="text-muted dark:text-slate-400 hover:text-primary transition ease-in duration-200 block mb-6 lg:mb-0"
|
||||
href={linkUrl}
|
||||
>
|
||||
{linkText} »
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{information && <p class="text-muted dark:text-slate-400 lg:text-sm lg:max-w-md" set:html={information} />}
|
||||
</div>
|
||||
|
||||
<Grid posts={posts} />
|
||||
</WidgetWrapper>
|
||||
) : (
|
||||
<Fragment />
|
||||
)
|
||||
}
|
63
src/components/widgets/BlogLatestPosts.astro
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
import { APP_BLOG } from 'astrowind:config';
|
||||
|
||||
import Grid from '~/components/blog/Grid.astro';
|
||||
|
||||
import { getBlogPermalink } from '~/utils/permalinks';
|
||||
import { findLatestPosts } from '~/utils/blog';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import type { Widget } from '~/types';
|
||||
import Button from '../ui/Button.astro';
|
||||
|
||||
export interface Props extends Widget {
|
||||
title?: string;
|
||||
linkText?: string;
|
||||
linkUrl?: string | URL;
|
||||
information?: string;
|
||||
count?: number;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
linkText = 'View all posts',
|
||||
linkUrl = getBlogPermalink(),
|
||||
information = await Astro.slots.render('information'),
|
||||
count = 4,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
|
||||
const posts = APP_BLOG.isEnabled ? await findLatestPosts({ count }) : [];
|
||||
---
|
||||
|
||||
{
|
||||
APP_BLOG.isEnabled ? (
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={classes?.container as string} bg={bg}>
|
||||
<div class="flex flex-col lg:justify-between lg:flex-row mb-8">
|
||||
{title && (
|
||||
<div class="md:max-w-sm">
|
||||
<h2
|
||||
class="text-3xl font-bold tracking-tight sm:text-4xl sm:leading-none group font-heading mb-2"
|
||||
set:html={title}
|
||||
/>
|
||||
{APP_BLOG.list.isEnabled && linkText && linkUrl && (
|
||||
<Button variant="link" href={linkUrl}>
|
||||
{' '}
|
||||
{linkText} »
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{information && <p class="text-muted dark:text-slate-400 lg:text-sm lg:max-w-md" set:html={information} />}
|
||||
</div>
|
||||
|
||||
<Grid posts={posts} />
|
||||
</WidgetWrapper>
|
||||
) : (
|
||||
<Fragment />
|
||||
)
|
||||
}
|
38
src/components/widgets/Brands.astro
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import type { Brands as Props } from '~/types';
|
||||
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
const {
|
||||
title = '',
|
||||
subtitle = '',
|
||||
tagline = '',
|
||||
icons = [],
|
||||
images = [],
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-6xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} />
|
||||
|
||||
<div class="flex flex-wrap justify-center gap-x-6 sm:gap-x-12 lg:gap-x-24">
|
||||
{icons && icons.map((icon) => <Icon name={icon} class="py-3 lg:py-5 w-12 h-auto mx-auto sm:mx-0 text-gray-500" />)}
|
||||
{
|
||||
images &&
|
||||
images.map(
|
||||
(image) =>
|
||||
image.src && (
|
||||
<div class="flex justify-center col-span-1 my-2 lg:my-4 py-1 px-3 rounded-md dark:bg-gray-200">
|
||||
<Image src={image.src} alt={image.alt || ''} class="max-h-12" width={120} height={48} layout="fixed" />
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</WidgetWrapper>
|
58
src/components/widgets/CallToAction.astro
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
import WidgetWrapper from '../ui/WidgetWrapper.astro';
|
||||
import type { CallToAction, Widget } from '~/types';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
interface Props extends Widget {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
tagline?: string;
|
||||
callToAction?: CallToAction;
|
||||
actions?: string | CallToAction[];
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline = await Astro.slots.render('tagline'),
|
||||
actions = await Astro.slots.render('actions'),
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-6xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<div
|
||||
class="max-w-3xl mx-auto text-center p-6 rounded-md shadow-xl dark:shadow-none dark:border dark:border-slate-600"
|
||||
>
|
||||
<Headline
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
tagline={tagline}
|
||||
classes={{
|
||||
container: 'mb-0 md:mb-0',
|
||||
title: 'text-4xl md:text-4xl font-bold tracking-tighter mb-4 font-heading',
|
||||
subtitle: 'text-xl text-muted dark:text-slate-400',
|
||||
}}
|
||||
/>
|
||||
{
|
||||
actions && (
|
||||
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4 mt-6">
|
||||
{Array.isArray(actions) ? (
|
||||
actions.map((action) => (
|
||||
<div class="flex w-full sm:w-auto">
|
||||
<Button {...(action || {})} class="w-full sm:mb-0" />
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Fragment set:html={actions} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</WidgetWrapper>
|
43
src/components/widgets/Contact.astro
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
import FormContainer from '~/components/ui/Form.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import type { Contact as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline = await Astro.slots.render('tagline'),
|
||||
inputs,
|
||||
textarea,
|
||||
disclaimer,
|
||||
button,
|
||||
description,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} />
|
||||
|
||||
{
|
||||
inputs && (
|
||||
<div class="flex flex-col max-w-xl mx-auto rounded-lg backdrop-blur border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900 shadow p-4 sm:p-6 lg:p-8 w-full">
|
||||
<FormContainer
|
||||
inputs={inputs}
|
||||
textarea={textarea}
|
||||
disclaimer={disclaimer}
|
||||
button={button}
|
||||
description={description}
|
||||
/>
|
||||
</div>
|
||||
<div class="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]" aria-hidden="true">
|
||||
<div class="aspect-[1108/632] w-[69.25rem] bg-[#80caff] opacity-10" style="clip-path: polygon(26.4% 51.7%, 8.3% 11.8%, 0% 46.4%, 2.6% 82.2%, 7.5% 84.9%, 24.3% 64%, 44.7% 47.5%, 53.5% 49.4%, 55% 62.9%, 49.7% 87.2%, 78.7% 64.1%, 99.9% 100%, 94.6% 51.1%, 78.6% 63.9%, 41.1% 0.2%, 26.4% 51.7%)"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</WidgetWrapper>
|
94
src/components/widgets/Content.astro
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
import type { Content as Props } from '~/types';
|
||||
import Headline from '../ui/Headline.astro';
|
||||
import WidgetWrapper from '../ui/WidgetWrapper.astro';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
import ItemGrid from '../ui/ItemGrid.astro';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
content = await Astro.slots.render('content'),
|
||||
callToAction,
|
||||
items = [],
|
||||
columns,
|
||||
image = await Astro.slots.render('image'),
|
||||
isReversed = false,
|
||||
isAfterContent = false,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper
|
||||
id={id}
|
||||
isDark={isDark}
|
||||
containerClass={`max-w-7xl mx-auto ${isAfterContent ? 'pt-0 md:pt-0 lg:pt-0' : ''} ${classes?.container ?? ''}`}
|
||||
bg={bg}
|
||||
>
|
||||
<Headline
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
tagline={tagline}
|
||||
classes={{
|
||||
container: 'max-w-xl sm:mx-auto lg:max-w-2xl',
|
||||
title: 'text-4xl md:text-5xl font-bold tracking-tighter mb-4 font-heading',
|
||||
subtitle: 'max-w-3xl mx-auto sm:text-center text-xl text-muted dark:text-slate-400',
|
||||
}}
|
||||
/>
|
||||
<div class="mx-auto max-w-7xl p-4 md:px-8">
|
||||
<div class={`md:flex ${isReversed ? 'md:flex-row-reverse' : ''} md:gap-16`}>
|
||||
<div class="md:basis-1/2 self-center">
|
||||
{content && <div class="mb-12 text-lg dark:text-slate-400" set:html={content} />}
|
||||
|
||||
{
|
||||
callToAction && (
|
||||
<div class="mt-[-40px] mb-8 text-primary">
|
||||
<Button variant="link" {...callToAction} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<ItemGrid
|
||||
items={items}
|
||||
columns={columns}
|
||||
defaultIcon="tabler:check"
|
||||
classes={{
|
||||
container: `gap-y-4 md:gap-y-8`,
|
||||
panel: 'max-w-none',
|
||||
title: 'text-lg font-medium leading-6 dark:text-white ml-2 rtl:ml-0 rtl:mr-2',
|
||||
description: 'text-muted dark:text-slate-400 ml-2 rtl:ml-0 rtl:mr-2',
|
||||
icon: 'flex h-7 w-7 items-center justify-center rounded-full bg-green-600 dark:bg-green-700 text-gray-50 p-1',
|
||||
action: 'text-lg font-medium leading-6 dark:text-white ml-2 rtl:ml-0 rtl:mr-2',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div aria-hidden="true" class="mt-10 md:mt-0 md:basis-1/2">
|
||||
{
|
||||
image && (
|
||||
<div class="relative m-auto max-w-4xl">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Image
|
||||
class="mx-auto w-full rounded-lg bg-gray-500 shadow-lg"
|
||||
width={500}
|
||||
height={500}
|
||||
widths={[400, 768]}
|
||||
sizes="(max-width: 768px) 100vw, 432px"
|
||||
layout="responsive"
|
||||
{...image}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WidgetWrapper>
|
33
src/components/widgets/FAQs.astro
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import ItemGrid from '~/components/ui/ItemGrid.astro';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import type { Faqs as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = '',
|
||||
subtitle = '',
|
||||
tagline = '',
|
||||
items = [],
|
||||
columns = 2,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} />
|
||||
<ItemGrid
|
||||
items={items}
|
||||
columns={columns}
|
||||
defaultIcon="tabler:chevron-right"
|
||||
classes={{
|
||||
container: `${columns === 1 ? 'max-w-4xl' : ''} gap-y-8 md:gap-y-12`,
|
||||
panel: 'max-w-none',
|
||||
icon: 'flex-shrink-0 mt-1 w-6 h-6 text-primary',
|
||||
}}
|
||||
/>
|
||||
</WidgetWrapper>
|
41
src/components/widgets/Features.astro
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import ItemGrid from '~/components/ui/ItemGrid.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import type { Features as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline = await Astro.slots.render('tagline'),
|
||||
items = [],
|
||||
columns = 2,
|
||||
|
||||
defaultIcon,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-5xl ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} classes={classes?.headline as Record<string, string>} />
|
||||
<ItemGrid
|
||||
items={items}
|
||||
columns={columns}
|
||||
defaultIcon={defaultIcon}
|
||||
classes={{
|
||||
container: '',
|
||||
title: 'md:text-[1.3rem]',
|
||||
icon: 'text-white bg-primary rounded-full w-10 h-10 p-2 md:w-12 md:h-12 md:p-3 mr-4 rtl:ml-4 rtl:mr-0',
|
||||
...((classes?.items as Record<string, never>) ?? {}),
|
||||
}}
|
||||
/>
|
||||
<div class="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]" aria-hidden="true">
|
||||
<div class="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#89CFF0] to-[#A2CFFE] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
|
||||
</div>
|
||||
</WidgetWrapper>
|
41
src/components/widgets/Features0.astro
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import ItemGrid2 from '~/components/ui/ItemGrid2.astro';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import type { Features as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline = await Astro.slots.render('tagline'),
|
||||
items = [],
|
||||
columns = 3,
|
||||
defaultIcon,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} classes={classes?.headline as Record<string, string>} />
|
||||
<ItemGrid2
|
||||
items={items}
|
||||
columns={columns}
|
||||
defaultIcon={defaultIcon}
|
||||
classes={{
|
||||
container: 'gap-4 md:gap-6',
|
||||
panel:
|
||||
'rounded-lg shadow-[0_4px_30px_rgba(0,0,0,0.1)] dark:shadow-[0_4px_30px_rgba(0,0,0,0.1)] backdrop-blur border border-[#ffffff29] bg-white dark:bg-slate-900 p-6',
|
||||
// panel:
|
||||
// "text-center bg-page items-center md:text-left rtl:md:text-right md:items-start p-6 p-6 rounded-md shadow-xl dark:shadow-none dark:border dark:border-slate-800",
|
||||
icon: 'w-12 h-12 mb-6 text-primary',
|
||||
...((classes?.items as Record<string, never>) ?? {}),
|
||||
}}
|
||||
/>
|
||||
</WidgetWrapper>
|
38
src/components/widgets/Features2.astro
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import ItemGrid2 from '~/components/ui/ItemGrid2.astro';
|
||||
import type { Features as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline = await Astro.slots.render('tagline'),
|
||||
items = [],
|
||||
columns = 3,
|
||||
defaultIcon,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} classes={classes?.headline as Record<string, string>} />
|
||||
<ItemGrid2
|
||||
items={items}
|
||||
columns={columns}
|
||||
defaultIcon={defaultIcon}
|
||||
classes={{
|
||||
container: 'gap-4 md:gap-6',
|
||||
panel:
|
||||
'rounded-lg shadow-[0_4px_30px_rgba(0,0,0,0.1)] dark:shadow-[0_4px_30px_rgba(0,0,0,0.1)] backdrop-blur border border-[#ffffff29] bg-white dark:bg-slate-900 p-6',
|
||||
// panel:
|
||||
// "text-center bg-page items-center md:text-left rtl:md:text-right md:items-start p-6 p-6 rounded-md shadow-xl dark:shadow-none dark:border dark:border-slate-800",
|
||||
icon: 'w-12 h-12 mb-6 text-primary',
|
||||
...((classes?.items as Record<string, never>) ?? {}),
|
||||
}}
|
||||
/>
|
||||
</WidgetWrapper>
|
70
src/components/widgets/Features3.astro
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import ItemGrid from '~/components/ui/ItemGrid.astro';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import type { Features as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline = await Astro.slots.render('tagline'),
|
||||
image,
|
||||
items = [],
|
||||
columns,
|
||||
defaultIcon,
|
||||
isBeforeContent,
|
||||
isAfterContent,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper
|
||||
id={id}
|
||||
isDark={isDark}
|
||||
containerClass={`${isBeforeContent ? 'md:pb-8 lg:pb-12' : ''} ${isAfterContent ? 'pt-0 md:pt-0 lg:pt-0' : ''} ${
|
||||
classes?.container ?? ''
|
||||
}`}
|
||||
bg={bg}
|
||||
>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} classes={classes?.headline as Record<string, string>} />
|
||||
|
||||
<div aria-hidden="true" class="aspect-w-16 aspect-h-7">
|
||||
{
|
||||
image && (
|
||||
<div class="w-full h-80 object-cover rounded-xl mx-auto bg-gray-500 shadow-lg">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Image
|
||||
class="w-full h-80 object-cover rounded-xl mx-auto bg-gray-500 shadow-lg"
|
||||
width="auto"
|
||||
height={320}
|
||||
widths={[400, 768]}
|
||||
layout="fullWidth"
|
||||
{...image}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<ItemGrid
|
||||
items={items}
|
||||
columns={columns}
|
||||
defaultIcon={defaultIcon}
|
||||
classes={{
|
||||
container: 'mt-12',
|
||||
panel: 'max-w-full sm:max-w-md',
|
||||
title: 'text-lg font-semibold',
|
||||
description: 'mt-0.5',
|
||||
icon: 'flex-shrink-0 mt-1 text-primary w-6 h-6',
|
||||
...((classes?.items as object) ?? {}),
|
||||
}}
|
||||
/>
|
||||
</WidgetWrapper>
|
102
src/components/widgets/Footer.astro
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { SITE } from 'astrowind:config';
|
||||
import { getHomePermalink } from '~/utils/permalinks';
|
||||
|
||||
interface Link {
|
||||
text?: string;
|
||||
href?: string;
|
||||
ariaLabel?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
interface Links {
|
||||
title?: string;
|
||||
links: Array<Link>;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
links: Array<Links>;
|
||||
secondaryLinks: Array<Link>;
|
||||
socialLinks: Array<Link>;
|
||||
footNote?: string;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const { socialLinks = [], secondaryLinks = [], links = [], footNote = '', theme = 'light' } = Astro.props;
|
||||
---
|
||||
|
||||
<footer class:list={[{ dark: theme === 'dark' }, 'relative border-t border-gray-200 dark:border-slate-800 not-prose']}>
|
||||
<div class="dark:bg-dark absolute inset-0 pointer-events-none" aria-hidden="true"></div>
|
||||
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 dark:text-slate-300">
|
||||
<div class="grid grid-cols-12 gap-4 gap-y-8 sm:gap-8 py-8 md:py-12">
|
||||
<div class="col-span-12 lg:col-span-4">
|
||||
<div class="mb-2">
|
||||
<a class="inline-block font-bold text-xl" href={getHomePermalink()}>{SITE?.name}</a>
|
||||
</div>
|
||||
<div class="text-sm text-muted flex gap-1">
|
||||
{
|
||||
secondaryLinks.map(({ text, href }, index) => (
|
||||
<>
|
||||
{index !== 0 ? ' · ' : ''}
|
||||
<a
|
||||
class="text-muted hover:text-gray-700 dark:text-gray-400 hover:underline transition duration-150 ease-in-out"
|
||||
href={href}
|
||||
set:html={text}
|
||||
/>
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
links.map(({ title, links }) => (
|
||||
<div class="col-span-6 md:col-span-3 lg:col-span-2">
|
||||
<div class="dark:text-gray-300 font-medium mb-2">{title}</div>
|
||||
{links && Array.isArray(links) && links.length > 0 && (
|
||||
<ul class="text-sm">
|
||||
{links.map(({ text, href, ariaLabel }) => (
|
||||
<li class="mb-2">
|
||||
<a
|
||||
class="text-muted hover:text-gray-700 hover:underline dark:text-gray-400 transition duration-150 ease-in-out"
|
||||
href={href}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<Fragment set:html={text} />
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div class="md:flex md:items-center md:justify-between py-6 md:py-8">
|
||||
{
|
||||
socialLinks?.length ? (
|
||||
<ul class="flex mb-4 md:order-1 -ml-2 md:ml-4 md:mb-0 rtl:ml-0 rtl:-mr-2 rtl:md:ml-0 rtl:md:mr-4">
|
||||
{socialLinks.map(({ ariaLabel, href, text, icon }) => (
|
||||
<li>
|
||||
<a
|
||||
class="text-muted dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center"
|
||||
aria-label={ariaLabel}
|
||||
href={href}
|
||||
>
|
||||
{icon && <Icon name={icon} class="w-5 h-5" />}
|
||||
<Fragment set:html={text} />
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
|
||||
<div class="text-sm mr-4 dark:text-muted">
|
||||
<Fragment set:html={footNote} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
166
src/components/widgets/Header.astro
Normal file
@@ -0,0 +1,166 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import Logo from '~/components/Logo.astro';
|
||||
import ToggleTheme from '~/components/common/ToggleTheme.astro';
|
||||
import ToggleMenu from '~/components/common/ToggleMenu.astro';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
import { getHomePermalink } from '~/utils/permalinks';
|
||||
import { trimSlash, getAsset } from '~/utils/permalinks';
|
||||
import type { CallToAction } from '~/types';
|
||||
|
||||
interface Link {
|
||||
text?: string;
|
||||
href?: string;
|
||||
ariaLabel?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
interface ActionLink extends CallToAction {}
|
||||
|
||||
interface MenuLink extends Link {
|
||||
links?: Array<MenuLink>;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
id?: string;
|
||||
links?: Array<MenuLink>;
|
||||
actions?: Array<ActionLink>;
|
||||
isSticky?: boolean;
|
||||
isDark?: boolean;
|
||||
isFullWidth?: boolean;
|
||||
showToggleTheme?: boolean;
|
||||
showRssFeed?: boolean;
|
||||
position?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
id = 'header',
|
||||
links = [],
|
||||
actions = [],
|
||||
isSticky = false,
|
||||
isDark = false,
|
||||
isFullWidth = false,
|
||||
showToggleTheme = false,
|
||||
showRssFeed = false,
|
||||
position = 'center',
|
||||
} = Astro.props;
|
||||
|
||||
const currentPath = `/${trimSlash(new URL(Astro.url).pathname)}`;
|
||||
---
|
||||
|
||||
<header
|
||||
class:list={[
|
||||
{ sticky: isSticky, relative: !isSticky, dark: isDark },
|
||||
'top-0 z-40 flex-none mx-auto w-full border-b border-gray-50/0 transition-[opacity] ease-in-out',
|
||||
]}
|
||||
{...isSticky ? { 'data-aw-sticky-header': true } : {}}
|
||||
{...id ? { id } : {}}
|
||||
>
|
||||
<div class="absolute inset-0"></div>
|
||||
<div
|
||||
class:list={[
|
||||
'relative text-default py-3 px-3 md:px-6 mx-auto w-full',
|
||||
{
|
||||
'md:flex md:justify-between': position !== 'center',
|
||||
},
|
||||
{
|
||||
'md:grid md:grid-cols-3 md:items-center': position === 'center',
|
||||
},
|
||||
{
|
||||
'max-w-7xl': !isFullWidth,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<div class:list={[{ 'mr-auto rtl:mr-0 rtl:ml-auto': position === 'right' }, 'flex justify-between']}>
|
||||
<a class="flex items-center" href={getHomePermalink()}>
|
||||
<Logo />
|
||||
</a>
|
||||
<div class="flex items-center md:hidden">
|
||||
<ToggleMenu />
|
||||
</div>
|
||||
</div>
|
||||
<nav
|
||||
class="items-center w-full md:w-auto hidden md:flex md:mx-5 text-default overflow-y-auto overflow-x-hidden md:overflow-y-visible md:overflow-x-auto md:justify-self-center"
|
||||
aria-label="Main navigation"
|
||||
>
|
||||
<ul
|
||||
class="flex flex-col md:flex-row md:self-center w-full md:w-auto text-xl md:text-[0.9375rem] tracking-[0.01rem] font-medium md:justify-center"
|
||||
>
|
||||
{
|
||||
links.map(({ text, href, links }) => (
|
||||
<li class={links?.length ? 'dropdown' : ''}>
|
||||
{links?.length ? (
|
||||
<>
|
||||
<button type="button" class="hover:text-link dark:hover:text-white px-4 py-3 flex items-center">
|
||||
{text}{' '}
|
||||
<Icon name="tabler:chevron-down" class="w-3.5 h-3.5 ml-0.5 rtl:ml-0 rtl:mr-0.5 hidden md:inline" />
|
||||
</button>
|
||||
<ul class="dropdown-menu md:backdrop-blur-md dark:md:bg-dark rounded md:absolute pl-4 md:pl-0 md:hidden font-medium md:bg-white/90 md:min-w-[200px] drop-shadow-xl">
|
||||
{links.map(({ text: text2, href: href2 }) => (
|
||||
<li>
|
||||
<a
|
||||
class:list={[
|
||||
'first:rounded-t last:rounded-b md:hover:bg-gray-100 hover:text-link dark:hover:text-white dark:hover:bg-gray-700 py-2 px-5 block whitespace-no-wrap',
|
||||
{ 'aw-link-active': href2 === currentPath },
|
||||
]}
|
||||
href={href2}
|
||||
>
|
||||
{text2}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
) : (
|
||||
<a
|
||||
class:list={[
|
||||
'hover:text-link dark:hover:text-white px-4 py-3 flex items-center',
|
||||
{ 'aw-link-active': href === currentPath },
|
||||
]}
|
||||
href={href}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
<div
|
||||
class:list={[
|
||||
{ 'ml-auto rtl:ml-0 rtl:mr-auto': position === 'left' },
|
||||
'hidden md:self-center md:flex items-center md:mb-0 fixed w-full md:w-auto md:static justify-end left-0 rtl:left-auto rtl:right-0 bottom-0 p-3 md:p-0 md:justify-self-end',
|
||||
]}
|
||||
>
|
||||
<div class="items-center flex justify-between w-full md:w-auto">
|
||||
<div class="flex">
|
||||
{showToggleTheme && <ToggleTheme iconClass="w-6 h-6 md:w-5 md:h-5 md:inline-block" />}
|
||||
{
|
||||
showRssFeed && (
|
||||
<a
|
||||
class="text-muted dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center"
|
||||
aria-label="RSS Feed"
|
||||
href={getAsset('/rss.xml')}
|
||||
>
|
||||
<Icon name="tabler:rss" class="w-5 h-5" />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
actions?.length ? (
|
||||
<span class="ml-4 rtl:ml-0 rtl:mr-4">
|
||||
{actions.map((btnProps) => (
|
||||
<Button {...btnProps} class="ml-2 py-2.5 px-5.5 md:px-6 font-semibold shadow-none text-sm w-auto" />
|
||||
))}
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
99
src/components/widgets/Hero.astro
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
import Background from '~/components/ui/Background.astro';
|
||||
|
||||
|
||||
import type { Hero as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
|
||||
content = await Astro.slots.render('content'),
|
||||
actions = await Astro.slots.render('actions'),
|
||||
image = await Astro.slots.render('image'),
|
||||
|
||||
id,
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class="relative md:-mt-[76px] not-prose" {...id ? { id } : {}}>
|
||||
<div class="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]" aria-hidden="true">
|
||||
<div class="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#89CFF0] to-[#A2CFFE] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
|
||||
</div>
|
||||
<div class="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]" aria-hidden="true">
|
||||
<div class="aspect-[1108/632] w-[69.25rem] bg-[#80caff] opacity-10" style="clip-path: polygon(26.4% 51.7%, 8.3% 11.8%, 0% 46.4%, 2.6% 82.2%, 7.5% 84.9%, 24.3% 64%, 44.7% 47.5%, 53.5% 49.4%, 55% 62.9%, 49.7% 87.2%, 78.7% 64.1%, 99.9% 100%, 94.6% 51.1%, 78.6% 63.9%, 41.1% 0.2%, 26.4% 51.7%)"></div>
|
||||
</div>
|
||||
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
|
||||
<slot name="bg">
|
||||
{bg ? <Fragment set:html={bg} /> : null}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="relative max-w-7xl mx-auto px-4 sm:px-6">
|
||||
<div class="pt-0 md:pt-[76px] pointer-events-none"></div>
|
||||
<div class="py-12 md:py-20">
|
||||
<div class="text-center pb-10 md:pb-16 max-w-5xl mx-auto">
|
||||
<div>
|
||||
{
|
||||
image && (
|
||||
<div class="relative m-auto max-w-5xl">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Image
|
||||
class="mx-auto rounded-md w-full"
|
||||
widths={[100, 192, 256, 510]}
|
||||
sizes="(max-width: 767px) 100px, (max-width: 1023px) 192px, (max-width: 2039px) 256px, 510px"
|
||||
loading="eager"
|
||||
width={256}
|
||||
height={256}
|
||||
{...image}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
tagline && (
|
||||
<p
|
||||
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase"
|
||||
set:html={tagline}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
title && (
|
||||
<h1
|
||||
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
|
||||
set:html={title}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div class="max-w-3xl mx-auto">
|
||||
{subtitle && <p class="text-xl text-muted mb-6 dark:text-slate-300" set:html={subtitle} />}
|
||||
{
|
||||
actions && (
|
||||
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4">
|
||||
{Array.isArray(actions) ? (
|
||||
actions.map((action) => (
|
||||
<div class="flex w-full sm:w-auto">
|
||||
<Button {...(action || {})} class="w-full sm:mb-0" />
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Fragment set:html={actions} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{content && <Fragment set:html={content} />}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
95
src/components/widgets/Hero2.astro
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
import Background from '~/components/ui/Background.astro';
|
||||
|
||||
import type { Hero as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
|
||||
content = await Astro.slots.render('content'),
|
||||
actions = await Astro.slots.render('actions'),
|
||||
image = await Astro.slots.render('image'),
|
||||
|
||||
id,
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class="relative md:-mt-[76px] not-prose" {...id ? { id } : {}}>
|
||||
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
|
||||
<div class="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]" aria-hidden="true">
|
||||
<div class="aspect-[1108/632] w-[69.25rem] bg-[#80caff] opacity-10" style="clip-path: polygon(26.4% 51.7%, 8.3% 11.8%, 0% 46.4%, 2.6% 82.2%, 7.5% 84.9%, 24.3% 64%, 44.7% 47.5%, 53.5% 49.4%, 55% 62.9%, 49.7% 87.2%, 78.7% 64.1%, 99.9% 100%, 94.6% 51.1%, 78.6% 63.9%, 41.1% 0.2%, 26.4% 51.7%)"></div>
|
||||
</div>
|
||||
<slot name="bg">
|
||||
{bg ? <Fragment set:html={bg} /> : null}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="relative max-w-7xl mx-auto px-4 sm:px-6">
|
||||
<div class="pt-0 md:pt-[76px] pointer-events-none"></div>
|
||||
<div class="py-12 md:py-20 lg:py-0 lg:flex lg:items-center lg:h-screen lg:gap-8">
|
||||
<div class="basis-1/2 text-center lg:text-left pb-10 md:pb-16 mx-auto">
|
||||
{
|
||||
tagline && (
|
||||
<p
|
||||
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase"
|
||||
set:html={tagline}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
title && (
|
||||
<h1
|
||||
class="text-3xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
|
||||
set:html={title}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div class="max-w-3xl mx-auto lg:max-w-none">
|
||||
{subtitle && <p class="text-xl text-muted mb-6 dark:text-slate-300" set:html={subtitle} />}
|
||||
|
||||
{
|
||||
actions && (
|
||||
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4 lg:justify-start lg:m-0 lg:max-w-7xl">
|
||||
{Array.isArray(actions) ? (
|
||||
actions.map((action) => (
|
||||
<div class="flex w-full sm:w-auto">
|
||||
<Button {...(action || {})} class="w-full sm:mb-0" />
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Fragment set:html={actions} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{content && <Fragment set:html={content} />}
|
||||
</div>
|
||||
<div class="basis-1/2">
|
||||
{
|
||||
image && (
|
||||
<div class="relative m-auto max-w-5xl">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Image
|
||||
class="mx-auto rounded-md w-full"
|
||||
widths={[400, 768, 1024, 2040]}
|
||||
sizes="(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px"
|
||||
loading="eager"
|
||||
width={600}
|
||||
height={600}
|
||||
{...image}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
77
src/components/widgets/HeroText.astro
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
import type { CallToAction } from '~/types';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
tagline?: string;
|
||||
content?: string;
|
||||
callToAction?: string | CallToAction;
|
||||
callToAction2?: string | CallToAction;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
content = await Astro.slots.render('content'),
|
||||
callToAction = await Astro.slots.render('callToAction'),
|
||||
callToAction2 = await Astro.slots.render('callToAction2'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class="relative md:-mt-[76px] not-prose">
|
||||
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"></div>
|
||||
<div class="relative max-w-7xl mx-auto px-4 sm:px-6">
|
||||
<div class="pt-0 md:pt-[76px] pointer-events-none"></div>
|
||||
<div class="py-12 md:py-20 pb-8 md:pb-8">
|
||||
<div class="text-center max-w-5xl mx-auto">
|
||||
{
|
||||
tagline && (
|
||||
<p
|
||||
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase"
|
||||
set:html={tagline}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
title && (
|
||||
<h1
|
||||
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
|
||||
set:html={title}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div class="max-w-3xl mx-auto">
|
||||
{subtitle && <p class="text-xl text-muted mb-6 dark:text-slate-300" set:html={subtitle} />}
|
||||
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4">
|
||||
{
|
||||
callToAction && (
|
||||
<div class="flex w-full sm:w-auto">
|
||||
{typeof callToAction === 'string' ? (
|
||||
<Fragment set:html={callToAction} />
|
||||
) : (
|
||||
<Button variant="primary" {...callToAction} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
callToAction2 && (
|
||||
<div class="flex w-full sm:w-auto">
|
||||
{typeof callToAction2 === 'string' ? (
|
||||
<Fragment set:html={callToAction2} />
|
||||
) : (
|
||||
<Button {...callToAction2} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{content && <Fragment set:html={content} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
11
src/components/widgets/Note.astro
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
---
|
||||
|
||||
<section class="bg-blue-50 dark:bg-slate-800 not-prose">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-4 text-md text-center font-medium">
|
||||
<span class="font-bold">
|
||||
<Icon name="tabler:info-square" class="w-5 h-5 inline-block align-text-bottom" /> Philosophy:</span
|
||||
> Simplicity, Best Practices and High Performance
|
||||
</div>
|
||||
</section>
|
83
src/components/widgets/Pricing.astro
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import type { Pricing as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = '',
|
||||
subtitle = '',
|
||||
tagline = '',
|
||||
prices = [],
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} />
|
||||
<div class="flex items-stretch justify-center">
|
||||
<div class="grid grid-cols-3 gap-4 dark:text-white sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3">
|
||||
{
|
||||
prices &&
|
||||
prices.map(({ title, subtitle, price, period, items, callToAction, hasRibbon = false, ribbonTitle }) => (
|
||||
<div class="col-span-3 mx-auto flex w-full sm:col-span-1 md:col-span-1 lg:col-span-1 xl:col-span-1">
|
||||
{price && period && (
|
||||
<div class="rounded-lg backdrop-blur border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900 shadow px-6 py-8 flex w-full max-w-sm flex-col justify-between text-center">
|
||||
{hasRibbon && ribbonTitle && (
|
||||
<div class="absolute right-[-5px] 2xl:right-[-8px] rtl:right-auto rtl:left-[-8px] rtl:2xl:left-[-10px] top-[-5px] 2xl:top-[-10px] z-[1] h-[100px] w-[100px] overflow-hidden text-right">
|
||||
<span class="absolute top-[19px] right-[-21px] rtl:right-auto rtl:left-[-21px] block w-full rotate-45 rtl:-rotate-45 bg-green-700 text-center text-[10px] font-bold uppercase leading-5 text-white shadow-[0_3px_10px_-5px_rgba(0,0,0,0.3)] before:absolute before:left-0 before:top-full before:z-[-1] before:border-[3px] before:border-r-transparent before:border-b-transparent before:border-l-green-800 before:border-t-green-800 before:content-[''] after:absolute after:right-0 after:top-full after:z-[-1] after:border-[3px] after:border-l-transparent after:border-b-transparent after:border-r-green-800 after:border-t-green-800 after:content-['']">
|
||||
{ribbonTitle}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div class="px-2 py-0">
|
||||
{title && (
|
||||
<h3 class="text-center text-xl font-semibold uppercase leading-6 tracking-wider mb-2">{title}</h3>
|
||||
)}
|
||||
{subtitle && <p class="font-light sm:text-lg text-gray-600 dark:text-slate-400">{subtitle}</p>}
|
||||
<div class="my-8">
|
||||
<div class="flex items-center justify-center text-center mb-1">
|
||||
<span class="text-5xl">$</span>
|
||||
<span class="text-6xl font-extrabold">{price}</span>
|
||||
</div>
|
||||
<span class="text-base leading-6 lowercase text-gray-600 dark:text-slate-400">{period}</span>
|
||||
</div>
|
||||
{items && (
|
||||
<ul class="my-8 md:my-10 space-y-2 text-left">
|
||||
{items.map(
|
||||
({ description, icon }) =>
|
||||
description && (
|
||||
<li class="mb-1.5 flex items-start space-x-3 leading-7">
|
||||
<div class="rounded-full bg-primary mt-1">
|
||||
<Icon name={icon ? icon : 'tabler:check'} class="w-5 h-5 font-bold p-1 text-white" />
|
||||
</div>
|
||||
<span>{description}</span>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
{callToAction && (
|
||||
<div class={`flex justify-center`}>
|
||||
{typeof callToAction === 'string' ? (
|
||||
<Fragment set:html={callToAction} />
|
||||
) : (
|
||||
callToAction &&
|
||||
callToAction.href && <Button {...(hasRibbon ? { variant: 'primary' } : {})} {...callToAction} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</WidgetWrapper>
|
46
src/components/widgets/Stats.astro
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
import type { Stats as Props } from '~/types';
|
||||
import WidgetWrapper from '../ui/WidgetWrapper.astro';
|
||||
import Headline from '../ui/Headline.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
stats = [],
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-6xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} />
|
||||
<div class="flex flex-wrap justify-center -m-4 text-center">
|
||||
{
|
||||
stats &&
|
||||
stats.map(({ amount, title, icon }) => (
|
||||
<div class="p-4 md:w-1/4 sm:w-1/2 w-full min-w-[220px] text-center md:border-r md:last:border-none dark:md:border-slate-500">
|
||||
{icon && (
|
||||
<div class="flex items-center justify-center mx-auto mb-4 text-primary">
|
||||
<Icon name={icon} class="w-10 h-10" />
|
||||
</div>
|
||||
)}
|
||||
{amount && (
|
||||
<div class="font-heading text-primary text-[2.6rem] font-bold dark:text-white lg:text-5xl xl:text-6xl">
|
||||
{amount}
|
||||
</div>
|
||||
)}
|
||||
{title && (
|
||||
<div class="text-sm font-medium uppercase tracking-widest text-gray-800 dark:text-slate-400 lg:text-base">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</WidgetWrapper>
|
59
src/components/widgets/Steps.astro
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import Timeline from '~/components/ui/Timeline.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import type { Steps as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline = await Astro.slots.render('tagline'),
|
||||
items = [],
|
||||
image = await Astro.slots.render('image'),
|
||||
isReversed = false,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-5xl ${classes?.container ?? ''}`} bg={bg}>
|
||||
<div class:list={['flex flex-col gap-8 md:gap-12', { 'md:flex-row-reverse': isReversed }, { 'md:flex-row': image }]}>
|
||||
<div class:list={['md:py-4 md:self-center', { 'md:basis-1/2': image }, { 'w-full': !image }]}>
|
||||
<Headline
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
tagline={tagline}
|
||||
classes={{
|
||||
container: 'text-left rtl:text-right',
|
||||
title: 'text-3xl lg:text-4xl',
|
||||
...((classes?.headline as object) ?? {}),
|
||||
}}
|
||||
/>
|
||||
<Timeline items={items} classes={classes?.items as Record<string, never>} />
|
||||
</div>
|
||||
{
|
||||
image && (
|
||||
<div class="relative md:basis-1/2">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Image
|
||||
class="inset-0 object-cover object-top w-full rounded-md shadow-lg md:absolute md:h-full bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 768]}
|
||||
sizes="(max-width: 768px) 100vw, 432px"
|
||||
width={432}
|
||||
height={768}
|
||||
layout="cover"
|
||||
src={image?.src}
|
||||
alt={image?.alt || ''}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</WidgetWrapper>
|
75
src/components/widgets/Steps2.astro
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
import type { Steps as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
callToAction = await Astro.slots.render('callToAction'),
|
||||
items = [],
|
||||
isReversed = false,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-6xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<div class="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]" aria-hidden="true">
|
||||
<div class="aspect-[1108/632] w-[69.25rem] bg-[#80caff] opacity-10" style="clip-path: polygon(26.4% 51.7%, 8.3% 11.8%, 0% 46.4%, 2.6% 82.2%, 7.5% 84.9%, 24.3% 64%, 44.7% 47.5%, 53.5% 49.4%, 55% 62.9%, 49.7% 87.2%, 78.7% 64.1%, 99.9% 100%, 94.6% 51.1%, 78.6% 63.9%, 41.1% 0.2%, 26.4% 51.7%)"></div>
|
||||
</div>
|
||||
<div class={`flex flex-col gap-8 md:gap-12 md:flex-row ${isReversed ? 'md:flex-row-reverse' : ''}`}>
|
||||
<div class={`w-full lg:w-1/2 gap-8 md:gap-12 ${isReversed ? 'lg:ml-16 md:ml-8 ml-0' : 'lg:mr-16 md:mr-8 mr-0'}`}>
|
||||
<Headline
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
tagline={tagline}
|
||||
classes={{
|
||||
container: 'text-center md:text-left rtl:md:text-right mb-4 md:mb-8',
|
||||
title: 'mb-4 text-3xl lg:text-4xl font-bold font-heading',
|
||||
subtitle: 'mb-8 text-xl text-muted dark:text-slate-400',
|
||||
// ...((classes?.headline as {}) ?? {}),
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="w-full text-center md:text-left rtl:md:text-right">
|
||||
{
|
||||
typeof callToAction === 'string' ? (
|
||||
<Fragment set:html={callToAction} />
|
||||
) : (
|
||||
callToAction &&
|
||||
callToAction.text &&
|
||||
callToAction.href && <Button variant="primary" {...callToAction} class="mb-12 w-auto" />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/2 px-0">
|
||||
<ul class="space-y-10">
|
||||
{
|
||||
items && items.length
|
||||
? items.map(({ title: title2, description, icon }, index) => (
|
||||
<li class="flex md:-mx-4">
|
||||
<div class="pr-4 sm:pl-4 rtl:pr-0 rtl:pl-4 rtl:sm:pl-0 rtl:sm:pr-4">
|
||||
<span class="flex w-16 h-16 mx-auto items-center justify-center text-2xl font-bold rounded-full bg-blue-100 text-primary">
|
||||
{icon ? <Icon name={icon} class="w-6 h-6 icon-bold" /> : index + 1}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pl-4 rtl:pl-0 rtl:pr-4">
|
||||
<h3 class="mb-4 text-xl font-semibold font-heading" set:html={title2} />
|
||||
<p class="text-muted dark:text-gray-400" set:html={description} />
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
: ''
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</WidgetWrapper>
|
75
src/components/widgets/Testimonials.astro
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import type { Testimonials as Props } from '~/types';
|
||||
|
||||
const {
|
||||
title = '',
|
||||
subtitle = '',
|
||||
tagline = '',
|
||||
testimonials = [],
|
||||
callToAction,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render('bg'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-6xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline title={title} subtitle={subtitle} tagline={tagline} />
|
||||
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{
|
||||
testimonials &&
|
||||
testimonials.map(({ title, testimonial, name, job, image }) => (
|
||||
<div class="flex h-auto">
|
||||
<div class="flex flex-col p-4 md:p-6 rounded-md shadow-xl dark:shadow-none dark:border dark:border-slate-600">
|
||||
{title && <h2 class="text-lg font-medium leading-6 pb-4">{title}</h2>}
|
||||
{testimonial && (
|
||||
<blockquote class="flex-auto">
|
||||
<p class="text-muted">" {testimonial} "</p>
|
||||
</blockquote>
|
||||
)}
|
||||
|
||||
<hr class="border-slate-200 dark:border-slate-600 my-4" />
|
||||
|
||||
<div class="flex items-center">
|
||||
{image && (
|
||||
<div class="h-10 w-10 rounded-full border border-slate-200 dark:border-slate-600">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Image
|
||||
class="h-10 w-10 rounded-full border border-slate-200 dark:border-slate-600 min-w-full min-h-full"
|
||||
width={40}
|
||||
height={40}
|
||||
widths={[400, 768]}
|
||||
layout="fixed"
|
||||
{...image}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="grow ml-3 rtl:ml-0 rtl:mr-3">
|
||||
{name && <p class="text-base font-semibold">{name}</p>}
|
||||
{job && <p class="text-xs text-muted">{job}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{
|
||||
callToAction && (
|
||||
<div class="flex justify-center mx-auto w-fit mt-8 md:mt-12 font-medium">
|
||||
<Button {...callToAction} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</WidgetWrapper>
|
72
src/config.yaml
Normal file
@@ -0,0 +1,72 @@
|
||||
site:
|
||||
name: FreeZone
|
||||
site: 'https://astrowind.vercel.app'
|
||||
base: '/'
|
||||
trailingSlash: false
|
||||
|
||||
googleSiteVerificationId: orcPxI47GSa-cRvY11tUe6iGg2IO_RPvnA1q95iEM3M
|
||||
|
||||
# Default SEO metadata
|
||||
metadata:
|
||||
title:
|
||||
default: FreeZone
|
||||
template: '%s — AstroWind'
|
||||
description: "\U0001F680 Suitable for Startups, Small Business, Sass Websites, Professional Portfolios, Marketing Websites, Landing Pages & Blogs."
|
||||
robots:
|
||||
index: true
|
||||
follow: true
|
||||
openGraph:
|
||||
site_name: FreeZone
|
||||
images:
|
||||
- url: '~/assets/images/default.png'
|
||||
width: 1200
|
||||
height: 628
|
||||
type: website
|
||||
twitter:
|
||||
handle: '@onwidget'
|
||||
site: '@onwidget'
|
||||
cardType: summary_large_image
|
||||
|
||||
i18n:
|
||||
language: en
|
||||
textDirection: ltr
|
||||
|
||||
apps:
|
||||
blog:
|
||||
isEnabled: true
|
||||
postsPerPage: 6
|
||||
|
||||
post:
|
||||
isEnabled: true
|
||||
permalink: '/%slug%' # Variables: %slug%, %year%, %month%, %day%, %hour%, %minute%, %second%, %category%
|
||||
robots:
|
||||
index: true
|
||||
|
||||
list:
|
||||
isEnabled: true
|
||||
pathname: 'blog' # Blog main path, you can change this to "articles" (/articles)
|
||||
robots:
|
||||
index: true
|
||||
|
||||
category:
|
||||
isEnabled: true
|
||||
pathname: 'category' # Category main path /category/some-category, you can change this to "group" (/group/some-category)
|
||||
robots:
|
||||
index: true
|
||||
|
||||
tag:
|
||||
isEnabled: true
|
||||
pathname: 'tag' # Tag main path /tag/some-tag, you can change this to "topics" (/topics/some-category)
|
||||
robots:
|
||||
index: false
|
||||
|
||||
isRelatedPostsEnabled: true
|
||||
relatedPostsCount: 4
|
||||
|
||||
analytics:
|
||||
vendors:
|
||||
googleAnalytics:
|
||||
id: null # or "G-XXXXXXXXXX"
|
||||
|
||||
ui:
|
||||
theme: 'system' # Values: "system" | "light" | "dark" | "light:only" | "dark:only"
|
68
src/content/config.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { z, defineCollection } from 'astro:content';
|
||||
|
||||
const metadataDefinition = () =>
|
||||
z
|
||||
.object({
|
||||
title: z.string().optional(),
|
||||
ignoreTitleTemplate: z.boolean().optional(),
|
||||
|
||||
canonical: z.string().url().optional(),
|
||||
|
||||
robots: z
|
||||
.object({
|
||||
index: z.boolean().optional(),
|
||||
follow: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
description: z.string().optional(),
|
||||
|
||||
openGraph: z
|
||||
.object({
|
||||
url: z.string().optional(),
|
||||
siteName: z.string().optional(),
|
||||
images: z
|
||||
.array(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
width: z.number().optional(),
|
||||
height: z.number().optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
locale: z.string().optional(),
|
||||
type: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
twitter: z
|
||||
.object({
|
||||
handle: z.string().optional(),
|
||||
site: z.string().optional(),
|
||||
cardType: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.optional();
|
||||
|
||||
const postCollection = defineCollection({
|
||||
schema: z.object({
|
||||
publishDate: z.date().optional(),
|
||||
updateDate: z.date().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
|
||||
title: z.string(),
|
||||
excerpt: z.string().optional(),
|
||||
image: z.string().optional(),
|
||||
|
||||
category: z.string().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
author: z.string().optional(),
|
||||
|
||||
metadata: metadataDefinition(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
post: postCollection,
|
||||
};
|
26
src/content/post/A-New-Chapter-Unfolds.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
publishDate: 2023-08-23T00:00:00Z
|
||||
author: Sam Taggart
|
||||
title: 🌍 A New Chapter Unfolds - OurWorld Free Zone Joins Forces with Tanzania's Officials
|
||||
excerpt: OurWorld Free Zone Establishes Official Partnership with Zanzibar Government.
|
||||
image: ~/assets/images/newchapter.png
|
||||
category: News
|
||||
tags:
|
||||
- zanzibar
|
||||
- ODFZ
|
||||
metadata:
|
||||
canonical: "https://freezone.ourworld.tf/blog/a-new-chapter-unfolds/"
|
||||
---
|
||||
|
||||
OurWorld Free Zone is excited to announce a significant milestone in its journey towards global collaboration. On July 23 2023, a momentous contract was signed between OurWorld Free Zone and the government of Tanzania, solidifying their official partnership.
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
The signing ceremony, graced by the presence of OurWorld Free Zone's co-founder, Kristof De Spiegeleer, marked a pivotal moment in the organization's expansion efforts. The event was attended by a distinguished gathering, including members of the press, honored guests, and key government officials from both Zanzibar and Tanzania.
|
||||
During the event, discussions were held to outline the next steps in this groundbreaking collaboration. The partnership between OurWorld Free Zone and the Zanzibar government aims to pave the way for new opportunities and mutual growth. The cooperative efforts are poised to foster economic development, innovation, and cooperation between different sectors.
|
||||
|
||||
<br/>
|
||||
|
||||
Stay tuned for more detailed updates on this exciting partnership as OurWorld Free Zone and the government of Zanzibar work together to shape a promising future of collaboration and progress. Further announcements will shed light on the specific initiatives and projects that will emerge from this alliance.
|
28
src/content/post/a-digital-freezone.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
publishDate: 2023-07-23T00:00:00Z
|
||||
author: Sam Taggart
|
||||
title: 📣 A Digital Free Zone in Zanzibar – And More!
|
||||
excerpt: The President of Zanzibar, Dr. Hussein Mwinyi, visited us this weekend to publicly share our collaborations.
|
||||
image: ~/assets/images/digitalfreezonezanzibar.png
|
||||
category: News
|
||||
tags:
|
||||
- zanzibar
|
||||
- ODFZ
|
||||
- Partnership
|
||||
metadata:
|
||||
canonical: "https://freezone.ourworld.tf/blog/a-digital-freezone/"
|
||||
---
|
||||
|
||||
This past weekend, we were honored with a visit from Zanzibar's President Dr. Hussein Mwinyi at the African Regenerative Cities Summit.
|
||||
|
||||
<br/>
|
||||
|
||||
There, he formally and publicly expressed the government's commitment to the establishment of a digital free zone, a locally-owned Internet infrastructure, an incubator to support young local innovators – all in collaboration with ThreeFold – and their commitment to the ICT sector as a whole.
|
||||
|
||||
<br/>
|
||||
|
||||
Read Zanzibar Seeks to Become a Digital FreeZone (via The Citizen) and Mwinyi Commits to Improvement of Digital Spaces (via Daily News Tanzania) for further details.
|
||||
<br/>
|
||||
|
||||
We'll be sharing more developments on this front in the community call next week and as progress unfolds.
|
||||
|
27
src/content/post/african-regenerative-summit.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
publishDate: 2023-07-21T00:00:00Z
|
||||
author: Sam Taggart
|
||||
title: 🌍 African Regenerative Cities Summit
|
||||
excerpt: Learn about the summit we hosted this past weekend in Zanzibar to unite innovators and investors to support Africa's future.
|
||||
image: ~/assets/images/africanregensummit.png
|
||||
category: News
|
||||
tags:
|
||||
- zanzibar
|
||||
- ODFZ
|
||||
- Events
|
||||
metadata:
|
||||
canonical: "https://freezone.ourworld.tf/blog/african-regenerative-summit/"
|
||||
---
|
||||
|
||||
Over the past four days, we have had the pleasure to host the African Regenerative Cities Summit in Zanzibar, Tanzania – uniting top innovators and investors supporting human flourishing, with a special focus on science and technology that will lay the foundation for Africa’s future.
|
||||
|
||||
<br/>
|
||||
|
||||
A lot has happened over the course of the summit, including a surprise visit today from an extremely special guest.
|
||||
|
||||
<br/>
|
||||
|
||||
Generally, we have been blown away by the participants and the projects they are working on, and we warmly welcome many of them now into the ThreeFold community.
|
||||
<br/>
|
||||
|
||||
We'll be sharing more in the coming days.
|
19
src/content/post/in-the-news.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
publishDate: 2023-08-01T00:00:00Z
|
||||
author: Sam Taggart
|
||||
title: In the News – OurWorld Digital Free Zone
|
||||
excerpt: OurWorld Digital Free Zone received some coverage recently from IPPMEDIA. Take a look!
|
||||
image: ~/assets/images/inthenews.png
|
||||
category: News
|
||||
tags:
|
||||
- zanzibar
|
||||
- ODFZ
|
||||
metadata:
|
||||
canonical: "https://freezone.ourworld.tf/blog/in-the-news/"
|
||||
---
|
||||
|
||||
"Our vision is to foster a digital ecosystem that harmoniously blends the tranquility of Zanzibar with the awe-inspiring power of the digital age. I am deeply appreciative of the dedication of the OurWorld Zanzibar team in turning this vision into a reality," Zanzibar President Dr. Hussein Mwinyi.
|
||||
|
||||
<br/>
|
||||
|
||||
Take a look at this piece from IPPMEDIA, which goes into some detail about our collaboration with the Revolutionary Government of Zanzibar and the OurWorld Digital Free Zone.
|
5
src/env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="../vendor/integration/types.d.ts" />
|
35
src/layouts/LandingLayout.astro
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
import PageLayout from '~/layouts/PageLayout.astro';
|
||||
import Header from '~/components/widgets/Header.astro';
|
||||
|
||||
import { headerData } from '~/navigation';
|
||||
import type { MetaData } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
metadata?: MetaData;
|
||||
}
|
||||
|
||||
const { metadata } = Astro.props;
|
||||
---
|
||||
|
||||
<PageLayout metadata={metadata}>
|
||||
<Fragment slot="announcement">
|
||||
<slot name="announcement" />
|
||||
</Fragment>
|
||||
<Fragment slot="header">
|
||||
<slot name="header">
|
||||
<Header
|
||||
links={headerData?.links[2] ? [headerData.links[2]] : undefined}
|
||||
actions={[
|
||||
{
|
||||
text: 'Download',
|
||||
href: 'https://github.com/onwidget/astrowind',
|
||||
},
|
||||
]}
|
||||
showToggleTheme
|
||||
position="right"
|
||||
/>
|
||||
</slot>
|
||||
</Fragment>
|
||||
<slot />
|
||||
</PageLayout>
|
54
src/layouts/Layout.astro
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
import '~/assets/styles/tailwind.css';
|
||||
|
||||
import { I18N } from 'astrowind:config';
|
||||
|
||||
import CommonMeta from '~/components/common/CommonMeta.astro';
|
||||
import Favicons from '~/components/Favicons.astro';
|
||||
import CustomStyles from '~/components/CustomStyles.astro';
|
||||
import ApplyColorMode from '~/components/common/ApplyColorMode.astro';
|
||||
import Metadata from '~/components/common/Metadata.astro';
|
||||
import SiteVerification from '~/components/common/SiteVerification.astro';
|
||||
import Analytics from '~/components/common/Analytics.astro';
|
||||
import BasicScripts from '~/components/common/BasicScripts.astro';
|
||||
|
||||
// Comment the line below to disable View Transitions
|
||||
import { ViewTransitions } from 'astro:transitions';
|
||||
|
||||
import type { MetaData as MetaDataType } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
metadata?: MetaDataType;
|
||||
}
|
||||
|
||||
const { metadata = {} } = Astro.props;
|
||||
const { language, textDirection } = I18N;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang={language} dir={textDirection} class="2xl:text-[20px]">
|
||||
<head>
|
||||
<CommonMeta />
|
||||
<Favicons />
|
||||
<CustomStyles />
|
||||
<ApplyColorMode />
|
||||
<Metadata {...metadata} />
|
||||
<SiteVerification />
|
||||
<Analytics />
|
||||
|
||||
<!-- Comment the line below to disable View Transitions -->
|
||||
<ViewTransitions fallback="swap" />
|
||||
</head>
|
||||
|
||||
<body class="antialiased text-default bg-page tracking-tight">
|
||||
<slot />
|
||||
|
||||
<BasicScripts />
|
||||
|
||||
<style is:global>
|
||||
img {
|
||||
content-visibility: auto;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
28
src/layouts/MarkdownLayout.astro
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
|
||||
import type { MetaData } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
frontmatter: {
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const { frontmatter } = Astro.props;
|
||||
|
||||
const metadata: MetaData = {
|
||||
title: frontmatter?.title,
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<section class="px-4 py-16 sm:px-6 mx-auto lg:px-8 lg:py-20 max-w-4xl">
|
||||
<h1 class="font-bold font-heading text-4xl md:text-5xl leading-tighter tracking-tighter">{frontmatter.title}</h1>
|
||||
<div
|
||||
class="mx-auto prose prose-lg max-w-4xl dark:prose-invert dark:prose-headings:text-slate-300 prose-md prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-img:rounded-md prose-img:shadow-lg mt-8"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
29
src/layouts/PageLayout.astro
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
import Layout from '~/layouts/Layout.astro';
|
||||
import Header from '~/components/widgets/Header.astro';
|
||||
import Footer from '~/components/widgets/Footer.astro';
|
||||
import Announcement from '~/components/widgets/Announcement.astro';
|
||||
|
||||
import { headerData, footerData } from '~/navigation';
|
||||
|
||||
import type { MetaData } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
metadata?: MetaData;
|
||||
}
|
||||
|
||||
const { metadata } = Astro.props;
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
|
||||
<slot name="header">
|
||||
<Header {...headerData} isSticky showRssFeed showToggleTheme />
|
||||
</slot>
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
<slot name="footer">
|
||||
<Footer {...footerData} />
|
||||
</slot>
|
||||
</Layout>
|
99
src/navigation.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import { getPermalink, getBlogPermalink, getAsset } from './utils/permalinks';
|
||||
|
||||
export const headerData = {
|
||||
links: [
|
||||
{
|
||||
text: 'Home',
|
||||
links: [
|
||||
{
|
||||
text: 'About',
|
||||
href: getPermalink('/#about'),
|
||||
},
|
||||
{
|
||||
text: 'Features',
|
||||
href: getPermalink('/#features'),
|
||||
},
|
||||
{
|
||||
text: 'Why Zanzibar',
|
||||
href: getPermalink('#zanzibar'),
|
||||
},
|
||||
{
|
||||
text: 'Benefits',
|
||||
href: getPermalink('#benefits'),
|
||||
},
|
||||
{
|
||||
text: 'Presale',
|
||||
href: getPermalink('#presale'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'FAQ',
|
||||
href: '#faq',
|
||||
},
|
||||
{
|
||||
text: 'Blog',
|
||||
href: '/blog',
|
||||
},
|
||||
{
|
||||
text: 'Docs',
|
||||
href: 'https://ourworldfreezone.github.io/info_freezone/intro/intro_readme.html',
|
||||
target: '_blank',
|
||||
},
|
||||
],
|
||||
actions: [{ text: 'Contact', href: '/contact', target: '_blank' }],
|
||||
};
|
||||
|
||||
export const footerData = {
|
||||
links: [
|
||||
{
|
||||
title: 'OurWorld',
|
||||
links: [
|
||||
{ text: 'OurWorld', href: 'https://ourworld.tf' },
|
||||
{ text: 'ODFZ Demo', href: 'https://ourworld.vindo.ai/slider/95' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Partners',
|
||||
links: [
|
||||
{ text: 'ThreeFold', href: 'https://threefold.io' },
|
||||
{ text: 'Govt of Zanzibar', href: 'https://egaz.go.tz/en/' },
|
||||
{ text: 'Incubaid', href: 'https://incubaid.com/' },
|
||||
{ text: 'Sikana', href: 'https://sikana.tv/' },
|
||||
{ text: 'VVerse', href: 'https://vverse.co/' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Support',
|
||||
links: [
|
||||
{ text: 'Docs', href: 'https://ourworldfreezone.github.io/info_freezone/intro/intro_readme.html' },
|
||||
{ text: 'Community Forum', href: 'https://forum.threefold.io' },
|
||||
{ text: 'Telegram', href: 'https://t.me/threefoldnews' },
|
||||
{ text: 'Support', href: 'https://threefoldfaq.crisp.help/en/' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Company',
|
||||
links: [
|
||||
{ text: 'About', href: '#about' },
|
||||
{ text: 'Blog', href: '#blog' },
|
||||
{ text: 'FAQ', href: '#faq' },
|
||||
{ text: 'Contact', href: '/contact' },
|
||||
],
|
||||
},
|
||||
],
|
||||
secondaryLinks: [
|
||||
{ text: 'Terms', href:'https://library.threefold.me/info/legal/#/legal__terms_conditions_websites', target: '_blank', },
|
||||
{ text: 'Privacy Policy', href: 'https://library.threefold.me/info/legal/#/legal__privacypolicy', target: '_blank', },
|
||||
],
|
||||
socialLinks: [
|
||||
{ ariaLabel: 'Telegram', icon: 'tabler:brand-telegram', href: 'https://t.me/threefoldnews' },
|
||||
{ ariaLabel: 'Github', icon: 'tabler:brand-github', href: 'https://github.com/ourworldventures/' },
|
||||
{ ariaLabel: 'RSS', icon: 'tabler:rss', href: getAsset('/rss.xml') },
|
||||
{ ariaLabel: 'Docs', icon: 'tabler:book', href: 'https://ourworldfreezone.github.io/info_freezone/intro/intro_readme.html' },
|
||||
],
|
||||
footNote: `
|
||||
<img class="w-5 h-5 md:w-6 md:h-6 md:-mt-0.5 bg-cover mr-1.5 rtl:mr-0 rtl:ml-1.5 float-left rtl:float-right rounded-sm" src="/src/assets/images/logo.svg" alt="onWidget logo" loading="lazy"></img>
|
||||
Made by <a class="text-blue-600 underline dark:text-muted" href="https://ourworld.tf/"> OurWorld Holdings</a> · All rights reserved.
|
||||
`,
|
||||
};
|
24
src/pages/404.astro
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
import Layout from '~/layouts/Layout.astro';
|
||||
import { getHomePermalink } from '~/utils/permalinks';
|
||||
|
||||
const title = `Error 404`;
|
||||
---
|
||||
|
||||
<Layout metadata={{ title }}>
|
||||
<section class="flex items-center h-full p-16">
|
||||
<div class="container flex flex-col items-center justify-center px-5 mx-auto my-8">
|
||||
<div class="max-w-md text-center">
|
||||
<h2 class="mb-8 font-bold text-9xl">
|
||||
<span class="sr-only">Error</span>
|
||||
<span class="text-primary">404</span>
|
||||
</h2>
|
||||
<p class="text-3xl font-semibold md:text-3xl">Sorry, we couldn't find this page.</p>
|
||||
<p class="mt-4 mb-8 text-lg text-muted dark:text-slate-400">
|
||||
But dont worry, you can find plenty of other things on our homepage.
|
||||
</p>
|
||||
<a rel="noopener noreferrer" href={getHomePermalink()} class="btn ml-4">Back to homepage</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
52
src/pages/[...blog]/[...page].astro
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
import type { InferGetStaticPropsType, GetStaticPaths } from 'astro';
|
||||
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
import BlogList from '~/components/blog/List.astro';
|
||||
import Headline from '~/components/blog/Headline.astro';
|
||||
import Pagination from '~/components/blog/Pagination.astro';
|
||||
// import PostTags from "~/components/blog/Tags.astro";
|
||||
|
||||
import { blogListRobots, getStaticPathsBlogList } from '~/utils/blog';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const getStaticPaths = (async ({ paginate }) => {
|
||||
return await getStaticPathsBlogList({ paginate });
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
|
||||
const { page } = Astro.props as Props;
|
||||
const currentPage = page.currentPage ?? 1;
|
||||
|
||||
// const allCategories = await findCategories();
|
||||
// const allTags = await findTags();
|
||||
|
||||
const metadata = {
|
||||
title: `Blog${currentPage > 1 ? ` — Page ${currentPage}` : ''}`,
|
||||
robots: {
|
||||
index: blogListRobots?.index && currentPage === 1,
|
||||
follow: blogListRobots?.follow,
|
||||
},
|
||||
openGraph: {
|
||||
type: 'blog',
|
||||
},
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<section class="px-6 sm:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl">
|
||||
<Headline
|
||||
subtitle="Explore our collection of articles and news about OurWorld Digital Freezone's latest developments."
|
||||
>
|
||||
The Blog
|
||||
</Headline>
|
||||
<BlogList posts={page.data} />
|
||||
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
|
||||
<!--
|
||||
<PostTags tags={allCategories} class="mb-2" title="Search by Categories:" isCategory />
|
||||
<PostTags tags={allTags} title="Search by Tags:" />
|
||||
-->
|
||||
</section>
|
||||
</Layout>
|
37
src/pages/[...blog]/[category]/[...page].astro
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import type { InferGetStaticPropsType, GetStaticPaths } from 'astro';
|
||||
import { blogCategoryRobots, getStaticPathsBlogCategory } from '~/utils/blog';
|
||||
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
import BlogList from '~/components/blog/List.astro';
|
||||
import Headline from '~/components/blog/Headline.astro';
|
||||
import Pagination from '~/components/blog/Pagination.astro';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const getStaticPaths = (async ({ paginate }) => {
|
||||
return await getStaticPathsBlogCategory({ paginate });
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticPaths> & { category: Record<string, string> };
|
||||
|
||||
const { page, category } = Astro.props as Props;
|
||||
|
||||
const currentPage = page.currentPage ?? 1;
|
||||
|
||||
const metadata = {
|
||||
title: `Category '${category.title}' ${currentPage > 1 ? ` — Page ${currentPage}` : ''}`,
|
||||
robots: {
|
||||
index: blogCategoryRobots?.index,
|
||||
follow: blogCategoryRobots?.follow,
|
||||
},
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<section class="px-4 md:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl">
|
||||
<Headline>{category.title}</Headline>
|
||||
<BlogList posts={page.data} />
|
||||
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
|
||||
</section>
|
||||
</Layout>
|
37
src/pages/[...blog]/[tag]/[...page].astro
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import type { InferGetStaticPropsType, GetStaticPaths } from 'astro';
|
||||
import { blogTagRobots, getStaticPathsBlogTag } from '~/utils/blog';
|
||||
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
import BlogList from '~/components/blog/List.astro';
|
||||
import Headline from '~/components/blog/Headline.astro';
|
||||
import Pagination from '~/components/blog/Pagination.astro';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const getStaticPaths = (async ({ paginate }) => {
|
||||
return await getStaticPathsBlogTag({ paginate });
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
|
||||
const { page, tag } = Astro.props as Props;
|
||||
|
||||
const currentPage = page.currentPage ?? 1;
|
||||
|
||||
const metadata = {
|
||||
title: `Posts by tag '${tag.title}'${currentPage > 1 ? ` — Page ${currentPage} ` : ''}`,
|
||||
robots: {
|
||||
index: blogTagRobots?.index,
|
||||
follow: blogTagRobots?.follow,
|
||||
},
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<section class="px-4 md:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl">
|
||||
<Headline>Tag: {tag.title}</Headline>
|
||||
<BlogList posts={page.data} />
|
||||
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
|
||||
</section>
|
||||
</Layout>
|
54
src/pages/[...blog]/index.astro
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
import type { InferGetStaticPropsType, GetStaticPaths } from 'astro';
|
||||
|
||||
import merge from 'lodash.merge';
|
||||
import type { ImageMetadata } from 'astro';
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
import SinglePost from '~/components/blog/SinglePost.astro';
|
||||
import ToBlogLink from '~/components/blog/ToBlogLink.astro';
|
||||
|
||||
import { getCanonical, getPermalink } from '~/utils/permalinks';
|
||||
import { getStaticPathsBlogPost, blogPostRobots } from '~/utils/blog';
|
||||
import { findImage } from '~/utils/images';
|
||||
import type { MetaData } from '~/types';
|
||||
import RelatedPosts from '~/components/blog/RelatedPosts.astro';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
return await getStaticPathsBlogPost();
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
|
||||
const { post } = Astro.props as Props;
|
||||
|
||||
const url = getCanonical(getPermalink(post.permalink, 'post'));
|
||||
const image = (await findImage(post.image)) as ImageMetadata | string | undefined;
|
||||
|
||||
const metadata = merge(
|
||||
{
|
||||
title: post.title,
|
||||
description: post.excerpt,
|
||||
robots: {
|
||||
index: blogPostRobots?.index,
|
||||
follow: blogPostRobots?.follow,
|
||||
},
|
||||
openGraph: {
|
||||
type: 'article',
|
||||
...(image
|
||||
? { images: [{ url: image, width: (image as ImageMetadata)?.width, height: (image as ImageMetadata)?.height }] }
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
{ ...(post?.metadata ? { ...post.metadata, canonical: post.metadata?.canonical || url } : {}) }
|
||||
) as MetaData;
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<SinglePost post={{ ...post, image: image }} url={url}>
|
||||
{post.Content ? <post.Content /> : <Fragment set:html={post.content || ''} />}
|
||||
</SinglePost>
|
||||
<ToBlogLink />
|
||||
<RelatedPosts post={post} />
|
||||
</Layout>
|
228
src/pages/about.astro
Normal file
@@ -0,0 +1,228 @@
|
||||
---
|
||||
import Features2 from '~/components/widgets/Features2.astro';
|
||||
import Features3 from '~/components/widgets/Features3.astro';
|
||||
import Hero from '~/components/widgets/Hero.astro';
|
||||
import Stats from '~/components/widgets/Stats.astro';
|
||||
import Steps2 from '~/components/widgets/Steps2.astro';
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
|
||||
const metadata = {
|
||||
title: 'About us',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<!-- Hero Widget ******************* -->
|
||||
|
||||
<Hero
|
||||
tagline="About us"
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1559136555-9303baea8ebd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'Caos Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
Elevate your online presence with our <br />
|
||||
<span class="text-accent dark:text-white highlight"> Beautiful Website Templates</span>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
Donec efficitur, ipsum quis congue luctus, mauris magna convallis mauris, eu auctor nisi lectus non augue. Donec
|
||||
quis lorem non massa vulputate efficitur ac at turpis. Sed tincidunt ex a nunc convallis, et lobortis nisi tempus.
|
||||
Suspendisse vitae nisi eget tortor luctus maximus sed non lectus.
|
||||
</Fragment>
|
||||
</Hero>
|
||||
|
||||
<!-- Stats Widget ****************** -->
|
||||
|
||||
<Stats
|
||||
title="Statistics about us"
|
||||
stats={[
|
||||
{ title: 'Offices', amount: '4' },
|
||||
{ title: 'Employees', amount: '248' },
|
||||
{ title: 'Templates', amount: '12' },
|
||||
{ title: 'Awards', amount: '24' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Features3 Widget ************** -->
|
||||
|
||||
<Features3
|
||||
title="Our templates"
|
||||
subtitle="Etiam scelerisque, enim eget vestibulum luctus, nibh mauris blandit nulla, nec vestibulum risus justo ut enim. Praesent lacinia diam et ante imperdiet euismod."
|
||||
columns={3}
|
||||
isBeforeContent={true}
|
||||
items={[
|
||||
{
|
||||
title: 'Educational',
|
||||
description:
|
||||
'Morbi faucibus luctus quam, sit amet aliquet felis tempor id. Cras augue massa, ornare quis dignissim a, molestie vel nulla.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Interior Design',
|
||||
description:
|
||||
'Vivamus porttitor, tortor convallis aliquam pretium, turpis enim consectetur elit, vitae egestas purus erat ac nunc nulla.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Photography',
|
||||
description:
|
||||
'Duis sed lectus in nisl vehicula porttitor eget quis odio. Aliquam erat volutpat. Nulla eleifend nulla id sem fermentum.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Features3 Widget ************** -->
|
||||
|
||||
<Features3
|
||||
columns={3}
|
||||
isAfterContent={true}
|
||||
items={[
|
||||
{
|
||||
title: 'E-commerce',
|
||||
description:
|
||||
'Rutrum non odio at vehicula. Proin ipsum justo, dignissim in vehicula sit amet, dignissim id quam. Sed ac tincidunt sapien.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Blog',
|
||||
description:
|
||||
'Nullam efficitur volutpat sem sed fringilla. Suspendisse et enim eu orci volutpat laoreet ac vitae libero.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Business',
|
||||
description:
|
||||
'Morbi et elit finibus, facilisis justo ut, pharetra ipsum. Donec efficitur, ipsum quis congue luctus, mauris magna.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Branding',
|
||||
description:
|
||||
'Suspendisse vitae nisi eget tortor luctus maximus sed non lectus. Cras malesuada pretium placerat. Nullam venenatis dolor a ante rhoncus.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Medical',
|
||||
description:
|
||||
'Vestibulum malesuada lacus id nibh posuere feugiat. Nam volutpat nulla a felis ultrices, id suscipit mauris congue. In hac habitasse platea dictumst.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Fashion Design',
|
||||
description:
|
||||
'Maecenas eu tellus eget est scelerisque lacinia et a diam. Aliquam velit lorem, vehicula id fermentum et, rhoncus et purus.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80',
|
||||
alt: 'Colorful Image',
|
||||
}}
|
||||
/>
|
||||
|
||||
<!-- Steps2 Widget ****************** -->
|
||||
|
||||
<Steps2
|
||||
title="Our values"
|
||||
subtitle="Maecenas eu tellus eget est scelerisque lacinia et a diam. Aliquam velit lorem, vehicula id fermentum et, rhoncus et purus. Nulla facilisi. Vestibulum malesuada lacus."
|
||||
items={[
|
||||
{
|
||||
title: 'Customer-centric approach',
|
||||
description:
|
||||
'Donec id nibh neque. Quisque et fermentum tortor. Fusce vitae dolor a mauris dignissim commodo. Ut eleifend luctus condimentum.',
|
||||
},
|
||||
{
|
||||
title: 'Constant Improvement',
|
||||
description:
|
||||
'Phasellus laoreet fermentum venenatis. Vivamus dapibus pulvinar arcu eget mattis. Fusce eget mauris leo.',
|
||||
},
|
||||
{
|
||||
title: 'Ethical Practices',
|
||||
description:
|
||||
'Vestibulum imperdiet libero et lectus molestie, et maximus augue porta. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Steps2 Widget ****************** -->
|
||||
|
||||
<Steps2
|
||||
title="Achievements"
|
||||
subtitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla, sed porttitor est nibh at nulla."
|
||||
isReversed={true}
|
||||
callToAction={{
|
||||
text: 'See more',
|
||||
href: '/',
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
title: 'Global reach',
|
||||
description: 'Nam malesuada urna in enim imperdiet tincidunt. Phasellus non tincidunt nisi, at elementum mi.',
|
||||
icon: 'tabler:globe',
|
||||
},
|
||||
{
|
||||
title: 'Positive customer feedback and reviews',
|
||||
description:
|
||||
'Cras semper nulla leo, eget laoreet erat cursus sed. Praesent faucibus massa in purus iaculis dictum.',
|
||||
icon: 'tabler:message-star',
|
||||
},
|
||||
{
|
||||
title: 'Awards and recognition as industry experts',
|
||||
description:
|
||||
'Phasellus lacinia cursus velit, eu malesuada magna pretium eu. Etiam aliquet tellus purus, blandit lobortis ex rhoncus vitae.',
|
||||
icon: 'tabler:award',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Features2 Widget ************** -->
|
||||
|
||||
<Features2
|
||||
title="Our locations"
|
||||
tagline="Find us"
|
||||
columns={4}
|
||||
items={[
|
||||
{
|
||||
title: 'EE.UU',
|
||||
description: '1234 Lorem Ipsum St, 12345, Miami',
|
||||
},
|
||||
{
|
||||
title: 'Spain',
|
||||
description: '5678 Lorem Ipsum St, 56789, Madrid',
|
||||
},
|
||||
{
|
||||
title: 'Australia',
|
||||
description: '9012 Lorem Ipsum St, 90123, Sydney',
|
||||
},
|
||||
{
|
||||
title: 'Brazil',
|
||||
description: '3456 Lorem Ipsum St, 34567, São Paulo',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Features2 Widget ************** -->
|
||||
|
||||
<Features2
|
||||
title="Technical Support"
|
||||
tagline="Contact us"
|
||||
columns={2}
|
||||
items={[
|
||||
{
|
||||
title: 'Chat with us',
|
||||
description:
|
||||
'Integer luctus laoreet libero, auctor varius purus rutrum sit amet. Ut nec molestie nisi, quis eleifend mi.',
|
||||
icon: 'tabler:messages',
|
||||
},
|
||||
{
|
||||
title: 'Call us',
|
||||
description:
|
||||
'Mauris faucibus finibus orci, in posuere elit viverra non. In hac habitasse platea dictumst. Cras lobortis metus a hendrerit congue.',
|
||||
icon: 'tabler:headset',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Layout>
|
50
src/pages/contact.astro
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
import HeroText from '~/components/widgets/HeroText.astro';
|
||||
import ContactUs from '~/components/widgets/Contact.astro';
|
||||
import Features2 from '~/components/widgets/Features2.astro';
|
||||
|
||||
const metadata = {
|
||||
title: 'Contact',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<!-- Features2 Widget ************** -->
|
||||
|
||||
<Features2
|
||||
tagline='Contact Us'
|
||||
title="We are here to help you."
|
||||
items={[
|
||||
{
|
||||
title: 'General support',
|
||||
description: `For any questions or assistance regarding our services, operational inquiries, or general support needs, our team is available to chat with you. We're committed to providing prompt and informative responses to ensure your experience with us is seamless.`,
|
||||
},
|
||||
{
|
||||
title: 'Contact sales',
|
||||
description:
|
||||
'Considering partnering with OurWorld FreeZone? Chat with our sales team to discover how our digital free zone can benefit your business. Whether you are looking to expand internationally or streamline your operations, we provide tailored solutions to meet your specific needs.',
|
||||
},
|
||||
{
|
||||
title: 'Technical support',
|
||||
description:
|
||||
'Need technical assistance with our digital infrastructure or services? Our technical support team is equipped to address your queries and resolve any issues promptly. We are dedicated to ensuring the smooth operation of your business within our innovative digital environment.',
|
||||
},
|
||||
{
|
||||
title: 'Customer Service',
|
||||
description: '<a href=\"https://threefoldfaq.crisp.help/en/\" target=\"_blank\"><u>Visit Our Customer Service Portal</u></a>',
|
||||
icon: 'tabler:headset',
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
description: '<a href=\mailto:"info@ourworld.tf" target=\"_blank\"><u>info@ourworld.tf</u></a>',
|
||||
icon: 'tabler:mail',
|
||||
},
|
||||
{
|
||||
title: 'Follow Us',
|
||||
description: '<a href=\"https://t.me/threefoldnews/\" target=\"_blank\"><u>Join Our Telegram</u></a>',
|
||||
icon: 'tabler:brand-telegram',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Layout>
|
297
src/pages/homes/mobile-app.astro
Normal file
@@ -0,0 +1,297 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
|
||||
import Header from '~/components/widgets/Header.astro';
|
||||
|
||||
import Hero2 from '~/components/widgets/Hero2.astro';
|
||||
import CallToAction from '~/components/widgets/CallToAction.astro';
|
||||
import Features3 from '~/components/widgets/Features3.astro';
|
||||
import Content from '~/components/widgets/Content.astro';
|
||||
import Testimonials from '~/components/widgets/Testimonials.astro';
|
||||
import FAQs from '~/components/widgets/FAQs.astro';
|
||||
import Stats from '~/components/widgets/Stats.astro';
|
||||
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
|
||||
const appStoreImg = '~/assets/images/app-store.png';
|
||||
const appStoreDownloadLink = 'https://github.com/onwidget/astrowind';
|
||||
|
||||
const googlePlayImg = '~/assets/images/google-play.png';
|
||||
const googlePlayDownloadLink = 'https://github.com/onwidget/astrowind';
|
||||
|
||||
const metadata = {
|
||||
title: 'Mobile App Homepage',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<Fragment slot="announcement"></Fragment>
|
||||
<Fragment slot="header">
|
||||
<Header
|
||||
position="left"
|
||||
links={[
|
||||
{ text: 'Services', href: '#' },
|
||||
{ text: 'Features', href: '#' },
|
||||
{ text: 'About', href: '#' },
|
||||
]}
|
||||
actions={[
|
||||
{
|
||||
text: 'Download',
|
||||
href: '#download',
|
||||
},
|
||||
]}
|
||||
isSticky
|
||||
showToggleTheme
|
||||
/>
|
||||
</Fragment>
|
||||
|
||||
<!-- Hero2 Widget ******************* -->
|
||||
|
||||
<Hero2
|
||||
tagline="Mobile App Web Demo"
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1535303311164-664fc9ec6532?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=987&q=80',
|
||||
alt: 'AstroWind Hero Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
<span class="text-accent dark:text-white highlight">AstroWind App</span>: <br /> professional websites <span
|
||||
class="hidden xl:inline">made easy</span
|
||||
>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
<span class="hidden sm:inline">
|
||||
Unlock boundless creativity at your fingertips: your gateway to innovative design.
|
||||
</span>
|
||||
Download now and embark on a journey to elevate your projects like never before.
|
||||
</Fragment>
|
||||
|
||||
<div slot="actions" class="flex max-w-sm gap-4">
|
||||
<Button variant="link" href={appStoreDownloadLink}>
|
||||
<Image src={appStoreImg} alt="App Store Image" width={200} />
|
||||
</Button>
|
||||
|
||||
<Button variant="link" href={googlePlayDownloadLink}>
|
||||
<Image src={googlePlayImg} alt="Google Play Image" width={200} />
|
||||
</Button>
|
||||
</div>
|
||||
</Hero2>
|
||||
|
||||
<!-- Features3 Widget ************** -->
|
||||
|
||||
<Features3
|
||||
id="features"
|
||||
title="How to use our app?"
|
||||
subtitle="Tired of spending hours crafting documents from scratch? Our app offers an innovative solution. With a wide array of professionally designed templates, you can now create stunning documents in minutes. Explore our templates now and experience the difference."
|
||||
tagline="Step-by-step guide"
|
||||
columns={2}
|
||||
items={[
|
||||
{
|
||||
title: 'Download and install the app',
|
||||
description: `Begin your journey by downloading our user-friendly app from your device's app store or our official website.`,
|
||||
icon: 'tabler:square-number-1',
|
||||
},
|
||||
{
|
||||
title: 'Sign up',
|
||||
description:
|
||||
'Create your account by providing the necessary information, enabling you to access our full range of features.',
|
||||
icon: 'tabler:square-number-2',
|
||||
},
|
||||
{
|
||||
title: 'Browse templates',
|
||||
description: 'Explore our diverse collection of website templates, categorized for easy navigation.',
|
||||
icon: 'tabler:square-number-3',
|
||||
},
|
||||
{
|
||||
title: 'Preview and select a template',
|
||||
description: `Visualize the potential of each template through previews, then choose the one that aligns best with your project's needs.`,
|
||||
icon: 'tabler:square-number-4',
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1521517407911-565264e7d82d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2069&q=80',
|
||||
alt: 'Colorful Image',
|
||||
}}
|
||||
/>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
isReversed
|
||||
items={[
|
||||
{
|
||||
title: 'User-friendly interface',
|
||||
description:
|
||||
'An intuitive and easy-to-navigate interface that allows users to quickly browse and find the templates they need.',
|
||||
icon: 'tabler:wand',
|
||||
},
|
||||
{
|
||||
title: 'Personalization options',
|
||||
description:
|
||||
'Include basic customization tools that let users modify text, colors, images, and other elements within the templates.',
|
||||
icon: 'tabler:settings',
|
||||
},
|
||||
{
|
||||
title: 'Ready-to-use components',
|
||||
description:
|
||||
'Enhance your designs with ready-to-use elements like graphics, icons, and layouts, saving you time and boosting visual appeal.',
|
||||
icon: 'tabler:template',
|
||||
},
|
||||
{
|
||||
title: 'Preview Mode',
|
||||
description: 'Provide a preview of each template, allowing users to see how it looks before making a purchase.',
|
||||
icon: 'tabler:carousel-horizontal',
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1576153192621-7a3be10b356e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1674&q=80',
|
||||
alt: 'Colorful Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Main Features</h3>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
isAfterContent
|
||||
items={[
|
||||
{
|
||||
title: 'Offline Access',
|
||||
description: 'Offer the option for users to download purchased templates for offline use.',
|
||||
icon: 'tabler:wifi-off',
|
||||
},
|
||||
{
|
||||
title: 'Secure Cloud Storage',
|
||||
description:
|
||||
'Provide cloud storage for purchased templates, ensuring users can access and back up their templates from anywhere securely.',
|
||||
icon: 'tabler:file-download',
|
||||
},
|
||||
{
|
||||
title: 'Regular Updates',
|
||||
description: 'Continuously add new templates and features to keep the app fresh and engaging for users.',
|
||||
icon: 'tabler:refresh',
|
||||
},
|
||||
{
|
||||
title: 'Wishlist',
|
||||
description: `Allow users to create a wishlist of templates they're interested in, making it easier for them to revisit and potentially purchase later.`,
|
||||
icon: 'tabler:heart',
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1453738773917-9c3eff1db985?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'Vintage Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Other features</h3>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Stats Widget ****************** -->
|
||||
|
||||
<Stats
|
||||
title="Statistics of our app"
|
||||
stats={[
|
||||
{ amount: '20K', icon: 'tabler:download' },
|
||||
{ amount: '18.5K', icon: 'tabler:users' },
|
||||
{ amount: '4.7', icon: 'tabler:user-star' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Testimonials Widget *********** -->
|
||||
|
||||
<Testimonials
|
||||
title="What our users say?"
|
||||
testimonials={[
|
||||
{
|
||||
testimonial: `It's made exploring and downloading website templates a breeze. The interface is intuitive, and I had no trouble finding the perfect template for my project. It's an app that truly empowers users.`,
|
||||
name: 'Cary Kennedy',
|
||||
job: 'Film director',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'Cary Kennedy Image',
|
||||
},
|
||||
},
|
||||
{
|
||||
testimonial: `The app's seamless download process and intuitive layout have made selecting templates an enjoyable experience. Being able to preview and experiment with different designs before committing has saved me time and ensured I get the perfect look for my website.`,
|
||||
name: 'Josh Wilkinson',
|
||||
job: 'Product Manager',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/flagged/photo-1570612861542-284f4c12e75f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'Josh Wilkinson Image',
|
||||
},
|
||||
},
|
||||
{
|
||||
testimonial:
|
||||
'I was able to download and use a professional website template within minutes. The step-by-step process and user-friendly interface made it easy for me to create a website that looks as if it was designed by a pro.',
|
||||
name: 'Sidney Hansen',
|
||||
job: 'Decorator',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1512361436605-a484bdb34b5f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'Sidney Hansen Image',
|
||||
},
|
||||
},
|
||||
]}
|
||||
callToAction={{
|
||||
target: '_blank',
|
||||
text: 'Read more testimonials',
|
||||
href: 'https://github.com/onwidget/astrowind',
|
||||
icon: 'tabler:chevron-right',
|
||||
}}
|
||||
/>
|
||||
|
||||
<!-- FAQs Widget ******************* -->
|
||||
|
||||
<FAQs
|
||||
title="Still have some doubts?"
|
||||
items={[
|
||||
{
|
||||
title: 'What does this app do?',
|
||||
description:
|
||||
'This app provides a platform for you to easily browse, purchase, download, and use a wide range of website templates for your projects.',
|
||||
},
|
||||
{
|
||||
title: 'How can this app solve my problem?',
|
||||
description:
|
||||
'This app streamlines the process of finding and implementing professional website designs, saving you time and effort in creating visually appealing and functional websites.',
|
||||
},
|
||||
{
|
||||
title: 'Is it available for my device?',
|
||||
description: `Our app is designed for compatibility across various devices and platforms, ensuring accessibility whether you're using a smartphone, tablet, or computer.`,
|
||||
},
|
||||
{
|
||||
title: 'What makes this app different from others?',
|
||||
description:
|
||||
'Our app stands out for its user-friendly interface, extensive template collection, and seamless integration of the purchasing and downloading process, making it highly efficient.',
|
||||
},
|
||||
{
|
||||
title: 'Are there any costs involved?',
|
||||
description:
|
||||
'While the app itself may be free to download, there may be costs associated with purchasing specific templates based on your preferences and project requirements.',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- CallToAction Widget *********** -->
|
||||
|
||||
<CallToAction
|
||||
id="download"
|
||||
title="Download our app now!"
|
||||
subtitle="Access a variety of stunning templates, simplify your creative process, and elevate your online presence."
|
||||
>
|
||||
<div slot="actions" class="flex max-w-sm gap-4">
|
||||
<Button variant="link" href={appStoreDownloadLink}>
|
||||
<Image src={appStoreImg} alt="App Store Image" width={200} />
|
||||
</Button>
|
||||
|
||||
<Button variant="link" href={googlePlayDownloadLink}>
|
||||
<Image src={googlePlayImg} alt="Google Play Image" width={200} />
|
||||
</Button>
|
||||
</div>
|
||||
</CallToAction>
|
||||
</Layout>
|
405
src/pages/homes/personal.astro
Normal file
@@ -0,0 +1,405 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
|
||||
import Header from '~/components/widgets/Header.astro';
|
||||
import Hero from '~/components/widgets/Hero.astro';
|
||||
import Content from '~/components/widgets/Content.astro';
|
||||
import CallToAction from '~/components/widgets/CallToAction.astro';
|
||||
import Features3 from '~/components/widgets/Features3.astro';
|
||||
import Testimonials from '~/components/widgets/Testimonials.astro';
|
||||
import Steps from '~/components/widgets/Steps.astro';
|
||||
import BlogLatestPosts from '~/components/widgets/BlogLatestPosts.astro';
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
|
||||
const metadata = {
|
||||
title: 'Personal Homepage Demo',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<Fragment slot="announcement"></Fragment>
|
||||
<Fragment slot="header">
|
||||
<Header
|
||||
links={[
|
||||
{ text: 'Home', href: '#' },
|
||||
{ text: 'About', href: '#about' },
|
||||
{ text: 'Resume', href: '#resume' },
|
||||
{ text: 'Porfolio', href: '#porfolio' },
|
||||
{ text: 'Blog', href: '#blog' },
|
||||
{ text: 'Github', href: 'https://github.com/onwidget' },
|
||||
]}
|
||||
actions={[
|
||||
{
|
||||
text: 'Hire me',
|
||||
href: '#',
|
||||
},
|
||||
]}
|
||||
isSticky
|
||||
showToggleTheme
|
||||
/>
|
||||
</Fragment>
|
||||
|
||||
<!-- Hero2 Widget ******************* -->
|
||||
|
||||
<Hero
|
||||
id="hero"
|
||||
title="Sarah Johnson"
|
||||
tagline="Personal Web Demo"
|
||||
actions={[{ variant: 'primary', text: 'Hire me', href: getPermalink('/contact#form') }]}
|
||||
>
|
||||
<Fragment slot="subtitle">
|
||||
I'm a Graphic Designer passionate about crafting visual stories. <br /> With 5 years of experience and a degree from
|
||||
New York University's School of Design. I infuse vitality into brands and designs, transforming concepts into captivating
|
||||
realities.
|
||||
</Fragment>
|
||||
</Hero>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
id="about"
|
||||
columns={3}
|
||||
items={[
|
||||
{
|
||||
icon: 'tabler:brand-dribbble',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Dribbble',
|
||||
href: '#',
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: 'tabler:brand-behance',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Behance',
|
||||
href: '#',
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: 'tabler:brand-pinterest',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Pinterest',
|
||||
href: '#',
|
||||
},
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1491349174775-aaafddd81942?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
||||
alt: 'Colorful Image',
|
||||
loading: 'eager',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h2 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">About me</h2>
|
||||
<p>
|
||||
Welcome to my creative journey. My work is a testament to my commitment to bringing ideas to life, where each
|
||||
pixel becomes a brushstroke in the canvas of imagination.
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
I find inspiration in the world around me, whether through the pages of a captivating novel, the intricate
|
||||
details of typography, or the vibrant hues of nature during my outdoor escapades.
|
||||
</p>
|
||||
<br />
|
||||
<p>If you're curious to dive deeper into my work, you can follow me:</p>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Steps Widget ****************** -->
|
||||
|
||||
<Steps
|
||||
id="resume"
|
||||
title="Work experience"
|
||||
items={[
|
||||
{
|
||||
title:
|
||||
'Graphic Designer <br /> <span class="font-normal">ABC Design Studio, New York, NY</span> <br /> <span class="text-sm font-normal">2021 - Present</span>',
|
||||
description: `Collaborate with clients to understand design requirements and objectives. <br /> Develop branding solutions, including logos, color palettes, and brand guidelines. <br /> Design marketing materials such as brochures, posters, and digital assets. <br /> Create visually appealing user interfaces for websites and applications.`,
|
||||
icon: 'tabler:briefcase',
|
||||
},
|
||||
{
|
||||
title:
|
||||
'Junior Graphic Designer <br /> <span class="font-normal">XYZ Creative Agency, Los Angeles, CA</span> <br /> <span class="text-sm font-normal">2018 - 2021</span>',
|
||||
description: `Assisted senior designers in creating design concepts and visual assets. <br /> Contributed to the development of brand identities and marketing collateral. <br /> Collaborated with the marketing team to ensure consistent design across campaigns. <br /> Gained hands-on experience in various design software and tools.`,
|
||||
icon: 'tabler:briefcase',
|
||||
},
|
||||
]}
|
||||
classes={{ container: 'max-w-3xl' }}
|
||||
/>
|
||||
|
||||
<!-- Steps Widget ****************** -->
|
||||
|
||||
<Steps
|
||||
id="resume"
|
||||
title="Education"
|
||||
items={[
|
||||
{
|
||||
title: `Master of Fine Arts in Graphic Design <br /> <span class="font-normal">New York University's School of Design</span> <br /> <span class="text-sm font-normal">2018 - 2020</span>`,
|
||||
icon: 'tabler:school',
|
||||
},
|
||||
{
|
||||
title: `Bachelor of Arts in Graphic Design <br /> <span class="font-normal">New York University's School of Design</span> <br /> <span class="text-sm font-normal">2014 - 2018</span>`,
|
||||
icon: 'tabler:school',
|
||||
},
|
||||
]}
|
||||
classes={{ container: 'max-w-3xl' }}
|
||||
/>
|
||||
|
||||
<!-- Features3 Widget ************** -->
|
||||
|
||||
<Features3
|
||||
title="Skills"
|
||||
subtitle="Discover the proficiencies that allow me to bring imagination to life through design."
|
||||
columns={3}
|
||||
defaultIcon="tabler:point-filled"
|
||||
items={[
|
||||
{
|
||||
title: 'Graphic design',
|
||||
description: 'Proficient in crafting visually appealing designs that convey messages effectively.',
|
||||
},
|
||||
{
|
||||
title: 'Branding and identity',
|
||||
description: 'Skilled at developing cohesive brand identities, including logos and brand guidelines.',
|
||||
},
|
||||
{
|
||||
title: 'User-centered design',
|
||||
description: 'Experienced in creating user-friendly interfaces and optimizing user experiences.',
|
||||
},
|
||||
{
|
||||
title: 'Adobe Creative Suite',
|
||||
description: 'Skilled in using Photoshop, Illustrator, and InDesign to create and edit visual elements.',
|
||||
},
|
||||
{
|
||||
title: 'Typography',
|
||||
description: 'Adept in selecting and manipulating typefaces to enhance design aesthetics.',
|
||||
},
|
||||
{
|
||||
title: 'Color theory',
|
||||
description: 'Proficient in using color to evoke emotions and enhance visual harmony.',
|
||||
},
|
||||
{
|
||||
title: 'Print and digital design',
|
||||
description: 'Knowledgeable in designing for both print materials and digital platforms.',
|
||||
},
|
||||
{
|
||||
title: 'Attention to detail',
|
||||
description: 'Diligent in maintaining precision and quality in all design work.',
|
||||
},
|
||||
{
|
||||
title: 'Adaptability',
|
||||
description: 'Quick to adapt to new design trends, technologies, and client preferences.',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
id="porfolio"
|
||||
title="Elevating visual narratives"
|
||||
subtitle="Embark on a design journey that surpasses pixels, entering a realm of imagination. Explore my portfolio, where passion and creativity converge to shape enthralling visual narratives."
|
||||
isReversed
|
||||
items={[
|
||||
{
|
||||
title: 'Description:',
|
||||
description:
|
||||
'Developed a comprehensive brand identity for a tech startup, Tech Innovators, specializing in disruptive innovations. The goal was to convey a modern yet approachable image that resonated with both corporate clients and tech enthusiasts.',
|
||||
},
|
||||
{
|
||||
title: 'Role:',
|
||||
description:
|
||||
'Led the entire branding process from concept to execution. Created a dynamic logo that symbolized innovation, selected a vibrant color palette, and I designed corporate stationery, website graphics, and social media assets.',
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1658248165252-71e116af1b34?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=928&q=80',
|
||||
alt: 'Tech Design Image',
|
||||
}}
|
||||
callToAction={{
|
||||
target: '_blank',
|
||||
text: 'Go to the project',
|
||||
icon: 'tabler:chevron-right',
|
||||
href: '#',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">
|
||||
Project 1: <br /><span class="text-2xl">Brand identity for tech innovators</span>
|
||||
</h3>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
isReversed
|
||||
isAfterContent={true}
|
||||
items={[
|
||||
{
|
||||
title: 'Description:',
|
||||
description:
|
||||
'Designed a captivating event poster for an art and music festival, "ArtWave Fusion," aiming to showcase the synergy between visual art and music genres.',
|
||||
},
|
||||
{
|
||||
title: 'Role:',
|
||||
description: `Translated the festival's creative theme into a visually striking poster. Used bold typography, vibrant colors, and abstract elements to depict the fusion of art and music. Ensured the design captured the festival's vibrant atmosphere.`,
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1619983081563-430f63602796?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
||||
alt: 'Art and Music Poster Image',
|
||||
}}
|
||||
callToAction={{
|
||||
target: '_blank',
|
||||
text: 'Go to the project',
|
||||
icon: 'tabler:chevron-right',
|
||||
href: '#',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">
|
||||
Project 2: <br /><span class="text-2xl">Event poster for art & music festival</span>
|
||||
</h3>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
isReversed
|
||||
isAfterContent={true}
|
||||
items={[
|
||||
{
|
||||
title: 'Description:',
|
||||
description: `Redesigned the e-commerce website for an eco-conscious fashion brand, GreenVogue. The objective was to align the brand's online presence with its sustainable ethos and improve user experience.`,
|
||||
},
|
||||
{
|
||||
title: 'Role:',
|
||||
description: `Conducted a thorough analysis of the brand's values and customer base to inform the design direction. Created a visually appealing interface with intuitive navigation, highlighting sustainable materials, and integrating a user-friendly shopping experience.`,
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://plus.unsplash.com/premium_photo-1683288295841-782fa47e4770?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=870&q=80',
|
||||
alt: 'Fashion e-commerce Image',
|
||||
}}
|
||||
callToAction={{
|
||||
target: '_blank',
|
||||
text: 'Go to the project',
|
||||
icon: 'tabler:chevron-right',
|
||||
href: '#',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">
|
||||
Project 3: <br /><span class="text-2xl">E-commerce website redesign for fashion brand</span>
|
||||
</h3>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Testimonials Widget *********** -->
|
||||
|
||||
<Testimonials
|
||||
title="Client testimonials"
|
||||
subtitle="Discover what clients have to say about their experiences working with me."
|
||||
testimonials={[
|
||||
{
|
||||
testimonial: `She took our vague concept and turned it into a visual masterpiece that perfectly aligned with our goals. Her attention to detail and ability to translate ideas into compelling visuals exceeded our expectations.`,
|
||||
name: 'Mark Thompson',
|
||||
job: 'Creative director',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
||||
alt: 'Mark Thompson Image',
|
||||
},
|
||||
},
|
||||
{
|
||||
testimonial: `She transformed our brand identity with her creative finesse, capturing our essence in every element. Her dedication and talent truly shine through her work.`,
|
||||
name: 'Emily Martinez',
|
||||
job: 'CEO',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1554151228-14d9def656e4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=772&q=80',
|
||||
alt: 'Emily Martinez Image',
|
||||
},
|
||||
},
|
||||
{
|
||||
testimonial: `She has an uncanny ability to communicate emotions and stories. She crafted a logo for our NGO that not only represents our cause but also evokes empathy. Her professionalism and commitment make her a designer of exceptional caliber.`,
|
||||
name: 'Laura Simmons',
|
||||
job: 'Founder of an NGO',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1554727242-741c14fa561c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
||||
alt: 'Laura Simmons Image',
|
||||
},
|
||||
},
|
||||
{
|
||||
testimonial: `We entrusted Sarah with revamping our website's user interface, and the results were astounding. Her intuitive design sense enhanced user experience, leading to a significant increase in engagement. She's a designer who truly understands the synergy of aesthetics and functionality.`,
|
||||
name: 'Alex Foster',
|
||||
job: 'Director of web services',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1599566150163-29194dcaad36?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
||||
alt: 'Alex Foster Image',
|
||||
},
|
||||
},
|
||||
{
|
||||
testimonial: `She took our vision and elevated it beyond imagination. Her ability to capture brand essence and translate it into design is nothing short of remarkable. Working with her has been an inspiring journey.`,
|
||||
name: 'Jessica Collins',
|
||||
job: 'Product Manager',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1548142813-c348350df52b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=778&q=80',
|
||||
alt: 'Jessica Collins Image',
|
||||
},
|
||||
},
|
||||
{
|
||||
testimonial: `Her ability to transform concepts into captivating visuals is nothing short of extraordinary. She took our event poster idea and turned it into a visual masterpiece that perfectly captured the essence of our festival. Sarah's dedication, creativity, and knack for delivering beyond expectations make her an invaluable asset to any project.`,
|
||||
name: 'Michael Carter',
|
||||
job: 'Event Coordinator',
|
||||
image: {
|
||||
src: 'https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
||||
alt: 'Michael Carter Image',
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- CallToAction Widget *********** -->
|
||||
|
||||
<CallToAction
|
||||
title="Let's create together"
|
||||
subtitle="Ready to transform your vision into captivating designs?"
|
||||
actions={[
|
||||
{
|
||||
variant: 'primary',
|
||||
text: 'Hire me',
|
||||
href: '/',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- BlogLatestPost Widget **************** -->
|
||||
|
||||
<BlogLatestPosts
|
||||
id="blog"
|
||||
title="Explore my insightful articles on my blog"
|
||||
information={`Dive into a realm of design wisdom and creative inspiration, where you'll find invaluable insights, practical tips, and captivating narratives that elevate and enrich your creative journey.`}
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</BlogLatestPosts>
|
||||
</Layout>
|
349
src/pages/homes/saas.astro
Normal file
@@ -0,0 +1,349 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
|
||||
import Header from '~/components/widgets/Header.astro';
|
||||
import Hero2 from '~/components/widgets/Hero2.astro';
|
||||
import Features from '~/components/widgets/Features.astro';
|
||||
import Steps2 from '~/components/widgets/Steps2.astro';
|
||||
import Content from '~/components/widgets/Content.astro';
|
||||
import Pricing from '~/components/widgets/Pricing.astro';
|
||||
|
||||
import { headerData } from '~/navigation';
|
||||
import FAQs from '~/components/widgets/FAQs.astro';
|
||||
import BlogLatestPosts from '~/components/widgets/BlogLatestPosts.astro';
|
||||
|
||||
const metadata = {
|
||||
title: 'SaaS Landing Page',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<Fragment slot="header">
|
||||
<Header
|
||||
{...headerData}
|
||||
actions={[
|
||||
{
|
||||
variant: 'secondary',
|
||||
text: 'Login',
|
||||
href: '#',
|
||||
},
|
||||
{
|
||||
variant: 'primary',
|
||||
text: 'Sign Up',
|
||||
href: '#',
|
||||
},
|
||||
]}
|
||||
isSticky
|
||||
/>
|
||||
</Fragment>
|
||||
|
||||
<!-- Hero2 Widget ******************* -->
|
||||
|
||||
<Hero2
|
||||
tagline="SaaS Web Demo"
|
||||
actions={[
|
||||
{ variant: 'primary', target: '_blank', text: 'Get Started', href: 'https://github.com/onwidget/astrowind' },
|
||||
{ text: 'Learn more', href: '#features' },
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1580481072645-022f9a6dbf27?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'AstroWind Hero Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
Simplify web design with Astrowind: <br /> your ultimate <span class="text-accent dark:text-white highlight"
|
||||
>SaaS</span
|
||||
> companion<br />
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
<span class="hidden sm:inline">
|
||||
Elevate your website creation process with <span class="font-semibold">AstroWind</span>'s SaaS solutions.</span
|
||||
>
|
||||
Seamlessly blend the power of Astro 4.0 and Tailwind CSS to craft websites that resonate with your brand and audience.
|
||||
</Fragment>
|
||||
</Hero2>
|
||||
|
||||
<!-- Features Widget *************** -->
|
||||
|
||||
<Features
|
||||
id="features"
|
||||
title="Why choose AstroWind?"
|
||||
subtitle="Each of the following features enhances AstroWind's value proposition."
|
||||
columns={2}
|
||||
items={[
|
||||
{
|
||||
title: 'Integration of Astro 4.0 and Tailwind CSS',
|
||||
description:
|
||||
'Offers a powerful combination that enhances both the development process and the end-user experience. Also, allows to build dynamic and visually stunning websites with optimized performance.',
|
||||
icon: 'tabler:layers-union',
|
||||
},
|
||||
{
|
||||
title: 'Versatile design for startups, small businesses, and more',
|
||||
description: `Easily customize AstroWind to harmonize with the unique branding and identity of your venture. AstroWind's versatile design adapts to suit your needs.`,
|
||||
icon: 'tabler:artboard',
|
||||
},
|
||||
{
|
||||
title: 'Effortless customization for portfolios and marketing sites',
|
||||
description:
|
||||
'With intuitive customization, easily showcase portfolio pieces, case studies, project highlights, and relevant content. Ideal for creative professionals and businesses looking to highlight their expertise.',
|
||||
icon: 'tabler:writing',
|
||||
},
|
||||
{
|
||||
title: 'Optimized landing pages and engaging blogs',
|
||||
description: `Landing pages are strategically designed to captivate visitors and prompt specific actions. Additionally, the blog creation feature empowers sharing insights, engaging the audience.`,
|
||||
icon: 'tabler:podium',
|
||||
},
|
||||
{
|
||||
title: 'Fast loading times and production-ready code',
|
||||
description: `Using Astro 4.0 ensures fast loading and seamless rendering, enhancing browsing. The code follows best practices, improving user experience, SEO, and reducing bounce rates.`,
|
||||
icon: 'tabler:rocket',
|
||||
},
|
||||
{
|
||||
title: 'SEO-optimized structure for enhanced visibility',
|
||||
description: `Follows SEO best practices with clean code, semantic HTML markup, and fast loading, enhancing search engine rankings. AstroWind's SEO structure ensures visibility to potential customers and clients.`,
|
||||
icon: 'tabler:eyeglass',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
title="Use cases"
|
||||
subtitle="Discover how AstroWind's versatile template serves as the ideal solution for various use cases, providing tailored solutions to drive success."
|
||||
isReversed
|
||||
items={[
|
||||
{
|
||||
title: 'Description:',
|
||||
description:
|
||||
'Are you a startup with big dreams? AstroWind propels your success. Our template forges a seamless online presence, attracting investors and customers from day one. Astro 4.0 and Tailwind CSS ensure striking, responsive sites, leaving lasting impressions. Countless startups leverage AstroWind to kickstart their journey and resonate with audiences.',
|
||||
},
|
||||
{
|
||||
title: 'Benefits:',
|
||||
description: `Allow startups to quickly create professional websites without investing extensive time and resources. <br /> Make a memorable first impression with visually appealing design elements that highlight your startup's unique value proposition. <br /> Ensures your website looks stunning and works well on all devices. <br /> Engage potential investors and customers with engaging content, clear messaging, and intuitive navigation.`,
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1620558138198-cfb9b4f3c294?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1671&q=80',
|
||||
alt: 'Startup Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">
|
||||
Startup success stories: <br /><span class="text-2xl">Launching with AstroWind</span>
|
||||
</h3>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
isAfterContent={true}
|
||||
items={[
|
||||
{
|
||||
title: 'Description:',
|
||||
description: `For SaaS businesses, user experience is key. AstroWind enhances showcasing SaaS solutions intuitively. The template's Astro 4.0 and Tailwind CSS integration guarantees user-friendly experience, mirroring your software's efficiency. Customize pages to communicate SaaS value and solutions for your audience.`,
|
||||
},
|
||||
{
|
||||
title: 'Benefits:',
|
||||
description: `Ensuring a cohesive and user-centric design for your SaaS website. <br /> Effectively communicate complex SaaS features through visual aids, animations, and interactive elements. <br /> Prioritize user needs and pain points through well-structured layouts and clear navigation. <br /> Encourage visitors to take action with strategically placed CTAs. <br /> Ensures your SaaS website works seamlessly across all devices.`,
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1531973486364-5fa64260d75b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1658&q=80',
|
||||
alt: 'SaaS Businesses Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">
|
||||
SaaS showcase: <br /><span class="text-2xl">Streamlining user experience</span>
|
||||
</h3>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
isReversed
|
||||
isAfterContent={true}
|
||||
items={[
|
||||
{
|
||||
title: 'Description:',
|
||||
description: `Your portfolio is your masterpiece, and AstroWind is your canvas. Whether you're a designer, photographer, artist, or any other creative professional, AstroWind empowers you to showcase your work with elegance and sophistication. Tailored to highlight your creative projects, AstroWind's templates offer a visually immersive experience that lets your portfolio shine.`,
|
||||
},
|
||||
{
|
||||
title: 'Benefits:',
|
||||
description: `Serve as a captivating backdrop to showcase your creative work, capturing attention and leaving a lasting impression. <br /> Tailor your portfolio to reflect your unique style and artistic vision. <br /> Prioritizes visuals, allowing you to present your work in high-resolution detail that draws viewers into your creations. <br /> Enables seamless navigation for effortless portfolio exploration.`,
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1635070041078-e363dbe005cb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'Portfolio Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">
|
||||
Creative portfolios: <br /><span class="text-2xl">Highlighting your work</span>
|
||||
</h3>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<Content
|
||||
items={[
|
||||
{
|
||||
title: 'Description:',
|
||||
description: `For small businesses, a well-crafted website can be a game-changer. AstroWind empowers small businesses to not only establish a credible online presence but also convert visitors into loyal customers. The template's thoughtful design and optimization features ensure that your website doesn't just attract attention but also guides visitors through a seamless journey, ultimately leading to conversions.`,
|
||||
},
|
||||
{
|
||||
title: 'Benefits:',
|
||||
description: `Present your small business with a professional and polished website that instills confidence and trust among visitors. <br /> Strategically placed CTAs, user-friendly forms, and optimized layouts work together to drive user engagement and conversions. <br /> Ensure a smooth browsing experience, reducing bounce rates and encouraging interaction.`,
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1514621166532-aa7eb1a3a2f4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
||||
alt: 'Small Business Image',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="content">
|
||||
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">
|
||||
Small business growth: <br /><span class="text-2xl">Converting visitors into customers</span>
|
||||
</h3>
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Content>
|
||||
|
||||
<!-- Pricing Widget ******************* -->
|
||||
|
||||
<Pricing
|
||||
title="Flexible pricing plans"
|
||||
prices={[
|
||||
{
|
||||
title: 'free',
|
||||
subtitle: 'Access to core features and a wide range of templates',
|
||||
price: '0',
|
||||
period: '/ month',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Get Started for Free',
|
||||
href: '#',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'pro',
|
||||
subtitle: 'Premium templates and advanced customization',
|
||||
price: 15,
|
||||
period: '/ Month',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Upgrade to Pro',
|
||||
href: '#',
|
||||
},
|
||||
hasRibbon: true,
|
||||
ribbonTitle: 'popular',
|
||||
},
|
||||
{
|
||||
title: 'Enterprise',
|
||||
subtitle: 'Tailored solutions for large-scale projects',
|
||||
price: 45,
|
||||
period: '/ Month',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Unlock Enterprise Features',
|
||||
href: '#',
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- FAQs Widget ******************* -->
|
||||
|
||||
<FAQs
|
||||
title="Frequently Asked Questions"
|
||||
items={[
|
||||
{
|
||||
title: 'Is AstroWind compatible with the latest versions of Astro and Tailwind CSS?',
|
||||
description:
|
||||
'Yes, AstroWind is designed to be compatible with the latest versions of both Astro and Tailwind CSS. This ensures that you can harness the full capabilities of these technologies while benefiting from the features offered by AstroWind.',
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
{
|
||||
title: 'Can I use AstroWind for both personal and commercial projects?',
|
||||
description: `Certainly! AstroWind is versatile and can be used for a wide range of projects, including both personal and commercial endeavors. Whether you're building a professional portfolio, launching a startup, or creating a marketing website, AstroWind has you covered.`,
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
{
|
||||
title: 'What level of coding knowledge is required to use AstroWind?',
|
||||
description:
|
||||
'While some familiarity with HTML, CSS, and web development concepts is helpful, the user-friendly interface and customization options allow those with limited coding experience to create impressive websites. For more advanced users, AstroWind offers extensive customization capabilities.',
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
{
|
||||
title: 'Is customer support available for AstroWind users seeking guidance?',
|
||||
description: `Absolutely, our dedicated customer support team is here to assist you with any questions or challenges you may encounter. Feel free to reach out to us through our support channels, and we'll be happy to provide the help you need.`,
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</FAQs>
|
||||
|
||||
<!-- Steps2 Widget ****************** -->
|
||||
|
||||
<Steps2
|
||||
title="Reach out to us"
|
||||
subtitle="Have questions? Feel free to contact us using the form below. We're here to help!"
|
||||
callToAction={{
|
||||
text: 'Contact us',
|
||||
href: '/',
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
title: 'Email us',
|
||||
description: 'info@ourworld.tf',
|
||||
icon: 'tabler:mail',
|
||||
},
|
||||
{
|
||||
title: 'Call us',
|
||||
description: '+1 (234) 567-890',
|
||||
icon: 'tabler:headset',
|
||||
},
|
||||
{
|
||||
title: 'Follow us',
|
||||
description: '@example',
|
||||
icon: 'tabler:brand-twitter',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- BlogLatestPost Widget **************** -->
|
||||
|
||||
<BlogLatestPosts
|
||||
id="blog"
|
||||
title="Stay informed with AstroWind's blog"
|
||||
information={`Explore our collection of articles, guides, and tutorials on web development, design trends, and using AstroWind effectively for your projects.`}
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</BlogLatestPosts>
|
||||
</Layout>
|
317
src/pages/homes/startup.astro
Normal file
@@ -0,0 +1,317 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
|
||||
import Hero from '~/components/widgets/Hero.astro';
|
||||
import CallToAction from '~/components/widgets/CallToAction.astro';
|
||||
|
||||
import Features2 from '~/components/widgets/Features2.astro';
|
||||
import Features from '~/components/widgets/Features.astro';
|
||||
import Stats from '~/components/widgets/Stats.astro';
|
||||
import Features3 from '~/components/widgets/Features3.astro';
|
||||
import FAQs from '~/components/widgets/FAQs.astro';
|
||||
import Brands from '~/components/widgets/Brands.astro';
|
||||
|
||||
import { YouTube } from 'astro-embed';
|
||||
|
||||
const metadata = {
|
||||
title: 'Startup Landing Page',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<!-- Hero Widget ******************* -->
|
||||
|
||||
<Hero
|
||||
tagline="Startup Web Demo"
|
||||
actions={[
|
||||
{
|
||||
variant: 'primary',
|
||||
target: '_blank',
|
||||
text: 'Get templates',
|
||||
href: 'https://github.com/onwidget/astrowind',
|
||||
icon: 'tabler:download',
|
||||
},
|
||||
{ text: 'Learn more', href: '#features' },
|
||||
]}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
Improve <span class="hidden sm:inline">the online presence of</span> your <span
|
||||
class="text-accent dark:text-white highlight">Startup</span
|
||||
> with Astrowind templates
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
Step into the spotlight with <span class="font-semibold">Astrowind</span> templates, your pathway to fortifying your
|
||||
startup's digital footprint, fostering credibility, and expanding your reach.
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="image">
|
||||
<YouTube id="gxBkghlglTg" title="Astro just Launched.... Could it be the ultimate web framework?" />
|
||||
<style is:inline>
|
||||
lite-youtube {
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
</Fragment>
|
||||
</Hero>
|
||||
|
||||
<!-- Features2 Widget ************** -->
|
||||
|
||||
<Features2
|
||||
title="About us"
|
||||
subtitle="We believe in the magic of turning dreams into stunning realities. Founded by passionate developers with a shared vision, we set out to simplify the website creation process. Our templates bring together the innovation of Astro 4.0 and the versatility of Tailwind CSS, enabling you to express your unique brand identity like never before."
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Features2>
|
||||
|
||||
<!-- Stats Widget ****************** -->
|
||||
|
||||
<Stats
|
||||
title="Discover the impressive impact of Astrowind"
|
||||
subtitle="The numbers below reflect the trust our users have placed in us and the remarkable outcomes we've helped them achieve."
|
||||
stats={[
|
||||
{ title: 'Downloads', amount: '182K' },
|
||||
{ title: 'Websites Launched', amount: '87' },
|
||||
{ title: 'User Ratings', amount: '4.8' },
|
||||
{ title: 'Satisfied Clients', amount: '116K' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Brands Widget ****************** -->
|
||||
|
||||
<Brands
|
||||
title="Partnerships & Collaborations"
|
||||
subtitle="At Astrowind, we believe in the power of collaboration to drive innovation and create exceptional experiences."
|
||||
icons={[]}
|
||||
images={[
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2015/05/26/09/37/paypal-784404_1280.png',
|
||||
alt: 'Paypal',
|
||||
},
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2021/12/06/13/48/visa-6850402_1280.png',
|
||||
alt: 'Visa',
|
||||
},
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2013/10/01/10/29/ebay-189064_1280.png',
|
||||
alt: 'Ebay',
|
||||
},
|
||||
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2015/04/13/17/45/icon-720944_1280.png',
|
||||
alt: 'Youtube',
|
||||
},
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2013/02/12/09/07/microsoft-80658_1280.png',
|
||||
alt: 'Microsoft',
|
||||
},
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2015/04/23/17/41/node-js-736399_1280.png',
|
||||
alt: 'Node JS',
|
||||
},
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2015/10/31/12/54/google-1015751_1280.png',
|
||||
alt: 'Google',
|
||||
},
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2021/12/06/13/45/meta-6850393_1280.png',
|
||||
alt: 'Meta',
|
||||
},
|
||||
{
|
||||
src: 'https://cdn.pixabay.com/photo/2013/01/29/22/53/yahoo-76684_1280.png',
|
||||
alt: 'Yahoo',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Features2 Widget ************** -->
|
||||
|
||||
<Features2
|
||||
title="What services do we provide?"
|
||||
subtitle="We offer a wide range of website templates that suit various industries and purposes such as business, portfolio, e-commerce, blog, etc."
|
||||
items={[
|
||||
{
|
||||
title: 'Installation Instructions',
|
||||
description:
|
||||
'Offer clear instructions on how to download the purchased templates and install them on various website platforms or content management systems.',
|
||||
icon: 'flat-color-icons:document',
|
||||
},
|
||||
{
|
||||
title: 'Demo and Previews',
|
||||
description:
|
||||
'Provide interactive demos and previews that allow customers to see how their chosen template will look and function before making a purchase.',
|
||||
icon: 'flat-color-icons:template',
|
||||
},
|
||||
{
|
||||
title: 'Technical Support',
|
||||
description:
|
||||
'Providing customer support for any technical issues related to the templates or their implementation.',
|
||||
icon: 'flat-color-icons:voice-presentation',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Features2>
|
||||
|
||||
<!-- Features Widget *************** -->
|
||||
|
||||
<Features
|
||||
id="features"
|
||||
title="Main features of our templates"
|
||||
subtitle="Possess several key characteristics to effectively cater to the needs of startups and entrepreneurs."
|
||||
columns={3}
|
||||
items={[
|
||||
{
|
||||
title: 'Modern and Professional Design',
|
||||
description:
|
||||
'Have a contemporary design that reflects current design trends and gives a professional impression.',
|
||||
icon: 'tabler:artboard',
|
||||
},
|
||||
{
|
||||
title: 'Responsive and Mobile-Friendly',
|
||||
description: 'Adapt seamlessly to different screen sizes and devices to ensure a consistent experience.',
|
||||
icon: 'tabler:picture-in-picture',
|
||||
},
|
||||
{
|
||||
title: 'Customizability',
|
||||
description:
|
||||
'Easily customizable, allowing users to adapt the design, colors, typography, and content to match their brand identity.',
|
||||
icon: 'tabler:adjustments-horizontal',
|
||||
},
|
||||
{
|
||||
title: 'Fast Loading Times',
|
||||
description: 'Optimized for speed to ensure a smooth user experience and favorable search engine rankings.',
|
||||
icon: 'tabler:rocket',
|
||||
},
|
||||
{
|
||||
title: 'Search Engine Optimization (SEO)',
|
||||
description:
|
||||
'Incorporate SEO best practices in template structure and code to improve visibility in search engine results.',
|
||||
icon: 'tabler:arrows-right-left',
|
||||
},
|
||||
{
|
||||
title: 'Compatibility',
|
||||
description: 'The templates work seamlessly across various content management systems and website builders.',
|
||||
icon: 'tabler:plug-connected',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- FAQs Widget ******************* -->
|
||||
|
||||
<FAQs
|
||||
title="Frequently Asked Questions"
|
||||
items={[
|
||||
{
|
||||
title: 'What are landing page templates?',
|
||||
description:
|
||||
'Landing page templates are pre-designed web page layouts that are specifically created to serve as a foundation for building effective landing pages. These templates are designed to capture the attention of visitors and guide them towards a specific action or goal, such as signing up for a newsletter, making a purchase, or downloading a resource.',
|
||||
},
|
||||
{
|
||||
title: 'Why should I use a template?',
|
||||
description:
|
||||
'Some of the advantages are that they provide a ready-to-use structure, saving you significant time. Are designed with user-friendliness in mind and provide a cost-effective alternative, saving you money while still delivering a quality result.',
|
||||
},
|
||||
{
|
||||
title: 'Can I preview templates before buying?',
|
||||
description:
|
||||
'Yes, the templates allow you to preview them before making a purchase. There is a "Demo" button associated with each template.',
|
||||
},
|
||||
{
|
||||
title: 'Do I need technical skills to use a template?',
|
||||
description:
|
||||
'Advanced technical skills are not required to use a template, but having a basic understanding of web navigation and familiarity with using online tools can still be beneficial. If you have more specific customization needs, you might need to consult guides or reach out to customer support for assistance.',
|
||||
},
|
||||
{
|
||||
title: 'Can I use the template on multiple websites?',
|
||||
description:
|
||||
'No, the template comes with a single-use license, meaning you can use the template on one website or project only. Using the template on additional websites would require purchasing additional licenses.',
|
||||
},
|
||||
{
|
||||
title: 'What if I need help with customization?',
|
||||
description:
|
||||
"The templates provides a comprehensive step-by-step guide that walk you through the customization process. If you still have doubts, you can reach out to our customer support team. They can answer your questions, provide guidance on customization, and address any issues you're facing.",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</FAQs>
|
||||
|
||||
<!-- Features3 Widget ************** -->
|
||||
|
||||
<Features3
|
||||
title="Let us know how we can help"
|
||||
subtitle="We’re here to help and answer any question you might have."
|
||||
columns={4}
|
||||
items={[
|
||||
{
|
||||
title: 'Phone',
|
||||
icon: 'tabler:phone',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Call us',
|
||||
href: '/',
|
||||
variant: 'link',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
icon: 'tabler:mail',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Write to us',
|
||||
href: '/',
|
||||
variant: 'link',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Chat with sales',
|
||||
icon: 'tabler:message-circle',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Start chatting',
|
||||
href: '/',
|
||||
variant: 'link',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Chat with support',
|
||||
icon: 'tabler:message-circle',
|
||||
callToAction: {
|
||||
target: '_blank',
|
||||
text: 'Start chatting',
|
||||
href: '/',
|
||||
variant: 'link',
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- CallToAction Widget *********** -->
|
||||
|
||||
<CallToAction
|
||||
actions={[
|
||||
{
|
||||
variant: 'primary',
|
||||
target: '_blank',
|
||||
text: 'Get templates',
|
||||
href: 'https://github.com/onwidget/astrowind',
|
||||
icon: 'tabler:download',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Fragment slot="title">Be a part of our vision</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
Discover a dynamic work environment, unparalleled growth opportunities, and the chance to make a meaningful
|
||||
impact.
|
||||
</Fragment>
|
||||
</CallToAction>
|
||||
</Layout>
|
264
src/pages/index.astro
Normal file
@@ -0,0 +1,264 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
|
||||
import Hero from '~/components/widgets/Hero.astro';
|
||||
import Hero2 from '~/components/widgets/Hero2.astro';
|
||||
import Note from '~/components/widgets/Note.astro';
|
||||
import Features from '~/components/widgets/Features.astro';
|
||||
import Features2 from '~/components/widgets/Features2.astro';
|
||||
import Features0 from '~/components/widgets/Features0.astro';
|
||||
import Steps from '~/components/widgets/Steps.astro';
|
||||
import Content from '~/components/widgets/Content.astro';
|
||||
import BlogLatestPosts from '~/components/widgets/BlogLatestPosts.astro';
|
||||
import FAQs from '~/components/widgets/FAQs.astro';
|
||||
import Stats from '~/components/widgets/Stats.astro';
|
||||
import CallToAction from '~/components/widgets/CallToAction.astro';
|
||||
import Steps2 from '~/components/widgets/Steps2.astro';
|
||||
|
||||
const metadata = {
|
||||
title: 'OurWorld Digital FreeZone',
|
||||
ignoreTitleTemplate: true,
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<!-- Hero Widget ******************* -->
|
||||
|
||||
<Hero
|
||||
tagline="Welcome to"
|
||||
actions={[
|
||||
{
|
||||
variant: 'primary',
|
||||
text: 'Get Started',
|
||||
href: '/contact',
|
||||
target: '_blank',
|
||||
icon: 'tabler:square-rounded-arrow-right',
|
||||
},
|
||||
{ text: 'Learn more', href: '#features', },
|
||||
]}
|
||||
image={{ src: '~/assets/images/ow.svg', alt: 'Hero Image' }}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
OurWorld Digital FreeZone
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
<span class="hidden sm:inline">
|
||||
<span class="font-semibold">ODFZ</span> OurWorld Digital Free Zone is a collaboration between the Government of Zanzibar and OurWorld Venture Creator. We are the world's first 100% digital free zone, accessible and affordable for all. Removing complexity from doing business.
|
||||
</span>
|
||||
</Fragment>
|
||||
</Hero>
|
||||
|
||||
|
||||
<!-- Features2 Widget ************** -->
|
||||
|
||||
<Features2
|
||||
id="about"
|
||||
title="About us"
|
||||
subtitle="OurWorld Digital FreeZone offer cutting-edge digital infrastructure, simplified financial transactions, and competitive tax structures to foster innovation and economic growth. With a clear regulatory framework and robust dispute resolution, we ensure a stable environment for businesses to thrive, removing complexity and enabling effortless global expansion."
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</Features2>
|
||||
|
||||
<!-- Features Widget *************** -->
|
||||
|
||||
<Features
|
||||
id="features"
|
||||
tagline="Features"
|
||||
title="Why Choose a Digital Free Zone"
|
||||
subtitle="Our automated onboarding process, will ensure compliance with KYC and AML regulations at ease and efficiency."
|
||||
items={[
|
||||
{
|
||||
title: 'Fully Automated Onboarding',
|
||||
description:
|
||||
'A digital free zone fosters effortless collaboration among individuals and organizations by eliminating intermediaries.',
|
||||
icon: 'tabler:automation',
|
||||
},
|
||||
{
|
||||
title: 'Banking & Web3 Compatible',
|
||||
description:
|
||||
'Seamlessly manage both fiat and digital currencies with our comprehensive banking solutions compatible with Web3 technologies.',
|
||||
icon: 'tabler:circle-dashed-check',
|
||||
},
|
||||
{
|
||||
title: 'Built-in Legal & Tax Settlement',
|
||||
description:
|
||||
'Experience hassle-free business operations, as we provide built-in legal and tax settlement services, simplifying legal obligations.',
|
||||
icon: 'tabler:gavel',
|
||||
},
|
||||
{
|
||||
title: 'Affordable & Flexible Company Licenses',
|
||||
description:
|
||||
'Ourworld Free Zone offers cost-effective and flexible company licenses, making it easier to kickstart your entrepreneurial journey.',
|
||||
icon: 'tabler:award',
|
||||
},
|
||||
{
|
||||
title: 'Sovereign Economic Jurisdiction',
|
||||
description:
|
||||
"Ourworld Free Zone empowers businesses to operate within an independent economic environment, fostering growth and prosperity.",
|
||||
icon: 'tabler:flag',
|
||||
},
|
||||
{
|
||||
title: 'Powered by Data Sovereign Tech',
|
||||
description:
|
||||
'Your Privacy, Your Data, Your Security. Our Quantum Safe Storage will empower you to safeguard your digital information.',
|
||||
icon: 'tabler:rocket',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Features2 Widget ************** -->
|
||||
|
||||
<Features0
|
||||
id="zanzibar"
|
||||
title="Why Zanzibar?"
|
||||
subtitle="With proactive business-friendly policies and Tanzania's robust economic growth, Zanzibar emerges as a promising hub for innovation and investment in East Africa."
|
||||
items={[
|
||||
{
|
||||
title: 'Strategic Location',
|
||||
description:
|
||||
'Situated at the intersection of Africa and the Indian Ocean, Zanzibar boasts a rich cultural heritage and provides convenient access to both regional and global markets.',
|
||||
icon: 'flat-color-icons:collect',
|
||||
},
|
||||
{
|
||||
title: 'Rapid Economic Growth',
|
||||
description:
|
||||
'In recent years, Tanzania has maintained a robust economic growth rate averaging 6-7% annually, underscoring its strong economic potential and stability.',
|
||||
icon: 'flat-color-icons:flash-on',
|
||||
},
|
||||
{
|
||||
title: 'Business-Friendly Policies',
|
||||
description:
|
||||
'Tanzania was recognized among the top 10 most improved economies globally in 2020 by the World Bank, demonstrating a proactive approach in adapting agile business policies.',
|
||||
icon: 'flat-color-icons:positive-dynamic',
|
||||
},
|
||||
]}
|
||||
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="bg-grad"></div>
|
||||
</Fragment>
|
||||
</Features2>
|
||||
|
||||
<!-- Hero2 Widget ******************* -->
|
||||
|
||||
<Hero2
|
||||
id="benefits"
|
||||
tagline="Benefits"
|
||||
actions={[
|
||||
{ variant: 'primary', target: '_blank', text: 'Sign Up Now', href: '/contact' },
|
||||
{ text: 'Learn more', href: 'https://ourworldfreezone.github.io/info_freezone/intro/intro_readme.html', target: '_blank', },
|
||||
]}
|
||||
image={{
|
||||
src: 'src/assets/images/simple.png',
|
||||
alt: 'img',
|
||||
}}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
Simple. Streamlined.
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
<span class="hidden sm:inline">
|
||||
For the entrepreneur, the digital nomad, or any company, we provide a revolutionary platform where you can obtain a digital company license, a bank account supporting both fiat and cryptocurrencies, and handle your legal requirements and taxes, all within a single, streamlined platform.<br>
|
||||
<br>Say goodbye to complexities and hello to a new era of simplicity and efficiency, empowering your business to thrive like never before.
|
||||
</Fragment>
|
||||
</Hero2>
|
||||
|
||||
|
||||
<!-- BlogLatestPost Widget **************** -->
|
||||
|
||||
<BlogLatestPosts
|
||||
id="blog"
|
||||
title="Stay Informed with ODFZ's Blog"
|
||||
information={`Explore our comprehensive collection of insightful articles and the latest news updates about OurWorld Digital Freezone's ongoing and upcoming developments. Stay informed with exclusive insights into our innovative projects and initiatives shaping the future of digital environments.`}
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</BlogLatestPosts>
|
||||
|
||||
<!-- Hero2 Widget ******************* -->
|
||||
|
||||
<Hero2
|
||||
id="presale"
|
||||
tagline="Company License Presale"
|
||||
title="Secure your spot with a Special Offer Price"
|
||||
subtitle="Be among the first to secure your company license with our exclusive presale offer at a special price. Embrace the future of digital entrepreneurship with OurWorld Free Zone and kickstart your business journey with unprecedented advantages."
|
||||
actions={[
|
||||
{ variant: 'primary', text: 'Join the Presale', href: '#', icon: 'tabler:square-rounded-arrow-right' },
|
||||
{ text: 'Learn more', href: 'https://ourworldfreezone.github.io/info_freezone/intro/intro_readme.html', target: '_blank', },
|
||||
]}
|
||||
image={{
|
||||
src: 'src/assets/images/zanzibar.png',
|
||||
alt: 'Store with a Coming Soon sign. Pre-launch Landing Page',
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
<!-- FAQs Widget ******************* -->
|
||||
|
||||
<FAQs
|
||||
id="faq"
|
||||
title="Frequently Asked Questions"
|
||||
items={[
|
||||
{
|
||||
title: 'What is OurWorld Digital FreeZone?',
|
||||
description:
|
||||
'OurWorld Digital FreeZone is the worlds first 100% digital free zone, designed to simplify and enhance business operations through advanced digital infrastructure. Strategically located in Zanzibar, it offers businesses seamless access to regional and global markets, fostering innovation and economic growth.',
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
{
|
||||
title: 'What are the benefits of operating within OurWorld Digital FreeZone?',
|
||||
description: `Businesses within OurWorld Digital FreeZone enjoy numerous benefits, including reduced tax burdens, streamlined financial transactions, and a supportive regulatory environment. The digital infrastructure facilitates efficient collaboration and global expansion, making it an ideal hub for ecommerce and service providers.`,
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
{
|
||||
title: 'How does OurWorld Digital FreeZone support financial transactions?',
|
||||
description:
|
||||
'OurWorld Digital FreeZone offers a frictionless banking environment, enabling businesses to send and receive money effortlessly. This reduces the complexities and fees associated with traditional banking systems, ensuring smooth financial operations.',
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
{
|
||||
title: 'What makes Zanzibar an ideal location for a digital free zone?',
|
||||
description: `Zanzibar's strategic location at the crossroads of Africa and the Indian Ocean provides a culturally rich environment and exceptional access to regional and global markets. Combined with Tanzania's robust economic growth and proactive business-friendly policies, Zanzibar is an emerging hub for innovation and investment in East Africa.`,
|
||||
icon: 'tabler:chevrons-right',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Fragment slot="bg">
|
||||
<div class="absolute inset-0 bg-blue-50 dark:bg-transparent"></div>
|
||||
</Fragment>
|
||||
</FAQs>
|
||||
|
||||
<!-- Steps2 Widget ****************** -->
|
||||
|
||||
<Steps2
|
||||
id="cta"
|
||||
title="Reach out to us"
|
||||
subtitle="Have questions? Feel free to reach out to us with any inquiries or collaborations; we're here to assist you!"
|
||||
callToAction={{
|
||||
text: 'Contact us',
|
||||
href: '/contact',
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
title: 'Email us',
|
||||
description: '<a href=\mailto:"info@ourworld.tf" target=\"_blank\"><u>info@ourworld.tf</u></a>',
|
||||
icon: 'tabler:mail',
|
||||
},
|
||||
{
|
||||
title: 'Chat',
|
||||
description: '<a href=\"https://threefoldfaq.crisp.help/en/\" target=\"_blank\"><u>Visit Our Customer Service Portal</u></a>',
|
||||
icon: 'tabler:headset',
|
||||
},
|
||||
{
|
||||
title: 'Follow us',
|
||||
description: '<a href=\"https://t.me/threefoldnews/\" target=\"_blank\"><u>Join Our Telegram</u></a>',
|
||||
icon: 'tabler:brand-telegram',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Layout>
|
41
src/pages/landing/click-through.astro
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
import Layout from '~/layouts/LandingLayout.astro';
|
||||
|
||||
import Hero2 from '~/components/widgets/Hero2.astro';
|
||||
import CallToAction from '~/components/widgets/CallToAction.astro';
|
||||
|
||||
const metadata = {
|
||||
title: 'Click-through Landing Page Demo',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<!-- Hero2 Widget ******************* -->
|
||||
|
||||
<Hero2
|
||||
tagline="Click-through Demo"
|
||||
title="Click-through Landing Page: The Perfect Bridge to Conversion!"
|
||||
subtitle="Learn how to design a Click-Through Landing Page that seamlessly guides visitors to your main offer."
|
||||
actions={[
|
||||
{ variant: 'primary', text: 'Call to Action', href: '#', icon: 'tabler:square-rounded-arrow-right' },
|
||||
{ text: 'Learn more', href: '#' },
|
||||
]}
|
||||
image={{
|
||||
src: 'https://images.unsplash.com/photo-1516321497487-e288fb19713f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
||||
alt: 'Click-through Landing Page Hero Image',
|
||||
}}
|
||||
/>
|
||||
|
||||
<CallToAction
|
||||
title="Coming soon"
|
||||
subtitle="We are working on the content of these demo pages. You will see them very soon. Stay tuned Stay tuned!"
|
||||
actions={[
|
||||
{
|
||||
variant: 'primary',
|
||||
text: 'Download Template',
|
||||
href: 'https://github.com/onwidget/astrowind',
|
||||
icon: 'tabler:download',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Layout>
|