Files
windwiderstand/src/lib/components/Card.svelte
T
Peter Meier d7a84aff0d SearchableTextBlock: animated accordion, tag counts, empty state, focus-visible
- 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>
2026-05-16 15:31:38 +02:00

90 lines
2.5 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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}