fixed accessibility
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import Button from "../ui/button/button.svelte";
|
||||||
|
|
||||||
let { portableText, children } = $props();
|
let { portableText, children } = $props();
|
||||||
const { value } = portableText;
|
const { value } = portableText;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a
|
<Button
|
||||||
href={value.href}
|
href={value.href}
|
||||||
|
variant="link"
|
||||||
target={value.blank ? '_blank' : '_self'}
|
target={value.blank ? '_blank' : '_self'}
|
||||||
rel={value.blank ? 'noopener noreferrer' : undefined}
|
rel={value.blank ? 'noopener noreferrer' : undefined}
|
||||||
class="text-primary hover:underline"
|
class="text-primary hover:underline p-0"
|
||||||
>
|
>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</a>
|
</Button>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { getFileAsset } from '@sanity/asset-utils';
|
import { getFileAsset } from '@sanity/asset-utils';
|
||||||
import { Download } from '@lucide/svelte';
|
import { Download } from '@lucide/svelte';
|
||||||
import { client } from '$lib/sanity';
|
import { client } from '$lib/sanity';
|
||||||
|
import Button from '../ui/button/button.svelte';
|
||||||
|
|
||||||
let { portableText } = $props();
|
let { portableText } = $props();
|
||||||
const { value } = portableText;
|
const { value } = portableText;
|
||||||
@@ -10,8 +11,8 @@
|
|||||||
|
|
||||||
{#if value?.asset}
|
{#if value?.asset}
|
||||||
{@const file = getFileAsset(value, { projectId, dataset })}
|
{@const file = getFileAsset(value, { projectId, dataset })}
|
||||||
<a href={file.url} download class="inline-flex items-center gap-2 text-primary hover:underline">
|
<Button href={file.url} download class="inline-flex items-center gap-2 text-primary hover:underline">
|
||||||
<Download size={16} />
|
<Download size={16} />
|
||||||
{value.title || 'Download file'}
|
{value.title || 'Download file'}
|
||||||
</a>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { cn } from '$lib/utils';
|
import { cn } from '$lib/utils';
|
||||||
import { ArrowRight, ExternalLink } from '@lucide/svelte';
|
import { ArrowRight, ExternalLink } from '@lucide/svelte';
|
||||||
|
import Button from './ui/button/button.svelte';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
text,
|
text,
|
||||||
@@ -28,54 +29,18 @@
|
|||||||
|
|
||||||
const baseClasses = cn(
|
const baseClasses = cn(
|
||||||
'group',
|
'group',
|
||||||
'rounded-full',
|
|
||||||
'font-semibold',
|
|
||||||
'transition-all',
|
|
||||||
'duration-300',
|
|
||||||
'no-underline',
|
|
||||||
'flex',
|
|
||||||
'items-center',
|
|
||||||
'gap-2',
|
'gap-2',
|
||||||
'inline-flex',
|
'inline-flex',
|
||||||
'justify-center',
|
|
||||||
'whitespace-nowrap',
|
'whitespace-nowrap',
|
||||||
'text-sm',
|
|
||||||
'font-medium',
|
|
||||||
'ring-offset-background',
|
|
||||||
'transition-colors',
|
|
||||||
'focus-visible:outline-none',
|
|
||||||
'focus-visible:ring-2',
|
|
||||||
'focus-visible:ring-ring',
|
|
||||||
'focus-visible:ring-offset-2',
|
|
||||||
'disabled:pointer-events-none',
|
|
||||||
'disabled:opacity-50',
|
|
||||||
showIcon ? 'pr-4' : 'px-6',
|
showIcon ? 'pr-4' : 'px-6',
|
||||||
'active:transform',
|
'active:transform',
|
||||||
'active:translate-y-[1px]',
|
'active:translate-y-[1px]',
|
||||||
{
|
|
||||||
// Variant styles
|
|
||||||
'bg-primary text-primary-foreground hover:bg-primary/90': variant === 'default',
|
|
||||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90':
|
|
||||||
variant === 'destructive',
|
|
||||||
'border border-input bg-background hover:bg-accent hover:text-accent-foreground':
|
|
||||||
variant === 'outline',
|
|
||||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80': variant === 'secondary',
|
|
||||||
'hover:bg-accent hover:text-accent-foreground': variant === 'ghost',
|
|
||||||
'text-primary underline-offset-4 hover:underline': variant === 'link'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Size styles
|
|
||||||
'h-10 px-4 py-2': size === 'default',
|
|
||||||
'h-9 rounded-md px-3': size === 'sm',
|
|
||||||
'h-11 rounded-md px-8': size === 'lg',
|
|
||||||
'h-10 w-10': size === 'icon'
|
|
||||||
},
|
|
||||||
className
|
className
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if linkData}
|
{#if linkData}
|
||||||
<a
|
<Button
|
||||||
href={linkData.href}
|
href={linkData.href}
|
||||||
target={linkData?.target ?? '_self'}
|
target={linkData?.target ?? '_self'}
|
||||||
rel={isExternal ? 'noopener noreferrer' : undefined}
|
rel={isExternal ? 'noopener noreferrer' : undefined}
|
||||||
@@ -102,5 +67,5 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -116,22 +116,28 @@
|
|||||||
expandedSubmenu = expandedSubmenu === itemName ? null : itemName;
|
expandedSubmenu = expandedSubmenu === itemName ? null : itemName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const shouldReload = $derived($page.url.pathname.startsWith('/blog/'));
|
const shouldReload = $derived($page.url.pathname.startsWith('/blog/'));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav class="relative z-[50] bg-primary py-3">
|
<nav class="bg-primary relative z-[50] py-3">
|
||||||
<div class="container px-4 md:mx-auto md:px-0">
|
<div class="container px-4 md:mx-auto md:px-0">
|
||||||
<div class="relative flex items-center">
|
<div class="relative flex items-center">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
{#if settings}
|
{#if settings}
|
||||||
<a href="/">
|
<Button variant="ghost" href="/">
|
||||||
<Button variant="ghost">
|
<Logo {settings} class="text-white" />
|
||||||
<Logo {settings} class="text-white" />
|
</Button>
|
||||||
</Button>
|
|
||||||
</a>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-lg font-semibold text-white">Logo</div>
|
<div class="text-lg font-semibold text-white">Logo</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<a
|
||||||
|
href="#main-content"
|
||||||
|
class="sr-only ml-2 focus:not-sr-only focus:bg-white focus:text-black focus:px-3 focus:py-1 focus:rounded focus:text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all"
|
||||||
|
>
|
||||||
|
Skip navigation
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="absolute left-1/2 hidden -translate-x-1/2 transform md:flex">
|
<div class="absolute left-1/2 hidden -translate-x-1/2 transform md:flex">
|
||||||
@@ -146,10 +152,12 @@
|
|||||||
<NavigationMenuItem data-navigation-menu-item>
|
<NavigationMenuItem data-navigation-menu-item>
|
||||||
{#if item.url && !item.subitems}
|
{#if item.url && !item.subitems}
|
||||||
<NavigationMenuLink
|
<NavigationMenuLink
|
||||||
class={cn('relative z-20 text-white transition-colors hover:text-white/80')}
|
class={cn(
|
||||||
|
'relative z-20 text-white transition-colors hover:text-white/80 focus:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/20'
|
||||||
|
)}
|
||||||
href={item.url}
|
href={item.url}
|
||||||
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
||||||
? 'true'
|
? true
|
||||||
: undefined}
|
: undefined}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
@@ -157,12 +165,13 @@
|
|||||||
{:else if item.subitems}
|
{:else if item.subitems}
|
||||||
{#if item.url}
|
{#if item.url}
|
||||||
<NavigationMenuTrigger
|
<NavigationMenuTrigger
|
||||||
class="relative z-20 cursor-pointer px-2 text-white transition-colors hover:text-white/80"
|
class="relative z-20 cursor-pointer px-2 text-white transition-colors hover:text-white/80 focus:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/20 data-[state=open]:bg-white/10"
|
||||||
>
|
>
|
||||||
<NavigationMenuLink
|
<NavigationMenuLink
|
||||||
href={item.url}
|
href={item.url}
|
||||||
|
class="focus:outline-none"
|
||||||
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
||||||
? 'true'
|
? true
|
||||||
: undefined}
|
: undefined}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
@@ -170,20 +179,20 @@
|
|||||||
</NavigationMenuTrigger>
|
</NavigationMenuTrigger>
|
||||||
{:else}
|
{:else}
|
||||||
<NavigationMenuTrigger
|
<NavigationMenuTrigger
|
||||||
class="relative z-20 cursor-pointer text-white transition-colors hover:text-white/80"
|
class="relative z-20 cursor-pointer text-white transition-colors hover:text-white/80 focus:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/20 data-[state=open]:bg-white/10"
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</NavigationMenuTrigger>
|
</NavigationMenuTrigger>
|
||||||
{/if}
|
{/if}
|
||||||
<NavigationMenuContent
|
<NavigationMenuContent
|
||||||
class="absolute left-1/2 z-[50] mt-2 min-w-max -translate-x-1/2 rounded-md border border-white/20 bg-primary shadow-lg"
|
class="bg-primary absolute left-1/2 z-[50] mt-2 min-w-max -translate-x-1/2 space-y-1 rounded-md border border-white/20 p-2 shadow-lg"
|
||||||
>
|
>
|
||||||
{#each item.subitems as subitem}
|
{#each item.subitems as subitem}
|
||||||
<NavigationMenuLink
|
<NavigationMenuLink
|
||||||
href={subitem.url}
|
href={subitem.url}
|
||||||
class="block rounded-md px-4 py-2 text-white transition-colors hover:text-white/80"
|
class="block rounded-md px-4 py-2 text-white transition-colors hover:bg-white/10 hover:text-white/80 focus:bg-white/10 focus:text-white focus:outline-none focus:ring-2 focus:ring-white/20"
|
||||||
data-sveltekit-reload={subitem.url?.startsWith('/blog/') && shouldReload
|
data-sveltekit-reload={subitem.url?.startsWith('/blog/') && shouldReload
|
||||||
? 'true'
|
? true
|
||||||
: undefined}
|
: undefined}
|
||||||
>
|
>
|
||||||
{subitem.name}
|
{subitem.name}
|
||||||
@@ -204,15 +213,13 @@
|
|||||||
<MoreHorizontal size={24} />
|
<MoreHorizontal size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
</SheetTrigger>
|
</SheetTrigger>
|
||||||
<SheetContent side="top" class="h-full w-full border-primary bg-primary text-white">
|
<SheetContent side="top" class="border-primary bg-primary h-full w-full text-white">
|
||||||
<SheetHeader class="border-b border-white/20 pb-4">
|
<SheetHeader class="border-b border-white/20 pb-4">
|
||||||
<SheetTitle class="flex items-center justify-start">
|
<SheetTitle class="flex items-center justify-start">
|
||||||
{#if settings}
|
{#if settings}
|
||||||
<a href="/">
|
<Button variant="ghost" class="px-1" href="/">
|
||||||
<Button variant="ghost" class="px-1">
|
<Logo {settings} class="text-white" />
|
||||||
<Logo {settings} class="text-white" />
|
</Button>
|
||||||
</Button>
|
|
||||||
</a>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-lg font-semibold">Navigation</div>
|
<div class="text-lg font-semibold">Navigation</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -222,7 +229,7 @@
|
|||||||
{#each items || [] as item, index}
|
{#each items || [] as item, index}
|
||||||
{#if item.url && !item.subitems}
|
{#if item.url && !item.subitems}
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<a
|
<Button
|
||||||
href={item.url}
|
href={item.url}
|
||||||
class={cn(
|
class={cn(
|
||||||
'mx-2 block rounded-md px-4 py-2 text-sm transition-colors hover:text-white/80',
|
'mx-2 block rounded-md px-4 py-2 text-sm transition-colors hover:text-white/80',
|
||||||
@@ -230,22 +237,26 @@
|
|||||||
)}
|
)}
|
||||||
onclick={closeMobileMenu}
|
onclick={closeMobileMenu}
|
||||||
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
||||||
? 'true'
|
? true
|
||||||
: undefined}
|
: undefined}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</a>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{:else if item.subitems}
|
{:else if item.subitems}
|
||||||
<div class="relative space-y-1">
|
<div class="relative space-y-1">
|
||||||
{#if index === activeIndex}
|
{#if index === activeIndex}
|
||||||
<div
|
<div
|
||||||
class="absolute top-0 bottom-0 left-0 w-1 rounded-r-md bg-white/20"
|
class="absolute bottom-0 left-0 top-0 w-1 rounded-r-md bg-white/20"
|
||||||
></div>
|
></div>
|
||||||
{/if}
|
{/if}
|
||||||
<Button
|
<Button
|
||||||
class="flex w-full items-center justify-between rounded-md px-4 py-2 text-left text-sm font-medium transition-all duration-200 hover:text-white/80"
|
class="flex w-full items-center justify-between rounded-md px-4 py-2 text-left text-sm font-medium text-white transition-all duration-200 hover:text-white/80 focus:outline-none focus:ring-2 focus:ring-white/20"
|
||||||
onclick={() => toggleSubmenu(item.name)}
|
onclick={() => toggleSubmenu(item.name)}
|
||||||
|
aria-expanded={expandedSubmenu === item.name}
|
||||||
|
aria-controls={`submenu-${item.name}`}
|
||||||
|
id={`submenu-trigger-${item.name}`}
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<span>{item.name}</span>
|
<span>{item.name}</span>
|
||||||
<div
|
<div
|
||||||
@@ -261,30 +272,33 @@
|
|||||||
<div
|
<div
|
||||||
class="ml-4 space-y-1"
|
class="ml-4 space-y-1"
|
||||||
transition:slide={{ duration: 300, easing: quintOut }}
|
transition:slide={{ duration: 300, easing: quintOut }}
|
||||||
|
id={`submenu-${item.name}`}
|
||||||
|
role="group"
|
||||||
|
aria-labelledby={`submenu-trigger-${item.name}`}
|
||||||
>
|
>
|
||||||
{#if item.url}
|
{#if item.url}
|
||||||
<a
|
<Button
|
||||||
href={item.url}
|
href={item.url}
|
||||||
class="block rounded-md px-6 py-2 text-sm text-white/80 transition-colors hover:text-white"
|
class="block rounded-md px-6 py-2 text-sm text-white/80 transition-colors hover:text-white focus:outline-none focus:ring-2 focus:ring-white/20"
|
||||||
onclick={closeMobileMenu}
|
onclick={closeMobileMenu}
|
||||||
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
data-sveltekit-reload={item.url?.startsWith('/blog/') && shouldReload
|
||||||
? 'true'
|
? true
|
||||||
: undefined}
|
: undefined}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</a>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
{#each item.subitems as subitem}
|
{#each item.subitems as subitem}
|
||||||
<a
|
<Button
|
||||||
href={subitem.url}
|
href={subitem.url}
|
||||||
class="block rounded-md px-6 py-2 text-sm text-white/80 transition-colors hover:text-white"
|
class="block rounded-md px-6 py-2 text-sm text-white/80 transition-colors hover:text-white focus:outline-none focus:ring-2 focus:ring-white/20"
|
||||||
onclick={closeMobileMenu}
|
onclick={closeMobileMenu}
|
||||||
data-sveltekit-reload={subitem.url?.startsWith('/blog/') && shouldReload
|
data-sveltekit-reload={subitem.url?.startsWith('/blog/') && shouldReload
|
||||||
? 'true'
|
? true
|
||||||
: undefined}
|
: undefined}
|
||||||
>
|
>
|
||||||
{subitem.name}
|
{subitem.name}
|
||||||
</a>
|
</Button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<div class="scroll-smooth font-sans antialiased">
|
<div class="scroll-smooth font-sans antialiased">
|
||||||
<Navbar settings={data.settings} items={data.navigation} />
|
<Navbar settings={data.settings} items={data.navigation} />
|
||||||
<main class="min-h-[calc(100vh-11.3rem)] md:min-h-[calc(100vh-9.05rem)]">
|
<main id="main-content" tabindex="-1" class="min-h-[calc(100vh-11.3rem)] md:min-h-[calc(100vh-9.05rem)] focus:outline-none">
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
{#if data.settings}
|
{#if data.settings}
|
||||||
|
|||||||
@@ -78,9 +78,9 @@
|
|||||||
<main class="container mx-auto min-h-screen max-w-6xl p-8">
|
<main class="container mx-auto min-h-screen max-w-6xl p-8">
|
||||||
<header class="mb-12">
|
<header class="mb-12">
|
||||||
<h1 class="mb-4 font-serif text-3xl font-bold sm:text-4xl md:text-5xl">Blog</h1>
|
<h1 class="mb-4 font-serif text-3xl font-bold sm:text-4xl md:text-5xl">Blog</h1>
|
||||||
<p class="text-lg text-muted-foreground">{meta.description}</p>
|
<p class="text-muted-foreground text-lg">{meta.description}</p>
|
||||||
{#if pagination.totalBlogs > 0}
|
{#if pagination.totalBlogs > 0}
|
||||||
<p class="mt-2 text-sm text-muted-foreground">
|
<p class="text-muted-foreground mt-2 text-sm">
|
||||||
Showing {blogs.length} of {pagination.totalBlogs} posts
|
Showing {blogs.length} of {pagination.totalBlogs} posts
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
|
|
||||||
{#if blogs.length === 0}
|
{#if blogs.length === 0}
|
||||||
<div class="py-16 text-center">
|
<div class="py-16 text-center">
|
||||||
<p class="text-lg text-muted-foreground">No blog posts found.</p>
|
<p class="text-muted-foreground text-lg">No blog posts found.</p>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
{@const extendedBlog = blog as any}
|
{@const extendedBlog = blog as any}
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
class="group cursor-pointer overflow-hidden transition-all focus-within:ring-2 focus-within:ring-primary/20 hover:shadow-md"
|
class="focus-within:ring-primary/20 group cursor-pointer overflow-hidden transition-all focus-within:ring-2 hover:shadow-md"
|
||||||
onclick={() => navigateToPost(blog.slug?.current)}
|
onclick={() => navigateToPost(blog.slug?.current)}
|
||||||
onkeydown={(e) => {
|
onkeydown={(e) => {
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
@@ -124,22 +124,22 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex aspect-video items-center justify-center bg-muted">
|
<div class="bg-muted flex aspect-video items-center justify-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="mb-2 text-4xl text-muted-foreground">📝</div>
|
<div class="text-muted-foreground mb-2 text-4xl">📝</div>
|
||||||
<p class="text-sm text-muted-foreground">No cover image</p>
|
<p class="text-muted-foreground text-sm">No cover image</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<CardHeader class="pb-3">
|
<CardHeader class="pb-3">
|
||||||
<CardTitle
|
<CardTitle
|
||||||
class="font-serif text-xl leading-tight transition-colors group-hover:text-primary"
|
class="group-hover:text-primary font-serif text-xl leading-tight transition-colors"
|
||||||
>
|
>
|
||||||
{blog.title}
|
{blog.title}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1 text-sm text-muted-foreground">
|
<div class="text-muted-foreground flex flex-col gap-1 text-sm">
|
||||||
{#if blog.author}
|
{#if blog.author}
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="font-medium">By {blog.author}</span>
|
<span class="font-medium">By {blog.author}</span>
|
||||||
@@ -154,13 +154,13 @@
|
|||||||
<div class="mt-3 flex flex-wrap gap-1">
|
<div class="mt-3 flex flex-wrap gap-1">
|
||||||
{#each blog.tags.slice(0, 3) as tag}
|
{#each blog.tags.slice(0, 3) as tag}
|
||||||
<span
|
<span
|
||||||
class="inline-block rounded bg-muted px-2 py-1 text-xs font-medium text-muted-foreground"
|
class="bg-muted text-muted-foreground inline-block rounded px-2 py-1 text-xs font-medium"
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
{#if blog.tags.length > 3}
|
{#if blog.tags.length > 3}
|
||||||
<span class="text-xs text-muted-foreground">+{blog.tags.length - 3} more</span>
|
<span class="text-muted-foreground text-xs">+{blog.tags.length - 3} more</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -168,13 +168,13 @@
|
|||||||
|
|
||||||
<CardContent class="pt-0">
|
<CardContent class="pt-0">
|
||||||
{#if blog.excerpt || extendedBlog.description}
|
{#if blog.excerpt || extendedBlog.description}
|
||||||
<p class="mb-4 line-clamp-3 text-sm leading-relaxed text-muted-foreground">
|
<p class="text-muted-foreground mb-4 line-clamp-3 text-sm leading-relaxed">
|
||||||
{blog.excerpt || extendedBlog.description}
|
{blog.excerpt || extendedBlog.description}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="inline-flex items-center text-sm font-medium text-primary transition-colors group-hover:text-primary/80"
|
class="text-primary group-hover:text-primary/80 inline-flex items-center text-sm font-medium transition-colors"
|
||||||
>
|
>
|
||||||
Read more
|
Read more
|
||||||
<svg class="ml-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="ml-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -195,51 +195,59 @@
|
|||||||
<nav class="mt-12 flex justify-center" aria-label="Pagination">
|
<nav class="mt-12 flex justify-center" aria-label="Pagination">
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
{#if pagination.hasPreviousPage}
|
{#if pagination.hasPreviousPage}
|
||||||
<a href={createPageUrl(pagination.currentPage - 1)} data-sveltekit-reload>
|
<Button
|
||||||
<Button variant="outline" size="sm">
|
variant="outline"
|
||||||
<svg class="mr-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
size="sm"
|
||||||
<path
|
href={createPageUrl(pagination.currentPage - 1)}
|
||||||
stroke-linecap="round"
|
data-sveltekit-reload
|
||||||
stroke-linejoin="round"
|
>
|
||||||
stroke-width="2"
|
<svg class="mr-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
d="M15 19l-7-7 7-7"
|
<path
|
||||||
></path>
|
stroke-linecap="round"
|
||||||
</svg>
|
stroke-linejoin="round"
|
||||||
Previous
|
stroke-width="2"
|
||||||
</Button>
|
d="M15 19l-7-7 7-7"
|
||||||
</a>
|
></path>
|
||||||
|
</svg>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#each getVisiblePages() as pageItem}
|
{#each getVisiblePages() as pageItem}
|
||||||
{#if pageItem === 'ellipsis'}
|
{#if pageItem === 'ellipsis'}
|
||||||
<span class="px-3 py-2 text-muted-foreground">...</span>
|
<span class="text-muted-foreground px-3 py-2">...</span>
|
||||||
{:else if pageItem === pagination.currentPage}
|
{:else if pageItem === pagination.currentPage}
|
||||||
<Button variant="default" size="sm" disabled>
|
<Button variant="default" size="sm" disabled>
|
||||||
{pageItem}
|
{pageItem}
|
||||||
</Button>
|
</Button>
|
||||||
{:else}
|
{:else}
|
||||||
<a href={createPageUrl(pageItem)} data-sveltekit-reload>
|
<Button
|
||||||
<Button variant="ghost" size="sm">
|
variant="ghost"
|
||||||
{pageItem}
|
size="sm"
|
||||||
</Button>
|
href={createPageUrl(pageItem)}
|
||||||
</a>
|
data-sveltekit-reload
|
||||||
|
>
|
||||||
|
{pageItem}
|
||||||
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if pagination.hasNextPage}
|
{#if pagination.hasNextPage}
|
||||||
<a href={createPageUrl(pagination.currentPage + 1)} data-sveltekit-reload>
|
<Button
|
||||||
<Button variant="outline" size="sm">
|
variant="outline"
|
||||||
Next
|
size="sm"
|
||||||
<svg class="ml-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
href={createPageUrl(pagination.currentPage + 1)}
|
||||||
<path
|
data-sveltekit-reload
|
||||||
stroke-linecap="round"
|
>
|
||||||
stroke-linejoin="round"
|
<svg class="ml-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
stroke-width="2"
|
<path
|
||||||
d="M9 5l7 7-7 7"
|
stroke-linecap="round"
|
||||||
></path>
|
stroke-linejoin="round"
|
||||||
</svg>
|
stroke-width="2"
|
||||||
</Button>
|
d="M9 5l7 7-7 7"
|
||||||
</a>
|
></path>
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
Reference in New Issue
Block a user