Read More
stimeo--read-more
Toggles a CSS line-clamp, hiding the toggle when the text already fits.
The stimeo--read-more controller borrows the Disclosure convention (aria-expanded) to switch a paragraph between a clamped and full view via data-state. The clamp itself is the consumer's CSS; the controller adds overflow detection — when the text does not actually overflow its clamp the toggle is hidden, and the check re-runs on resize. The full text always stays in the DOM, so assistive technology reads everything regardless of state. Behavior only — styling is owned by this Playground.
Stimeo is a headless Stimulus UI framework for Ruby on Rails. It ships behavior only — accessible, attribute-driven components that own roles, keyboard interaction, and live-region feedback, while leaving every pixel of styling to you. There is no bundled CSS and no runtime dependency: you bring the markup and the looks, Stimeo brings the correct, tested accessibility semantics underneath.
Keyboard
| Key | Action |
|---|---|
| Enter / Space | Toggle between the clamped and full text (native button). |
<%# Markup for the read-more demo.
The body is visually truncated with a CSS line clamp, and a toggle switches to the
full text. The library syncs aria-expanded / data-state and hides the toggle when
there is no overflow (so no needless button appears). The clamp itself lives in demo.css. %>
<div class="read-more" data-controller="stimeo--read-more">
<p
class="read-more__content"
id="read-more-demo-content"
data-state="collapsed"
data-stimeo--read-more-target="content">
<%= t("components.read_more.demo.body") %>
</p>
<button
class="read-more__trigger"
type="button"
aria-expanded="false"
aria-controls="read-more-demo-content"
hidden
data-stimeo--read-more-target="trigger"
data-action="click->stimeo--read-more#toggle">
<%= t("components.read_more.demo.trigger") %>
</button>
</div>
/*
* Presentation-only styles for the read-more demo.
* The visual truncation (line clamp) is applied only when data-state="collapsed".
* The library toggles the toggle's visibility (hidden) based on overflow detection.
*/
.read-more {
max-width: 32rem;
}
.read-more__content {
margin: 0 0 0.5rem;
line-height: 1.6;
}
/* Clamp to 3 lines while collapsed. Line-based, so it follows text zoom (WCAG 1.4.4). */
.read-more__content[data-state="collapsed"] {
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.read-more__content[data-state="expanded"] {
-webkit-line-clamp: none;
line-clamp: none;
}
.read-more__trigger {
padding: 0.25rem 0;
background: transparent;
border: 0;
font: inherit;
font-weight: 600;
color: var(--accent);
cursor: pointer;
}
.read-more__trigger::after {
content: "";
}
.read-more__trigger[aria-expanded="true"]::before {
content: "▲ ";
}
.read-more__trigger[aria-expanded="false"]::before {
content: "▼ ";
}
This demo needs no consumer-side JS (the controller handles the behavior).
These demo styles use shared design tokens (light + dark). Copy the shared styles too, then toggle data-theme on your root element for dark mode.
The data-* attributes you add to your own HTML to wire this component. Put the data-controller below on a root element, then place its targets / values / actions inside that element.
On the root element
data-controller="stimeo--read-more"
Targets
| Name | Description | Attribute |
|---|---|---|
content
required
|
The clamped text element; its data-state (collapsed/expanded) keys the consumer's visual clamp. |
data-stimeo--read-more-target="content" |
trigger
required
|
The read-more button, hidden unless the text overflows; carries aria-expanded. |
data-stimeo--read-more-target="trigger" |
Values
| Name | Description | Attribute |
|---|---|---|
collapsed
|
Initial collapsed (clamped) state reflected on connect (default true). | data-stimeo--read-more-collapsed-value |
Actions
| Name | Description | Action |
|---|---|---|
toggle
|
Toggles between collapsed (clamped) and expanded, then re-evaluates overflow. | stimeo--read-more#toggle |
State hooks
The library only manages these ARIA/data attributes and custom properties. Your CSS reads them to render the look — selectors like [aria-selected], [aria-expanded], or var(--stimeo--…) hook into this state.
| Hook | Target | Meaning |
|---|---|---|
aria-expanded |
Trigger | "true" when expanded, "false" when collapsed. |
data-state |
Content | "collapsed" / "expanded" — drives the CSS line-clamp. |
hidden |
Trigger | Present when the text fits (no overflow), so no toggle is shown. |