d7a84aff0d
- Replace <details> with Svelte-controlled accordion + slide transition (180ms) - Close all items on search/tag filter change - Live tag counts per filter showing matches for current search query - Visual empty state with reset button when no results - focus-visible ring on all interactive elements (was focus: — shows on click too) - Tag.svelte: remove ring-0, add focus-visible:ring-2 - Card.svelte: add focus-visible ring to linked cards Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
90 lines
2.5 KiB
Svelte
90 lines
2.5 KiB
Svelte
<script lang="ts">
|
||
import type { Snippet } from "svelte";
|
||
import { cva, type VariantProps } from "class-variance-authority";
|
||
|
||
/**
|
||
* tile + stack/inline: Org-Karten (Padding im Shell).
|
||
* tile + post: PostCard – Shell ohne Padding, Medien bündig, Text mit p-4 im Kind.
|
||
* callout: CTA; layout wird ignoriert.
|
||
*/
|
||
const cardSurface = cva("relative min-w-0 overflow-hidden", {
|
||
variants: {
|
||
variant: {
|
||
tile: "",
|
||
callout:
|
||
"flex flex-col gap-1.5 border border-dashed border-wald-300 bg-wald-50 p-3",
|
||
},
|
||
layout: {
|
||
stack: "",
|
||
inline: "",
|
||
post: "",
|
||
},
|
||
},
|
||
compoundVariants: [
|
||
{
|
||
variant: "tile",
|
||
layout: "stack",
|
||
class:
|
||
"flex flex-col gap-2 border border-stein-200 bg-white p-4 text-stein-900",
|
||
},
|
||
{
|
||
variant: "tile",
|
||
layout: "inline",
|
||
class:
|
||
"flex flex-row items-start gap-3 border border-stein-200 bg-white p-4 text-stein-900",
|
||
},
|
||
{
|
||
variant: "tile",
|
||
layout: "post",
|
||
class:
|
||
"flex flex-col gap-0 border border-stein-200 bg-white p-0 text-stein-900",
|
||
},
|
||
],
|
||
defaultVariants: { variant: "tile", layout: "stack" },
|
||
});
|
||
|
||
const cardAsLink = cva("", {
|
||
variants: {
|
||
variant: {
|
||
callout: "cursor-pointer transition-colors no-underline text-inherit hover:bg-wald-100",
|
||
tile: "transition-[transform,box-shadow,border-color] duration-200 no-underline hover:-translate-y-0.5 hover:shadow-md hover:border-wald-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-500 focus-visible:ring-offset-2",
|
||
},
|
||
},
|
||
defaultVariants: { variant: "tile" },
|
||
});
|
||
|
||
type SurfaceProps = VariantProps<typeof cardSurface>;
|
||
type CardLinkVariant = NonNullable<SurfaceProps["variant"]>;
|
||
|
||
let {
|
||
children,
|
||
href,
|
||
target,
|
||
rel,
|
||
variant = "tile",
|
||
layout = "stack",
|
||
class: className = "",
|
||
}: {
|
||
children?: Snippet;
|
||
href?: string;
|
||
target?: string;
|
||
rel?: string;
|
||
class?: string;
|
||
} & SurfaceProps = $props();
|
||
|
||
const surface = $derived(cardSurface({ variant, layout }));
|
||
const linkSurface = $derived(
|
||
href ? cardAsLink({ variant: (variant ?? "tile") as CardLinkVariant }) : "",
|
||
);
|
||
</script>
|
||
|
||
{#if href}
|
||
<a {href} {target} {rel} class="{surface} {linkSurface} {className}">
|
||
{@render children?.()}
|
||
</a>
|
||
{:else}
|
||
<div class="{surface} {className}">
|
||
{@render children?.()}
|
||
</div>
|
||
{/if}
|