> For the complete documentation index, see [llms.txt](https://analog.groupthought.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://analog.groupthought.com/reference/alpine/read-more.md).

# x-read-more

Truncated content with a smooth height expand, and automatic expansion for focus, print, hash links, and find-in-page.

Truncated content with a smooth height expand, and automatic expansion for focus, print, hash links, and find-in-page.

| Directive / magic      | What it does                                                                                                                |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `x-read-more`          | The root. Holds the expanded state and runs the height animation                                                            |
| `x-read-more:button`   | Click expands the content                                                                                                   |
| `x-read-more:preview`  | Optional. The line-clamped preview shown while collapsed; `id` attributes are stripped so the full copy keeps the real ones |
| `x-read-more:content`  | Optional. The full copy, marked `hidden="until-found"` while collapsed                                                      |
| `$readMore.isExpanded` | Whether the content is expanded                                                                                             |
| `$readMore.expand()`   | Expand programmatically (no-op if already expanded)                                                                         |

The root also expands itself whenever collapsing would hide something the user is looking for: focus entering the truncated content (tabbing to a link inside), printing the page, the URL hash pointing at an element inside the root, and find-in-page or text-fragment navigation hitting `x-read-more:content`. All except focus skip the animation. There is no collapse control. Expansion is one-way, except that a print-triggered expand collapses again after printing.

## Examples

### Basic truncation

Use a line clamp driven by `$readMore.isExpanded`, with the button hidden after expanding:

```html
<div x-read-more class="overflow-clip">
  <div
    class="line-clamp-[--truncate-lines]"
    :style="{ '--truncate-lines': $readMore.isExpanded ? 'none' : 3 }"
  >
    Long content…
  </div>
  <button type="button" x-read-more:button x-show="!$readMore.isExpanded">
    Read more
  </button>
</div>
```

The root animates its height from the collapsed measurement to the expanded one. Set an inline `transition` style on the root to change the curve (default `height 300ms`).

### Find-in-page support

Use a clamped `x-read-more:preview` plus a full copy on `x-read-more:content`. A line clamp alone cannot receive the browser's `beforematch` event:

```html
<div x-read-more class="overflow-clip">
  <div x-read-more:preview class="line-clamp-3">…</div>
  <!-- hidden must be in the HTML (not only set by JS) to avoid a flash of full text -->
  <div x-read-more:content hidden="until-found">…</div>
  <button type="button" x-read-more:button x-show="!$readMore.isExpanded">Read more</button>
</div>
```

Searching the page, or following a link with a text fragment, reveals the full copy with no animation. The `_product-description` block uses this pair for its "Truncate description" setting.

### Showing the button only when truncated

Use the `x-overflow` plugin's detector to tell whether the clamp actually cut anything:

```html
<div x-overflow x-read-more>
  <div x-read-more:preview class="line-clamp-3">
    …
    <div class="w-full h-px" x-overflow:detector aria-hidden="true"></div>
  </div>
  <button
    x-read-more:button
    :class="{ 'invisible opacity-0': !$overflow.detector || $readMore.isExpanded }"
  >
    Read more
  </button>
</div>
```

The detector sits at the end of the full text; while the clamp hides it, `$overflow.detector` is true and the button shows. A short description never shows a pointless button.

## Options

There is no options object and no modifiers. The animation is the root's own `transition` style; everything else is markup.
