Dots

Variants

Default   Accent   Subtle

Sizes

Default   Large

Inverse

On dark

Inline with text

Sending message


  
/*
 * Dots — foundation
 *
 * Inline loading indicator: three dots pulsing in sequence. CSS-only.
 * Smaller and quieter than Spinner — designed to live inside running
 * text or button labels.
 *
 *   <span class="dots" role="status" aria-label="Loading">
 *     <span class="dots__item"></span>
 *     <span class="dots__item"></span>
 *     <span class="dots__item"></span>
 *   </span>
 */

@layer malevich.components {
  .dots {
    --dots-size: var(--space-inset-element-s);
    --dots-gap: var(--space-inset-element-s);
    --dots-color: var(--color-ink-regular);
    --dots-duration: 1200ms;

    display: inline-flex;
    align-items: center;
    gap: var(--dots-gap);
    vertical-align: middle;
  }

  .dots__item {
    inline-size: var(--dots-size);
    block-size: var(--dots-size);
    border-radius: 50%;
    background-color: var(--dots-color);
    opacity: 0.3;
    animation: malevich-dots-pulse var(--dots-duration) ease-in-out infinite;
  }

  .dots__item:nth-child(1) { animation-delay: 0ms; }
  .dots__item:nth-child(2) { animation-delay: 200ms; }
  .dots__item:nth-child(3) { animation-delay: 400ms; }

  .dots.-accent {
    --dots-color: var(--color-accent);
  }

  .dots.-subtle {
    --dots-color: var(--color-ink-subtle);
  }

  .dots.-inverse {
    --dots-color: var(--color-ink-inverse);
  }

  .dots.-l {
    --dots-size: var(--space-inset-element-m);
    --dots-gap: var(--space-inset-element-m);
  }

  @keyframes malevich-dots-pulse {
    0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
    40%           { opacity: 1;   transform: scale(1); }
  }

  @media (prefers-reduced-motion: reduce) {
    .dots__item {
      animation: none;
      opacity: 0.6;
    }
  }
}
This component is pure CSS — no JavaScript required.
# Dots — AGENTS.md

> Auto-generated from `dots.docs.md` + the modifier applicability matrix.
> Edit those source files; this file regenerates on build.

## Identity

- **Component:** `dots`
- **Layer:** foundations
- **Status:** stable
- **Last updated:** 2026-05-18

## Summary

- Inline next to a label that's loading: "Sending…".

## Variants

| Variant   | Class               | Use for                                |
|-----------|---------------------|----------------------------------------|
| Default   | `.dots`             | Ink-regular dots, default size         |
| Accent    | `.dots.-accent`     | Accent-colored, for emphasis           |
| Subtle    | `.dots.-subtle`     | De-emphasized, for muted contexts      |
| Inverse   | `.dots.-inverse`    | On dark / accent surfaces              |
| Large     | `.dots.-l`          | Larger dots and gap                    |

## Modifier applicability

Per ADR-0007. Modifiers attach via `data-{category}="value"` attributes.

| Category | Accepts |
|---|---|
| `background` | — |
| `surface` | — |
| `effect` | — |
| `shader` | — |
| `rhythm` | — |
| `motion` | `none`, `standard` |
| `behavior` | — |

## Anatomy

```html
<span class="dots" role="status" aria-label="Loading">
  <span class="dots__item"></span>
  <span class="dots__item"></span>
  <span class="dots__item"></span>
</span>

<!-- Inside running text -->
<p>
  Sending message
  <span class="dots -subtle" role="status" aria-label="Loading">
    <span class="dots__item"></span>
    <span class="dots__item"></span>
    <span class="dots__item"></span>
  </span>
</p>
```

## Tokens consumed

### From semantic tier
- `--color-ink-regular` — default dot color
- `--color-ink-subtle` — subtle variant
- `--color-ink-inverse` — inverse variant
- `--color-accent` — accent variant
- `--space-inset-element-s` — default size and gap
- `--space-inset-element-m` — large size and gap

### Component-tier (defined inline)
- `--dots-size` — overridable dot diameter
- `--dots-gap` — overridable gap between dots
- `--dots-color` — overridable color
- `--dots-duration` — overridable pulse cycle

## Accessibility contract

Authors set `role="status"` and `aria-label="Loading"` on the outer
`.dots` element so assistive technology announces the loading state.

The three `.dots__item` children are presentational; they do not need
ARIA attributes. They render purely as visual rhythm.

The component honors `prefers-reduced-motion: reduce` by removing the
pulse animation and showing the dots at a constant opacity.

## Guidance

### Do

- Use dots when the work is inline-scoped (a single label, a single
  cell in a table).
- Use spinner when the work is layout-scoped (a whole section, a
  modal body).

### Don't

- Don't combine dots with a spinner in the same loading context — pick
  one indicator vocabulary per surface.
- Don't author more or fewer than three items. The component is a
  fixed-shape primitive.