Skip to content
Ahimsa

Components

Ten primitives, and the markup to paste

These are the web reference implementation: plain CSS classes, no framework, no runtime, no build step. On a phone or desktop app, take the same proportions, tokens and refusals and build the native equivalent. Every snippet below is complete and correct, including its ARIA.

Actions

Button

A quiet CTA in wide-tracked uppercase small caps. Press shrinks by 3% and never bounces. The feedback is :active rather than a pointerdown handler, so sliding your finger off still cancels the action — which matters to anyone whose aim is not precise.

Variants

--ghost is an outlined pill, not gray text. Gray text beside a filled pill reads as a caption, and a caption is not a choice. --danger is warm earth, never red: removing something is not an emergency.

<button class="ah-btn" type="button">Continue</button>
<button class="ah-btn ah-btn--ghost" type="button">Not now</button>
<button class="ah-btn ah-btn--sm ah-hit" type="button">Small</button>
<button class="ah-btn ah-btn--danger" type="button">Remove it</button>

On a gradient

Glass needs something behind it. On flat cream it reads as nothing — keep it for surfaces sitting on a gradient.

<div class="ah-surface--dawn">
  <button class="ah-btn ah-btn--glass" type="button">Glass</button>
</div>

Actions

Pill

The workhorse: tags, switchers, filters, quiet actions. Always wrap small pills in .ah-hit — the painted control is under the 44px target floor, and that class grows what the finger hits without moving a pixel of what the eye sees.

Variants and states

With a dot

The dot is never the only carrier of its meaning — the pill's own text says the same thing, because color is not a channel everyone has.

<button class="ah-pill ah-hit" type="button">Default</button>
<button class="ah-pill is-active ah-hit" type="button" aria-pressed="true">Active</button>
<span class="ah-pill ah-pill--static">
  <span class="ah-pill__dot"></span>With a dot
</span>

Forms

Field

No box. A single bottom rule that strengthens on focus does the whole job: a full border around a text field is a container, and a container implies something being collected.

Utility and essence

Inputs are 1rem, not the 0.8125rem the rest of the UI uses. Safari on iOS zooms the viewport when a field under 16px takes focus, and a zoomed fixed-position app cannot be scrolled — which reads as the app breaking the moment you try to type.

<label class="ah-field">
  <span class="ah-field__label">Their name</span>
  <input type="text" placeholder="Someone you are thinking of">
</label>

<label class="ah-field ah-field--essence">
  <span class="ah-field__label">What is alive in you</span>
  <textarea rows="2" placeholder="Say it however it comes"></textarea>
</label>

Forms

ToggleChip

Multi-select in the emotional voice. Nothing is pre-selected and nothing is required.

Selected and not

<button class="ah-chip" type="button" role="checkbox" aria-checked="false">seen</button>
<button class="ah-chip is-selected" type="button" role="checkbox" aria-checked="true">held</button>

Surfaces

Card

Three surface levels plus one glass level, expressed as background first and shadow second. A sunk surface gets no shadow at all — the color is the elevation.

Variants

elevated

The default. Warm paper shadow with an inner highlight.

list

A lighter shadow, for rows that repeat.

sunk

Recessed. No shadow at all.

gradient

One featured moment per screen.

No colored-left-border cards and no hard outlines. Two gradient cards side by side is the rule this system breaks least gladly.

<div class="ah-card">…</div>
<div class="ah-card ah-card--list">…</div>
<div class="ah-card ah-card--sunk">…</div>

<!-- a gradient card carries its own measured ink -->
<div class="ah-card ah-surface--cream">…</div>

Surfaces

Sheet

The bottom sheet, and the only modal this system has. Three ways out, always: the scrim, the grab handle, and a visible Close at equal weight. Never a trap. Putting the Close in the shared chrome is what makes omitting it structurally impossible rather than something to remember.

Structure

Share this card

It leaves this device only when you send it.

Wire Escape to close as well. A sheet you can only leave by dragging is not leaveable by everyone.

<div class="ah-scrim" role="presentation">
  <div class="ah-sheet" role="dialog" aria-modal="true" aria-labeledby="t">
    <div class="ah-sheet__grab"></div>
    <div class="ah-sheet__head">
      <h2 class="ah-sheet__title" id="t">Share this card</h2>
      <button class="ah-sheet__close" type="button" aria-label="Close">×</button>
    </div>
    …
  </div>
</div>

Display

Small caps, pull quote, monogram, dots

This system is deliberately icon-light, and its most distinctive "icon" is the absence of one: there is no notification badge component, by design. Updates surface through the rhythm of use, never a red dot.

SmallCapsLabel

Last together With a rule Micro, for the quietest metadata
<span class="ah-caps">Last together</span>
<span class="ah-caps ah-caps--rule">With a rule</span>
<span class="ah-caps ah-caps--micro">Quietest metadata</span>

PullQuote

Nothing here is designed to grab you.

In NVC, every no is a yes to something else.

Never quotation marks. The setting is already the quotation; the marks would only shout.

<p class="ah-quote">Nothing here is designed to grab you.</p>
<p class="ah-quote ah-quote--bordered">Every no is a yes to something else.</p>

Monogram — an initial, never a photograph

A R M J

Mark it aria-hidden when the person's name is already beside it — otherwise a screen reader reads the initial twice.

<span class="ah-mono" aria-hidden="true">R</span>
<span class="ah-mono ah-mono--s84 ah-mono--sunk" aria-hidden="true">M</span>

ProgressDots — where you are, with no count

Each dot is a real button. Pagination you can only reach by dragging is not reachable at all for some people (WCAG 2.5.7), and the 44px target is added invisibly.

<div class="ah-dots">
  <button type="button" class="is-on" aria-label="Page 1 of 4" aria-current="true"></button>
  <button type="button" aria-label="Page 2 of 4"></button>
</div>

SectionRule

This week

<p class="ah-rule-row ah-caps">This week</p>

Feedback

Toast and Notice

This is where the attention principle is load-bearing. Nothing in an Ahimsa interface appears on its own. The test is not "is it dismissible" but did the person cause this, here, now?

Toast — the acknowledgement of an action just taken

Saved to this device. You can undo this.

Always role="status" with aria-live="polite". Never role="alert" or aria-live="assertive": an assertive region interrupts a screen-reader user mid-sentence, which is the same harm in a different channel.

<div class="ah-toast" role="status" aria-live="polite">
  <div class="ah-caption">Saved to this device. You can undo this.</div>
</div>

Notice — what replaces every banner this system refuses

Version

You are running 1.4.0.

That did not go through

The file could not be read. Nothing was changed, and your cards are as they were.

A notice sits in the document flow, where the person already is. It does not float, does not cover anything, and does not take focus. There is deliberately no UpdateBar, no InstallBar, no banner and no modal in this system.

<div class="ah-notice">
  <p class="ah-notice__title">Version</p>
  <div class="ah-notice__row">
    <p class="ah-body">You are running 1.4.0.</p>
    <button class="ah-btn ah-btn--sm ah-btn--ghost ah-hit">Check for a new one</button>
  </div>
</div>