Navigation

v0.0.4
Structure

Expand

A long text that shows its first lines and opens on request.

On this page

Expand shows the first lines of a long text and opens the rest on request: a description, a review, a changelog entry. The closed text fades out at its foot, and the toggle under it turns over as it opens to its full height.

Expand

<details class="expand max-w(420) mx-auto text-sm leading-md fg-text-body">  <summary class="expand-toggle">    <span class="expand-more">Show more</span>    <span class="expand-less">Show less</span>  </summary>  <p class="m(0)">Moonstone is a feldspar with a soft glow that seems to move under its surface as the stone turns. The effect comes from light scattering between thin layers inside it. Cut as a smooth dome, it shows a band of light that glides across the top. The theme takes its name from that glow: surfaces are pale and even, edges are found by a line of light before a line of ink, and nothing ends in a hard corner. Obsidian is its counterpart, a volcanic glass, dark and smooth, with edges that catch the light.</p></details>

It is a native <details>, and the toggle is its <summary>: the keyboard, find-in-page and assistive technology already know how it works. Where the browser exposes ::details-content, the closed state is a preview instead of nothing. Elsewhere the text is hidden while closed, as in any details, and the toggle still opens it.

The summary comes first in the markup, as <details> requires, and is drawn under the text.

#Lines

--expand-lines sets the height of the preview in lines of the text's own line height. The default is four.

Lines

<details class="expand max-w(420) mx-auto text-sm leading-md fg-text-body" style="--expand-lines: 2">  <summary class="expand-toggle">    <span class="expand-more">Read the full note</span>    <span class="expand-less">Show less</span>  </summary>  <p class="m(0)">Moonstone is a feldspar with a soft glow that seems to move under its surface as the stone turns. The effect comes from light scattering between thin layers inside it. Cut as a smooth dome, it shows a band of light that glides across the top. The theme takes its name from that glow: surfaces are pale and even, edges are found by a line of light before a line of ink, and nothing ends in a hard corner. Obsidian is its counterpart, a volcanic glass, dark and smooth, with edges that catch the light.</p></details>

#The toggle's words

Put the two wordings in expand-more and expand-less; the stylesheet shows the one that fits the state. Say what opening gives when you can ("Read the full review") instead of a bare "More". With one wording that suits both states, write it as plain text in the summary.

#When to use

Use expand for prose that most readers skim. For a list of questions and answers use an accordion, where each row is closed to its heading. Keep links and controls out of the hidden part: while closed, the keyboard can still reach what the preview has clipped.

#Accessibility

  • The summary is a button to assistive technology and reports whether the text is expanded. Find-in-page opens the details when a match is inside it.
  • The fade at the foot is a mask over the text, so the last visible line dims; the first lines keep full contrast.
  • With reduced motion the text opens at once.

#Classes

ClassDescription
expandThe details element; while closed it shows a preview that fades out at its foot
expand-toggleThe summary: first in the markup, drawn under the text, with a chevron that turns over
expand-moreThe toggle's wording while closed
expand-lessThe toggle's wording while open

#Custom properties

PropertyDefaultDescription
--expand-lines4The height of the preview, in lines of the text's own line height

#Source

@master/ui/components/expand.css
/* * Expand: a long text that shows its first lines and opens on request. * It is a <details>: the toggle is its <summary>, so the keyboard, find-in-page and assistive technology * already know how it works. Where the browser exposes ::details-content, the closed state is a preview, * --expand-lines tall, that fades out at its foot and opens to its full height. Elsewhere the text is * hidden while closed, as any details is. */@property --expand-fade {  syntax: '<length>';  inherits: false;  initial-value: 0px;}@layer components {  .expand {    --expand-lines: 4;    display: flex;    flex-direction: column;    min-width: 0;    interpolate-size: allow-keywords;    @supports selector(::details-content) {      &::details-content {        --expand-fade: 2lh;        display: block;        block-size: calc(var(--expand-lines) * 1lh);        overflow: clip;        content-visibility: visible;        mask-image: linear-gradient(black calc(100% - var(--expand-fade)), transparent);        transition-property: block-size, --expand-fade;        transition-duration: var(--duration-base);        transition-timing-function: var(--easing-glide);      }      &[open]::details-content {        --expand-fade: 0px;        block-size: auto;      }      @media (prefers-reduced-motion: reduce) {        &::details-content {          transition: none;        }      }    }  }  /* The toggle sits under the text it opens: a quiet word with a chevron that turns over. */  .expand-toggle {    order: 1;    display: inline-flex;    align-self: start;    align-items: center;    gap: .25rem;    margin-top: .5rem;    border-radius: calc((var(--radius-chip) - 2px) * var(--corner-scale));    corner-shape: var(--corner-shape);    color: var(--color-text-strong);    -webkit-tap-highlight-color: transparent;    font-size: .8125rem;    font-weight: var(--font-weight-control);    line-height: 1.4;    list-style: none;    user-select: none;    -webkit-user-select: none;    cursor: pointer;    touch-action: manipulation;    &::-webkit-details-marker {      display: none;    }    &::after {      content: '';      width: 14px;      height: 14px;      background: var(--color-text-muted);      mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 6.5 3.5 3.5 3.5-3.5'/%3E%3C/svg%3E") center / contain no-repeat;      transition: rotate var(--duration-base) var(--easing-glide);    }    &:focus-visible {      @apply --focus-ring;    }    @media (prefers-reduced-motion: reduce) {      &::after {        transition: none;      }    }    @media (forced-colors: active) {      &::after {        background: CanvasText;      }    }    @media (pointer: coarse) {      min-height: 2.75rem;    }  }  .expand[open] .expand-toggle::after {    rotate: x 180deg;  }  /* The toggle's two wordings: one shows while closed, the other while open. */  .expand[open] .expand-more {    display: none;  }  .expand:not([open]) .expand-less {    display: none;  }}