# StoryThread

> A narrative thread for scroll stories: the page pans sideways through your sections while a dotted line draws itself from node to node, a glowing head rides its tip, nodes light up as it reaches them, and segments can fork and meet again.

- Package: `fancy-ui-vue` (Vue 3.5+ + Tailwind CSS v4)
- Category: Navigation
- Tags: story, storytelling, scrollytelling, path, thread, timeline, horizontal scroll, svg, narrative
- Types: `StoryThreadProps`, `StoryNodeProps`, `StoryThreadSegment`, `StoryThreadLayout`, `StoryThreadDash`
- Docs: https://fancy-ui.rama.app/docs/components/story-thread
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/vue/src/components/story-thread

## Import

```vue
<script setup lang="ts">
import { StoryThread } from 'fancy-ui-vue';
</script>
```

Setup (once per project): `npm install fancy-ui-vue`, then put these lines in your main CSS file (the stylesheet imports go right after `@import "tailwindcss";`):

```css
@import "tailwindcss";
@import "fancy-ui-vue/tailwind.css";
@import "fancy-ui-vue/styles.css";
```

Full guide: https://fancy-ui.rama.app/llms.txt

The names below are the ones to write in a Vue template; when one differs from the Svelte name, the Svelte name follows in parentheses. Snippets become named slots (`#default` is the default snippet), a prop the Vue port declares as a model is a `v-model:<name>` (except `ref`, exposed via `defineExpose({ ref })`: put a template ref on the component and read `.ref`) and callbacks are listeners (`onclick` is `@click`). Every component sets `inheritAttrs: false`: `class` is a declared prop, merged where the Svelte component applies it (not always the root element), and only when the props table below lists it; other attributes do not fall through unless the package README lists the component as forwarding native attributes. Divergences: https://github.com/RamaHerbin/fancy-ui/tree/main/vue#divergences-from-the-svelte-api

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `layout` | `"auto" \| "horizontal" \| "vertical"` | `"auto"` | `horizontal` pins a full-height viewport and pans the content sideways as the page scrolls; `vertical` keeps a plain stack with the thread running down it. `auto` is horizontal from 900 × 520 px up. |
| `segments` | `StoryThreadSegment[]` | `[]` | Segments as `{ nodes: string[], color? }`, each through a list of StoryNode ids. Empty draws one segment through every node in document order. Segments that leave the same node draw a fork whose branches advance together. |
| `lead` | `number` | `0.7` | Where the tip of the thread sits in the viewport, 0..1 along the reading axis. |
| `tension` | `number` | `0.55` | Curve tension between nodes: 0 draws straight lines, 1 long flat tangents. |
| `dash` | `"dotted" \| "dashed" \| "solid"` | `"dotted"` | Stroke pattern of the thread. |
| `head` | `boolean` | `true` | Draws a glowing dot at the tip of every segment while it is drawing. |
| `sound` | `boolean` | `false` | Plays a soft `tick` each time the thread reaches a node going forward. Only audible once the user has enabled sound. |
| `@reach` (Svelte `onReach`) | `(id: string) => void` | — | Called with a node's id each time the thread reaches it going forward. |
| `ariaLabel` | `string` | `"Story"` | Accessible name of the region. |
| `class` | `string` | — | Additional CSS classes on the root. |

## Slots

| Slot | Description |
| --- | --- |
| `#default` (Svelte `children`) | The story: sections holding `<StoryNode id>` anchors (`variant` `dot` or `ring`). Size the sections yourself; in the horizontal layout they sit side by side at full height. |

## Example

```vue
<script setup lang="ts">
import { ref } from "vue";
import { StoryThread, StoryNode } from "fancy-ui-vue";

const reached = ref("");
</script>

<template>
	<StoryThread
		layout="vertical"
		ariaLabel="Product story"
		:on-reach="(id: string) => (reached = id)"
	>
		<section class="relative flex min-h-48 items-center gap-6 py-10 pl-10">
			<StoryNode id="idea" class="absolute left-0" />
			<p>Last reached: {{ reached || "none" }}</p>
		</section>
		<section class="relative flex min-h-48 items-center gap-6 py-10 pl-10">
			<StoryNode id="build" class="absolute left-0" />
			<p>Build it.</p>
		</section>
		<section class="relative flex min-h-48 items-center gap-6 py-10 pl-10">
			<StoryNode id="ship" variant="ring" class="absolute left-0" />
			<p>Ship it.</p>
		</section>
	</StoryThread>
</template>
```
