Shadcn Blocks for Wiki-Style Websites

Craft sleek, Notion-inspired marketing pages. Built on top of Shadcn Svelte UI.

Overview

Mist is the Notion-inspired variant designed for wiki-style and content-heavy websites. It keeps the visual language minimal so product messaging and documentation stay readable.

  • Best for documentation hubs, changelogs, product guides, and lightweight marketing pages.
  • Includes Mist-specific UI primitives like neutral buttons and soft/mixed cards.
  • Works with the same Svelte 5 and shadcn-svelte foundations as other variants.

Theme Quickstart

Add the Mist theme variables to your global stylesheet (for example `globals.css`) so Mist components resolve colors, borders, and contrast correctly.

globals.css
[data-theme="mist"] .theme-container {
	--radius: 0.625rem;
	--background: var(--color-white);
	--foreground: var(--color-zinc-950);
	--card: var(--color-white);
	--card-foreground: var(--color-zinc-950);
	--popover: var(--color-white);
	--popover-foreground: var(--color-zinc-950);
	--primary: var(--color-indigo-500);
	--primary-foreground: var(--color-white);
	--secondary: var(--color-indigo-100);
	--secondary-foreground: var(--color-indigo-600);
	--muted: var(--color-zinc-100);
	--muted-foreground: var(--color-zinc-600);
	--accent: var(--color-zinc-700);
	--accent-foreground: var(--color-white);
	--destructive: var(--color-red-600);
	--border: var(--color-zinc-200);
	--input: var(--color-zinc-200);
	--ring: var(--color-indigo-500);

	@variant dark {
		--radius: 0.625rem;
		--background: var(--color-white);
		--foreground: var(--color-zinc-950);
		--card: var(--color-white);
		--card-foreground: black;
		--popover: var(--color-white);
		--popover-foreground: var(--color-zinc-950);
		--primary: var(--color-indigo-500);
		--primary-foreground: var(--color-white);
		--secondary: var(--color-indigo-100);
		--secondary-foreground: var(--color-indigo-600);
		--muted: var(--color-zinc-100);
		--muted-foreground: var(--color-zinc-600);
		--accent: var(--color-zinc-700);
		--accent-foreground: var(--color-white);
		--destructive: var(--color-red-600);
		--border: var(--color-zinc-200);
		--input: var(--color-zinc-200);
		--ring: var(--color-indigo-500);
	}

	@apply *:text-foreground;
}

Button Component

Mist button adds a `neutral` variant for bold monochrome actions while keeping the standard shadcn-like variants.

Usage

button-usage.svelte
<script lang="ts">
	import { Button } from "$lib/components/ui/mist/button";
</script>

<Button variant="neutral">Neutral Button</Button>

Source

button.svelte
<script lang="ts" module>
	import type { WithElementRef } from "bits-ui";
	import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
	import { type VariantProps, tv } from "tailwind-variants";

	export const buttonVariants = tv({
		base: "cursor-pointer font-medium inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
		variants: {
			variant: {
				default: "bg-primary text-primary-foreground hover:brightness-95",
				neutral: "bg-foreground text-background hover:brightness-95",
				destructive:
					"text-destructive-foreground bg-destructive shadow-md hover:bg-destructive/90",
				outline:
					"border border-transparent bg-background text-foreground shadow-sm ring-1 shadow-black/15 ring-foreground/10 duration-200 hover:bg-muted/50",
				secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
				ghost: "text-foreground/75 hover:bg-foreground/5 hover:text-foreground",
				link: "text-primary underline-offset-4 hover:underline",
			},
			size: {
				default: "h-9 rounded-md px-4 py-2",
				sm: "h-8 rounded-full px-3 text-sm",
				lg: "h-11 px-6 text-base font-medium",
				icon: "size-9",
			},
		},
		defaultVariants: {
			variant: "default",
			size: "sm",
		},
	});

	export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
	export type ButtonSize = VariantProps<typeof buttonVariants>["size"];

	export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
		WithElementRef<HTMLAnchorAttributes> & {
			variant?: ButtonVariant;
			size?: ButtonSize;
		};
</script>

<script lang="ts">
	import { cn } from "$lib/utils.js";

	let {
		class: className,
		variant = "default",
		size = "default",
		ref = $bindable(null),
		href = undefined,
		type = "button",
		children,
		...restProps
	}: ButtonProps = $props();
</script>

{#if href}
	<a
		bind:this={ref}
		class={cn(buttonVariants({ variant, size }), className)}
		{href}
		{...restProps}
	>
		{@render children?.()}
	</a>
{:else}
	<button
		bind:this={ref}
		class={cn(buttonVariants({ variant, size }), className)}
		{type}
		{...restProps}
	>
		{@render children?.()}
	</button>
{/if}
index.ts
import Root, {
	type ButtonProps,
	type ButtonSize,
	type ButtonVariant,
	buttonVariants,
} from "./button.svelte";

export {
	Root,
	type ButtonProps as Props,
	//
	Root as Button,
	buttonVariants,
	type ButtonProps,
	type ButtonSize,
	type ButtonVariant,
};

Card Component

Mist card adds `soft` and `mixed` variants to support subtle sections commonly used across documentation-style layouts.

Usage

card-usage.svelte
<script lang="ts">
	import { Card, CardContent, CardHeader, CardTitle } from "$lib/components/ui/mist/card";
</script>

<Card variant="soft">
	<CardHeader>
		<CardTitle>Soft Card</CardTitle>
	</CardHeader>
	<CardContent>...</CardContent>
</Card>

<Card variant="mixed">
	<CardHeader>
		<CardTitle>Mixed Card</CardTitle>
	</CardHeader>
	<CardContent>...</CardContent>
</Card>

Source

card.svelte
<script module lang="ts">
	import type { WithElementRef } from "bits-ui";
	import { type VariantProps, tv } from "tailwind-variants";

	export const cardVariants = tv({
		base: "text-card-foreground rounded-xl",
		variants: {
			variant: {
				default: "border border-transparent bg-card shadow ring-1 ring-foreground/5",
				soft: "bg-foreground/5",
				mixed: "border-foreground.5 border bg-foreground/5",
			},
		},
		defaultVariants: {
			variant: "default",
		},
	});

	export type CardVariant = VariantProps<typeof cardVariants>["variant"];

	export type CardProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
		variant?: CardVariant;
	};
</script>

<script lang="ts">
	import type { HTMLAttributes } from "svelte/elements";
	import { cn } from "$lib/utils.js";

	let {
		ref = $bindable(null),
		class: className,
		children,
		variant = "default",
		...restProps
	}: CardProps = $props();
</script>

<div bind:this={ref} class={cn(cardVariants({ variant }), className)} {...restProps}>
	{@render children?.()}
</div>
card-header.svelte
<script lang="ts">
	import type { WithElementRef } from "bits-ui";
	import type { HTMLAttributes } from "svelte/elements";
	import { cn } from "$lib/utils.js";

	let {
		ref = $bindable(null),
		class: className,
		children,
		...restProps
	}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>

<div bind:this={ref} class={cn("flex flex-col space-y-1.5 p-6", className)} {...restProps}>
	{@render children?.()}
</div>
card-title.svelte
<script lang="ts">
	import type { WithElementRef } from "bits-ui";
	import type { HTMLAttributes } from "svelte/elements";
	import { cn } from "$lib/utils.js";

	let {
		ref = $bindable(null),
		class: className,
		level = 3,
		children,
		...restProps
	}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
		level?: 1 | 2 | 3 | 4 | 5 | 6;
	} = $props();
</script>

<div
	role="heading"
	aria-level={level}
	bind:this={ref}
	class={cn("font-semibold leading-none tracking-tight", className)}
	{...restProps}
>
	{@render children?.()}
</div>
card-description.svelte
<script lang="ts">
	import type { WithElementRef } from "bits-ui";
	import type { HTMLAttributes } from "svelte/elements";
	import { cn } from "$lib/utils.js";

	let {
		ref = $bindable(null),
		class: className,
		children,
		...restProps
	}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
</script>

<p bind:this={ref} class={cn("text-muted-foreground text-sm", className)} {...restProps}>
	{@render children?.()}
</p>
card-content.svelte
<script lang="ts">
	import type { WithElementRef } from "bits-ui";
	import type { HTMLAttributes } from "svelte/elements";
	import { cn } from "$lib/utils.js";

	let {
		ref = $bindable(null),
		class: className,
		children,
		...restProps
	}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>

<div bind:this={ref} class={cn("p-6 pt-0", className)} {...restProps}>
	{@render children?.()}
</div>
card-footer.svelte
<script lang="ts">
	import type { WithElementRef } from "bits-ui";
	import type { HTMLAttributes } from "svelte/elements";
	import { cn } from "$lib/utils.js";

	let {
		ref = $bindable(null),
		class: className,
		children,
		...restProps
	}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>

<div bind:this={ref} class={cn("p-6 pt-0", className)} {...restProps}>
	{@render children?.()}
</div>
index.ts
import Root from "./card.svelte";
import Content from "./card-content.svelte";
import Description from "./card-description.svelte";
import Footer from "./card-footer.svelte";
import Header from "./card-header.svelte";
import Title from "./card-title.svelte";

export {
	Root,
	Content,
	Description,
	Footer,
	Header,
	Title,
	//
	Root as Card,
	Content as CardContent,
	Description as CardDescription,
	Footer as CardFooter,
	Header as CardHeader,
	Title as CardTitle,
};

Next Steps

If you have not completed the base setup, start with Installation. After Mist setup is ready, continue to MCP Server for AI-assisted discovery and installation workflows.

  • Use Mist sections from `/mist/*` routes as composition references.
  • Reuse Mist primitives from `$lib/components/ui/mist/*` for consistency.
  • Keep accents subtle and prioritize readable copy blocks.