Merge branch 'main' into migration-astro-v5
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@onwidget/astrowind",
|
"name": "@onwidget/astrowind",
|
||||||
"version": "1.0.0-beta.47",
|
"version": "1.0.0-beta.49",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@onwidget/astrowind",
|
"name": "@onwidget/astrowind",
|
||||||
"version": "1.0.0-beta.47",
|
"version": "1.0.0-beta.49",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/rss": "^4.0.10",
|
"@astrojs/rss": "^4.0.10",
|
||||||
"@astrojs/sitemap": "^3.2.1",
|
"@astrojs/sitemap": "^3.2.1",
|
||||||
@@ -10782,4 +10782,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@onwidget/astrowind",
|
"name": "@onwidget/astrowind",
|
||||||
"version": "1.0.0-beta.47",
|
"version": "1.0.0-beta.49",
|
||||||
"description": "AstroWind: A free template using Astro 5.0 and Tailwind CSS. Astro starter theme.",
|
"description": "AstroWind: A free template using Astro 5.0 and Tailwind CSS. Astro starter theme.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
@@ -17,7 +17,9 @@ const image = await findImage(post.image);
|
|||||||
const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') : '';
|
const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') : '';
|
||||||
---
|
---
|
||||||
|
|
||||||
<article class="mb-6 transition">
|
<article
|
||||||
|
class="mb-6 transition intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
|
>
|
||||||
<div class="relative md:h-64 bg-gray-400 dark:bg-slate-700 rounded shadow-lg mb-6">
|
<div class="relative md:h-64 bg-gray-400 dark:bg-slate-700 rounded shadow-lg mb-6">
|
||||||
{
|
{
|
||||||
image &&
|
image &&
|
||||||
|
@@ -21,7 +21,9 @@ const image = (await findImage(post.image)) as ImageMetadata | undefined;
|
|||||||
const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') : '';
|
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' : ''}`}>
|
<article
|
||||||
|
class={`max-w-md mx-auto md:max-w-none grid gap-6 md:gap-8 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade ${image ? 'md:grid-cols-2' : ''}`}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
image &&
|
image &&
|
||||||
(link ? (
|
(link ? (
|
||||||
|
@@ -18,7 +18,10 @@ const relatedPosts = post.tags ? await getRelatedPosts(post, 4) : [];
|
|||||||
{
|
{
|
||||||
APP_BLOG.isRelatedPostsEnabled ? (
|
APP_BLOG.isRelatedPostsEnabled ? (
|
||||||
<BlogHighlightedPosts
|
<BlogHighlightedPosts
|
||||||
classes={{ container: 'pt-0 lg:pt-0 md:pt-0' }}
|
classes={{
|
||||||
|
container:
|
||||||
|
'pt-0 lg:pt-0 md:pt-0 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade',
|
||||||
|
}}
|
||||||
title="Related Posts"
|
title="Related Posts"
|
||||||
linkText="View All Posts"
|
linkText="View All Posts"
|
||||||
linkUrl={getBlogPermalink()}
|
linkUrl={getBlogPermalink()}
|
||||||
|
@@ -20,7 +20,11 @@ const { post, url } = Astro.props;
|
|||||||
|
|
||||||
<section class="py-8 sm:py-16 lg:py-20 mx-auto">
|
<section class="py-8 sm:py-16 lg:py-20 mx-auto">
|
||||||
<article>
|
<article>
|
||||||
<header class={post.image ? '' : ''}>
|
<header
|
||||||
|
class={post.image
|
||||||
|
? 'intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade'
|
||||||
|
: 'intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade'}
|
||||||
|
>
|
||||||
<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">
|
<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>
|
<p>
|
||||||
<Icon name="tabler:clock" class="w-4 h-4 inline-block -mt-0.5 dark:text-gray-400" />
|
<Icon name="tabler:clock" class="w-4 h-4 inline-block -mt-0.5 dark:text-gray-400" />
|
||||||
|
@@ -160,3 +160,96 @@ import { UI } from 'astrowind:config';
|
|||||||
onPageShow();
|
onPageShow();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script is:inline>
|
||||||
|
/* Inspired by: https://github.com/heidkaemper/tailwindcss-intersect */
|
||||||
|
const Observer = {
|
||||||
|
observer: null,
|
||||||
|
delayBetweenAnimations: 100,
|
||||||
|
animationCounter: 0,
|
||||||
|
|
||||||
|
start() {
|
||||||
|
const selectors = [
|
||||||
|
'[class*=" intersect:"]',
|
||||||
|
'[class*=":intersect:"]',
|
||||||
|
'[class^="intersect:"]',
|
||||||
|
'[class="intersect"]',
|
||||||
|
'[class*=" intersect "]',
|
||||||
|
'[class^="intersect "]',
|
||||||
|
'[class$=" intersect"]',
|
||||||
|
];
|
||||||
|
|
||||||
|
const elements = Array.from(document.querySelectorAll(selectors.join(',')));
|
||||||
|
|
||||||
|
const getThreshold = (element) => {
|
||||||
|
if (element.classList.contains('intersect-full')) return 0.99;
|
||||||
|
if (element.classList.contains('intersect-half')) return 0.5;
|
||||||
|
if (element.classList.contains('intersect-quarter')) return 0.25;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
elements.forEach((el) => {
|
||||||
|
el.setAttribute('no-intersect', '');
|
||||||
|
el._intersectionThreshold = getThreshold(el);
|
||||||
|
});
|
||||||
|
|
||||||
|
const callback = (entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const target = entry.target;
|
||||||
|
const intersectionRatio = entry.intersectionRatio;
|
||||||
|
const threshold = target._intersectionThreshold;
|
||||||
|
|
||||||
|
if (target.classList.contains('intersect-no-queue')) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
target.removeAttribute('no-intersect');
|
||||||
|
if (target.classList.contains('intersect-once')) {
|
||||||
|
this.observer.unobserve(target);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target.setAttribute('no-intersect', '');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersectionRatio >= threshold) {
|
||||||
|
if (!target.hasAttribute('data-animated')) {
|
||||||
|
target.removeAttribute('no-intersect');
|
||||||
|
target.setAttribute('data-animated', 'true');
|
||||||
|
|
||||||
|
const delay = this.animationCounter * this.delayBetweenAnimations;
|
||||||
|
this.animationCounter++;
|
||||||
|
|
||||||
|
target.style.transitionDelay = `${delay}ms`;
|
||||||
|
target.style.animationDelay = `${delay}ms`;
|
||||||
|
|
||||||
|
if (target.classList.contains('intersect-once')) {
|
||||||
|
this.observer.unobserve(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target.setAttribute('no-intersect', '');
|
||||||
|
target.removeAttribute('data-animated');
|
||||||
|
target.style.transitionDelay = '';
|
||||||
|
target.style.animationDelay = '';
|
||||||
|
|
||||||
|
this.animationCounter = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.observer = new IntersectionObserver(callback.bind(this), { threshold: [0, 0.25, 0.5, 0.99] });
|
||||||
|
|
||||||
|
elements.forEach((el) => {
|
||||||
|
this.observer.observe(el);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Observer.start();
|
||||||
|
|
||||||
|
document.addEventListener('astro:after-swap', () => {
|
||||||
|
Observer.start();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@@ -41,15 +41,12 @@ const _image = await findImage(props.src);
|
|||||||
|
|
||||||
let image: ImageType | undefined = undefined;
|
let image: ImageType | undefined = undefined;
|
||||||
|
|
||||||
if (typeof _image === 'string') {
|
if (
|
||||||
if ((_image.startsWith('http://') || _image.startsWith('https://')) && isUnpicCompatible(_image)) {
|
typeof _image === 'string' &&
|
||||||
image = await getImagesOptimized(_image, props, unpicOptimizer);
|
(_image.startsWith('http://') || _image.startsWith('https://')) &&
|
||||||
} else {
|
isUnpicCompatible(_image)
|
||||||
image = {
|
) {
|
||||||
src: _image,
|
image = await getImagesOptimized(_image, props, unpicOptimizer);
|
||||||
attributes: { ...props, src: undefined },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else if (_image) {
|
} else if (_image) {
|
||||||
image = await getImagesOptimized(_image, props, astroAsseetsOptimizer);
|
image = await getImagesOptimized(_image, props, astroAsseetsOptimizer);
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ const {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
||||||
<div>
|
<div class="intersect-once motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade">
|
||||||
<div class={twMerge('flex flex-row max-w-md', panelClass, itemClasses?.panel)}>
|
<div class={twMerge('flex flex-row max-w-md', panelClass, itemClasses?.panel)}>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
{(icon || defaultIcon) && (
|
{(icon || defaultIcon) && (
|
||||||
|
@@ -33,7 +33,13 @@ const {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
||||||
<div class={twMerge('relative flex flex-col', panelClass, itemClasses?.panel)}>
|
<div
|
||||||
|
class={twMerge(
|
||||||
|
'relative flex flex-col intersect-once intersect-quarter intercept-no-queue motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade',
|
||||||
|
panelClass,
|
||||||
|
itemClasses?.panel
|
||||||
|
)}
|
||||||
|
>
|
||||||
{(icon || defaultIcon) && (
|
{(icon || defaultIcon) && (
|
||||||
<Icon name={icon || defaultIcon} class={twMerge('mb-2 w-10 h-10', defaultIconClass, itemClasses?.icon)} />
|
<Icon name={icon || defaultIcon} class={twMerge('mb-2 w-10 h-10', defaultIconClass, itemClasses?.icon)} />
|
||||||
)}
|
)}
|
||||||
|
@@ -24,7 +24,13 @@ const {
|
|||||||
items && items.length && (
|
items && items.length && (
|
||||||
<div class={containerClass}>
|
<div class={containerClass}>
|
||||||
{items.map(({ title, description, icon, classes: itemClasses = {} }, index = 0) => (
|
{items.map(({ title, description, icon, classes: itemClasses = {} }, index = 0) => (
|
||||||
<div class={twMerge('flex', panelClass, itemClasses?.panel)}>
|
<div
|
||||||
|
class={twMerge(
|
||||||
|
'flex intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade',
|
||||||
|
panelClass,
|
||||||
|
itemClasses?.panel
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div class="flex flex-col items-center mr-4 rtl:mr-0 rtl:ml-4">
|
<div class="flex flex-col items-center mr-4 rtl:mr-0 rtl:ml-4">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
|
@@ -22,7 +22,10 @@ const WrapperTag = as;
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class:list={[
|
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),
|
twMerge(
|
||||||
|
'relative mx-auto max-w-7xl px-4 md:px-6 py-12 md:py-16 lg:py-20 text-default intersect-once intersect-quarter intercept-no-queue motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade',
|
||||||
|
containerClass
|
||||||
|
),
|
||||||
{ dark: isDark },
|
{ dark: isDark },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
@@ -28,7 +28,9 @@ const { socialLinks = [], secondaryLinks = [], links = [], footNote = '', theme
|
|||||||
|
|
||||||
<footer class:list={[{ dark: theme === 'dark' }, 'relative border-t border-gray-200 dark:border-slate-800 not-prose']}>
|
<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="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="relative max-w-7xl mx-auto px-4 sm:px-6 dark:text-slate-300 intersect-once intersect-quarter intercept-no-queue motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
|
>
|
||||||
<div class="grid grid-cols-12 gap-4 gap-y-8 sm:gap-8 py-8 md:py-12">
|
<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="col-span-12 lg:col-span-4">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
|
@@ -90,7 +90,10 @@ const currentPath = `/${trimSlash(new URL(Astro.url).pathname)}`;
|
|||||||
<li class={links?.length ? 'dropdown' : ''}>
|
<li class={links?.length ? 'dropdown' : ''}>
|
||||||
{links?.length ? (
|
{links?.length ? (
|
||||||
<>
|
<>
|
||||||
<button type="button" class="hover:text-link dark:hover:text-white px-4 py-3 flex items-center">
|
<button
|
||||||
|
type="button"
|
||||||
|
class="hover:text-link dark:hover:text-white px-4 py-3 flex items-center whitespace-nowrap"
|
||||||
|
>
|
||||||
{text}{' '}
|
{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" />
|
<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>
|
</button>
|
||||||
@@ -113,7 +116,7 @@ const currentPath = `/${trimSlash(new URL(Astro.url).pathname)}`;
|
|||||||
) : (
|
) : (
|
||||||
<a
|
<a
|
||||||
class:list={[
|
class:list={[
|
||||||
'hover:text-link dark:hover:text-white px-4 py-3 flex items-center',
|
'hover:text-link dark:hover:text-white px-4 py-3 flex items-center whitespace-nowrap',
|
||||||
{ 'aw-link-active': href === currentPath },
|
{ 'aw-link-active': href === currentPath },
|
||||||
]}
|
]}
|
||||||
href={href}
|
href={href}
|
||||||
|
@@ -31,7 +31,7 @@ const {
|
|||||||
{
|
{
|
||||||
tagline && (
|
tagline && (
|
||||||
<p
|
<p
|
||||||
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase"
|
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
set:html={tagline}
|
set:html={tagline}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -39,16 +39,23 @@ const {
|
|||||||
{
|
{
|
||||||
title && (
|
title && (
|
||||||
<h1
|
<h1
|
||||||
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
|
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
set:html={title}
|
set:html={title}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div class="max-w-3xl mx-auto">
|
<div class="max-w-3xl mx-auto">
|
||||||
{subtitle && <p class="text-xl text-muted mb-6 dark:text-slate-300" set:html={subtitle} />}
|
{
|
||||||
|
subtitle && (
|
||||||
|
<p
|
||||||
|
class="text-xl text-muted mb-6 dark:text-slate-300 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
|
set:html={subtitle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
actions && (
|
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">
|
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade">
|
||||||
{Array.isArray(actions) ? (
|
{Array.isArray(actions) ? (
|
||||||
actions.map((action) => (
|
actions.map((action) => (
|
||||||
<div class="flex w-full sm:w-auto">
|
<div class="flex w-full sm:w-auto">
|
||||||
@@ -64,7 +71,9 @@ const {
|
|||||||
</div>
|
</div>
|
||||||
{content && <Fragment set:html={content} />}
|
{content && <Fragment set:html={content} />}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div
|
||||||
|
class="intersect-once intercept-no-queue intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
|
>
|
||||||
{
|
{
|
||||||
image && (
|
image && (
|
||||||
<div class="relative m-auto max-w-5xl">
|
<div class="relative m-auto max-w-5xl">
|
||||||
|
@@ -31,7 +31,7 @@ const {
|
|||||||
{
|
{
|
||||||
tagline && (
|
tagline && (
|
||||||
<p
|
<p
|
||||||
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase"
|
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase intersect-once motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter"
|
||||||
set:html={tagline}
|
set:html={tagline}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -39,17 +39,24 @@ const {
|
|||||||
{
|
{
|
||||||
title && (
|
title && (
|
||||||
<h1
|
<h1
|
||||||
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
|
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200 intersect-once motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter"
|
||||||
set:html={title}
|
set:html={title}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div class="max-w-3xl mx-auto lg:max-w-none">
|
<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} />}
|
{
|
||||||
|
subtitle && (
|
||||||
|
<p
|
||||||
|
class="text-xl text-muted mb-6 dark:text-slate-300 intersect-once motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter"
|
||||||
|
set:html={subtitle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
actions && (
|
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">
|
<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 intersect-once motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter">
|
||||||
{Array.isArray(actions) ? (
|
{Array.isArray(actions) ? (
|
||||||
actions.map((action) => (
|
actions.map((action) => (
|
||||||
<div class="flex w-full sm:w-auto">
|
<div class="flex w-full sm:w-auto">
|
||||||
@@ -68,7 +75,7 @@ const {
|
|||||||
<div class="basis-1/2">
|
<div class="basis-1/2">
|
||||||
{
|
{
|
||||||
image && (
|
image && (
|
||||||
<div class="relative m-auto max-w-5xl">
|
<div class="relative m-auto max-w-5xl intersect-once intercept-no-queue motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter">
|
||||||
{typeof image === 'string' ? (
|
{typeof image === 'string' ? (
|
||||||
<Fragment set:html={image} />
|
<Fragment set:html={image} />
|
||||||
) : (
|
) : (
|
||||||
|
@@ -30,7 +30,7 @@ const {
|
|||||||
{
|
{
|
||||||
tagline && (
|
tagline && (
|
||||||
<p
|
<p
|
||||||
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase"
|
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
set:html={tagline}
|
set:html={tagline}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -38,14 +38,23 @@ const {
|
|||||||
{
|
{
|
||||||
title && (
|
title && (
|
||||||
<h1
|
<h1
|
||||||
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
|
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
set:html={title}
|
set:html={title}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div class="max-w-3xl mx-auto">
|
<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">
|
subtitle && (
|
||||||
|
<p
|
||||||
|
class="text-xl text-muted mb-6 dark:text-slate-300 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
|
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 intersect-once intersect-quarter motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade"
|
||||||
|
>
|
||||||
{
|
{
|
||||||
callToAction && (
|
callToAction && (
|
||||||
<div class="flex w-full sm:w-auto">
|
<div class="flex w-full sm:w-auto">
|
||||||
|
@@ -25,7 +25,7 @@ const {
|
|||||||
{
|
{
|
||||||
prices &&
|
prices &&
|
||||||
prices.map(({ title, subtitle, price, period, items, callToAction, hasRibbon = false, ribbonTitle }) => (
|
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">
|
<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 intersect-once motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter">
|
||||||
{price && period && (
|
{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">
|
<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 && (
|
{hasRibbon && ribbonTitle && (
|
||||||
|
@@ -23,7 +23,7 @@ const {
|
|||||||
{
|
{
|
||||||
stats &&
|
stats &&
|
||||||
stats.map(({ amount, title, icon }) => (
|
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">
|
<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 intersect-once motion-safe:md:opacity-0 motion-safe:md:intersect:animate-fade intersect-quarter">
|
||||||
{icon && (
|
{icon && (
|
||||||
<div class="flex items-center justify-center mx-auto mb-4 text-primary">
|
<div class="flex items-center justify-center mx-auto mb-4 text-primary">
|
||||||
<Icon name={icon} class="w-10 h-10" />
|
<Icon name={icon} class="w-10 h-10" />
|
||||||
|
@@ -26,7 +26,7 @@ const {
|
|||||||
{
|
{
|
||||||
testimonials &&
|
testimonials &&
|
||||||
testimonials.map(({ title, testimonial, name, job, image }) => (
|
testimonials.map(({ title, testimonial, name, job, image }) => (
|
||||||
<div class="flex h-auto">
|
<div class="flex h-auto intersect-once motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter">
|
||||||
<div class="flex flex-col p-4 md:p-6 rounded-md shadow-xl dark:shadow-none dark:border dark:border-slate-600">
|
<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>}
|
{title && <h2 class="text-lg font-medium leading-6 pb-4">{title}</h2>}
|
||||||
{testimonial && (
|
{testimonial && (
|
||||||
|
@@ -22,13 +22,16 @@ export interface ImageProps extends Omit<HTMLAttributes<'img'>, 'src'> {
|
|||||||
widths?: number[] | null;
|
widths?: number[] | null;
|
||||||
aspectRatio?: string | number | null;
|
aspectRatio?: string | number | null;
|
||||||
objectPosition?: string;
|
objectPosition?: string;
|
||||||
|
|
||||||
|
format?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ImagesOptimizer = (
|
export type ImagesOptimizer = (
|
||||||
image: ImageMetadata | string,
|
image: ImageMetadata | string,
|
||||||
breakpoints: number[],
|
breakpoints: number[],
|
||||||
width?: number,
|
width?: number,
|
||||||
height?: number
|
height?: number,
|
||||||
|
format?: string
|
||||||
) => Promise<Array<{ src: string; width: number }>>;
|
) => Promise<Array<{ src: string; width: number }>>;
|
||||||
|
|
||||||
/* ******* */
|
/* ******* */
|
||||||
@@ -209,17 +212,25 @@ const getBreakpoints = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* ** */
|
/* ** */
|
||||||
export const astroAsseetsOptimizer: ImagesOptimizer = async (image, breakpoints, _width, _height) => {
|
export const astroAsseetsOptimizer: ImagesOptimizer = async (
|
||||||
|
image,
|
||||||
|
breakpoints,
|
||||||
|
_width,
|
||||||
|
_height,
|
||||||
|
format = undefined
|
||||||
|
) => {
|
||||||
if (!image) {
|
if (!image) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
breakpoints.map(async (w: number) => {
|
breakpoints.map(async (w: number) => {
|
||||||
const url = (await getImage({ src: image, width: w, inferSize: true })).src;
|
const result = await getImage({ src: image, width: w, inferSize: true, ...(format ? { format: format } : {}) });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
src: url,
|
src: result?.src,
|
||||||
width: w,
|
width: result?.attributes?.width ?? w,
|
||||||
|
height: result?.attributes?.height,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -230,7 +241,7 @@ export const isUnpicCompatible = (image: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* ** */
|
/* ** */
|
||||||
export const unpicOptimizer: ImagesOptimizer = async (image, breakpoints, width, height) => {
|
export const unpicOptimizer: ImagesOptimizer = async (image, breakpoints, width, height, format = undefined) => {
|
||||||
if (!image || typeof image !== 'string') {
|
if (!image || typeof image !== 'string') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -242,16 +253,19 @@ export const unpicOptimizer: ImagesOptimizer = async (image, breakpoints, width,
|
|||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
breakpoints.map(async (w: number) => {
|
breakpoints.map(async (w: number) => {
|
||||||
|
const _height = width && height ? computeHeight(w, width / height) : height;
|
||||||
const url =
|
const url =
|
||||||
transformUrl({
|
transformUrl({
|
||||||
url: image,
|
url: image,
|
||||||
width: w,
|
width: w,
|
||||||
height: width && height ? computeHeight(w, width / height) : height,
|
height: _height,
|
||||||
cdn: urlParsed.cdn,
|
cdn: urlParsed.cdn,
|
||||||
|
...(format ? { format: format } : {}),
|
||||||
}) || image;
|
}) || image;
|
||||||
return {
|
return {
|
||||||
src: String(url),
|
src: String(url),
|
||||||
width: w,
|
width: w,
|
||||||
|
height: _height,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -270,6 +284,7 @@ export async function getImagesOptimized(
|
|||||||
widths,
|
widths,
|
||||||
layout = 'constrained',
|
layout = 'constrained',
|
||||||
style = '',
|
style = '',
|
||||||
|
format,
|
||||||
...rest
|
...rest
|
||||||
}: ImageProps,
|
}: ImageProps,
|
||||||
transform: ImagesOptimizer = () => Promise.resolve([])
|
transform: ImagesOptimizer = () => Promise.resolve([])
|
||||||
@@ -312,7 +327,7 @@ export async function getImagesOptimized(
|
|||||||
let breakpoints = getBreakpoints({ width: width, breakpoints: widths, layout: layout });
|
let breakpoints = getBreakpoints({ width: width, breakpoints: widths, layout: layout });
|
||||||
breakpoints = [...new Set(breakpoints)].sort((a, b) => a - b);
|
breakpoints = [...new Set(breakpoints)].sort((a, b) => a - b);
|
||||||
|
|
||||||
const srcset = (await transform(image, breakpoints, Number(width) || undefined, Number(height) || undefined))
|
const srcset = (await transform(image, breakpoints, Number(width) || undefined, Number(height) || undefined, format))
|
||||||
.map(({ src, width }) => `${src} ${width}w`)
|
.map(({ src, width }) => `${src} ${width}w`)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { getImage } from 'astro:assets';
|
import { isUnpicCompatible, unpicOptimizer, astroAsseetsOptimizer } from './images-optimization';
|
||||||
import type { ImageMetadata } from 'astro';
|
import type { ImageMetadata } from 'astro';
|
||||||
import type { OpenGraph } from '@astrolib/seo';
|
import type { OpenGraph } from '@astrolib/seo';
|
||||||
|
|
||||||
@@ -64,23 +64,34 @@ export const adaptOpenGraphImages = async (
|
|||||||
const adaptedImages = await Promise.all(
|
const adaptedImages = await Promise.all(
|
||||||
images.map(async (image) => {
|
images.map(async (image) => {
|
||||||
if (image?.url) {
|
if (image?.url) {
|
||||||
const resolvedImage = (await findImage(image.url)) as ImageMetadata | undefined;
|
const resolvedImage = (await findImage(image.url)) as ImageMetadata | string | undefined;
|
||||||
if (!resolvedImage) {
|
if (!resolvedImage) {
|
||||||
return {
|
return {
|
||||||
url: '',
|
url: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const _image = await getImage({
|
let _image;
|
||||||
src: resolvedImage,
|
|
||||||
alt: 'Placeholder alt',
|
if (
|
||||||
width: image?.width || defaultWidth,
|
typeof resolvedImage === 'string' &&
|
||||||
height: image?.height || defaultHeight,
|
(resolvedImage.startsWith('http://') || resolvedImage.startsWith('https://')) &&
|
||||||
});
|
isUnpicCompatible(resolvedImage)
|
||||||
|
) {
|
||||||
|
_image = (await unpicOptimizer(resolvedImage, [defaultWidth], defaultWidth, defaultHeight, 'jpg'))[0];
|
||||||
|
} else if (resolvedImage) {
|
||||||
|
const dimensions =
|
||||||
|
typeof resolvedImage !== 'string' && resolvedImage?.width <= defaultWidth
|
||||||
|
? [resolvedImage?.width, resolvedImage?.height]
|
||||||
|
: [defaultWidth, defaultHeight];
|
||||||
|
_image = (
|
||||||
|
await astroAsseetsOptimizer(resolvedImage, [dimensions[0]], dimensions[0], dimensions[1], 'jpg')
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof _image === 'object') {
|
if (typeof _image === 'object') {
|
||||||
return {
|
return {
|
||||||
url: 'src' in _image && typeof _image.src === 'string' ? String(new URL(_image.src, astroSite)) : 'pepe',
|
url: 'src' in _image && typeof _image.src === 'string' ? String(new URL(_image.src, astroSite)) : '',
|
||||||
width: 'width' in _image && typeof _image.width === 'number' ? _image.width : undefined,
|
width: 'width' in _image && typeof _image.width === 'number' ? _image.width : undefined,
|
||||||
height: 'height' in _image && typeof _image.height === 'number' ? _image.height : undefined,
|
height: 'height' in _image && typeof _image.height === 'number' ? _image.height : undefined,
|
||||||
};
|
};
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import defaultTheme from 'tailwindcss/defaultTheme';
|
import defaultTheme from 'tailwindcss/defaultTheme';
|
||||||
|
import plugin from 'tailwindcss/plugin';
|
||||||
import typographyPlugin from '@tailwindcss/typography';
|
import typographyPlugin from '@tailwindcss/typography';
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
content: ['./src/**/*.{astro,html,js,jsx,json,md,mdx,svelte,ts,tsx,vue}'],
|
content: ['./src/**/*.{astro,html,js,jsx,json,md,mdx,svelte,ts,tsx,vue}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
@@ -17,8 +18,24 @@ module.exports = {
|
|||||||
serif: ['var(--aw-font-serif, ui-serif)', ...defaultTheme.fontFamily.serif],
|
serif: ['var(--aw-font-serif, ui-serif)', ...defaultTheme.fontFamily.serif],
|
||||||
heading: ['var(--aw-font-heading, ui-sans-serif)', ...defaultTheme.fontFamily.sans],
|
heading: ['var(--aw-font-heading, ui-sans-serif)', ...defaultTheme.fontFamily.sans],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
animation: {
|
||||||
|
fade: 'fadeInUp 1s both',
|
||||||
|
},
|
||||||
|
|
||||||
|
keyframes: {
|
||||||
|
fadeInUp: {
|
||||||
|
'0%': { opacity: 0, transform: 'translateY(2rem)' },
|
||||||
|
'100%': { opacity: 1, transform: 'translateY(0)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [typographyPlugin],
|
plugins: [
|
||||||
|
typographyPlugin,
|
||||||
|
plugin(({ addVariant }) => {
|
||||||
|
addVariant('intersect', '&:not([no-intersect])');
|
||||||
|
}),
|
||||||
|
],
|
||||||
darkMode: 'class',
|
darkMode: 'class',
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user