Patterns

Compositions built from the components. These are illustrative, not part of the system - nothing here introduces a class of its own.

Examples

Content list

Card, Mark and the layout primitives together. This is the shape softkiwi.games uses for its games list, and it is the reason the Card component itself is deliberately free of any game-specific structure.

Our games

Kiwi Kart

Racing

Four players, one couch, no loading screens.

Orchard

Puzzle

Grow something out of almost nothing.

Night Shift

Roguelike

The vending machine is not your friend.

Show markup
<div class="sk-flex sk-flex-col sk-gap-4">
  <h2>
    Our
    <span class="sk-mark">
      games
    </span>
  </h2>
  <div class="sk-flex sk-flex-col sk-gap-3">
    <div class="sk-card">
      <div class="sk-card-body">
        <div class="sk-flex sk-flex-col sk-gap-1">
          <h3 class="sk-card-title">
            Kiwi Kart
          </h3>
          <p class="sk-card-subtitle">
            Racing
          </p>
          <p>
            Four players, one couch, no loading screens.
          </p>
        </div>
      </div>
    </div>
    <div class="sk-card">
      <div class="sk-card-body">
        <div class="sk-flex sk-flex-col sk-gap-1">
          <h3 class="sk-card-title">
            Orchard
          </h3>
          <p class="sk-card-subtitle">
            Puzzle
          </p>
          <p>
            Grow something out of almost nothing.
          </p>
        </div>
      </div>
    </div>
    <div class="sk-card">
      <div class="sk-card-body">
        <div class="sk-flex sk-flex-col sk-gap-1">
          <h3 class="sk-card-title">
            Night Shift
          </h3>
          <p class="sk-card-subtitle">
            Roguelike
          </p>
          <p>
            The vending machine is not your friend.
          </p>
        </div>
      </div>
    </div>
  </div>
</div>

Signup card

An input group inside a card. The button is attached to the control rather than sitting in a row beside the field, so nothing has to be aligned by hand.

Stay in touch

Occasional mail about what we are building. No spam.

Show markup
<div class="sk-card" data-elevated="true">
  <div class="sk-card-body">
    <div class="sk-flex sk-flex-col sk-gap-3">
      <h3 class="sk-card-title">
        Stay in touch
      </h3>
      <p>
        Occasional mail about what we are building. No spam.
      </p>
      <div class="sk-field">
        <label class="sk-label" for="pattern-email">
          Email
        </label>
        <div class="sk-input-group">
          <input class="sk-input" id="pattern-email" name="pattern-email" type="email" value="" placeholder="you@example.com" data-size="md" aria-invalid="false">
          <button class="sk-button" type="submit" data-variant="solid" data-size="md">
            Sign up
          </button>
        </div>
      </div>
    </div>
  </div>
</div>

Settings form

The real test of a spacing scale: mixed control types, a checkbox, and an action row.

We only use this to reply.

Show markup
<form>
  <div class="sk-flex sk-flex-col sk-gap-5">
    <div class="sk-field">
      <label class="sk-label" for="pattern-name">
        Name
        <span class="sk-required" aria-hidden="true">
          *
        </span>
      </label>
      <input class="sk-input" id="pattern-name" name="pattern-name" type="text" value="" autocomplete="name" data-size="md" aria-invalid="false" required>
    </div>
    <div class="sk-field">
      <label class="sk-label" for="pattern-contact">
        Email
        <span class="sk-required" aria-hidden="true">
          *
        </span>
      </label>
      <input class="sk-input" id="pattern-contact" name="pattern-contact" type="email" value="" autocomplete="email" data-size="md" aria-invalid="false" aria-describedby="pattern-contact-hint" required>
      <p class="sk-hint" id="pattern-contact-hint">
        We only use this to reply.
      </p>
    </div>
    <div class="sk-field">
      <label class="sk-label" for="pattern-visibility">
        Visibility
      </label>
      <select class="sk-select" id="pattern-visibility" name="pattern-visibility" data-size="md" aria-invalid="false">
        <option value="public">
          Public
        </option>
        <option value="private" selected>
          Private
        </option>
      </select>
    </div>
    <div class="sk-field">
      <label class="sk-label" for="pattern-bio">
        Bio
      </label>
      <textarea class="sk-textarea" id="pattern-bio" name="pattern-bio" rows="4" aria-invalid="false">
      </textarea>
    </div>
    <label class="sk-check" for="pattern-terms">
      <input id="pattern-terms" name="pattern-terms" type="checkbox" value="">
      <span class="sk-check-label">
        I have read the privacy policy
      </span>
    </label>
    <div class="sk-button-group" role="group" data-attached="false">
      <button class="sk-button" type="submit" data-variant="solid" data-size="md">
        Save
      </button>
      <button class="sk-button" type="reset" data-variant="quiet" data-size="md">
        Cancel
      </button>
    </div>
  </div>
</form>

Build pipeline

The dot doing what it was extracted for. Queued steps are hollow, the running step spins, finished steps take their status colour - and every row still says its state in words, because the vivid greens and ambers do not clear 3:1 on white.

Build 412

main - pushed 4 minutes ago

In progress
PassedCheckout 2s
PassedInstall dependencies 41s
PassedVet and test 1m 12s
RunningLayout audit running
QueuedBuild image
QueuedDeploy
Show markup
<div class="sk-card">
  <div class="sk-card-header">
    <div class="sk-flex sk-flex-col sk-gap-1">
      <h3 class="sk-card-title">
        Build 412
      </h3>
      <p class="sk-card-subtitle">
        main - pushed 4 minutes ago
      </p>
    </div>
    <span class="sk-badge" data-status="info">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="M12 11v5">
        </path>
        <path d="M12 8h.01">
        </path>
      </svg>
      In progress
    </span>
  </div>
  <div class="sk-card-body">
    <div class="sk-flex sk-flex-col sk-gap-3">
      <div class="sk-flex sk-items-center sk-gap-3">
        <span class="sk-dot" data-status="success" data-size="lg">
          <span class="sk-visually-hidden">
            Passed
          </span>
        </span>
        <span class="sk-flex-1 sk-min-w-0">
          Checkout
        </span>
        <span class="sink-caption">
          2s
        </span>
      </div>
      <div class="sk-flex sk-items-center sk-gap-3">
        <span class="sk-dot" data-status="success" data-size="lg">
          <span class="sk-visually-hidden">
            Passed
          </span>
        </span>
        <span class="sk-flex-1 sk-min-w-0">
          Install dependencies
        </span>
        <span class="sink-caption">
          41s
        </span>
      </div>
      <div class="sk-flex sk-items-center sk-gap-3">
        <span class="sk-dot" data-status="success" data-size="lg">
          <span class="sk-visually-hidden">
            Passed
          </span>
        </span>
        <span class="sk-flex-1 sk-min-w-0">
          Vet and test
        </span>
        <span class="sink-caption">
          1m 12s
        </span>
      </div>
      <div class="sk-flex sk-items-center sk-gap-3">
        <span class="sk-dot" data-status="warning" data-size="lg" data-busy="true" data-motion="essential">
          <span class="sk-visually-hidden">
            Running
          </span>
        </span>
        <span class="sk-flex-1 sk-min-w-0">
          Layout audit
        </span>
        <span class="sink-caption">
          running
        </span>
      </div>
      <div class="sk-flex sk-items-center sk-gap-3">
        <span class="sk-dot" data-size="lg" data-hollow="true">
          <span class="sk-visually-hidden">
            Queued
          </span>
        </span>
        <span class="sk-flex-1 sk-min-w-0">
          Build image
        </span>
      </div>
      <div class="sk-flex sk-items-center sk-gap-3">
        <span class="sk-dot" data-size="lg" data-hollow="true">
          <span class="sk-visually-hidden">
            Queued
          </span>
        </span>
        <span class="sk-flex-1 sk-min-w-0">
          Deploy
        </span>
      </div>
    </div>
  </div>
  <div class="sk-card-footer">
    <div class="sk-progress-field">
      <div class="sk-progress-head">
        <span class="sk-progress-value">
          3 of 6 steps
        </span>
      </div>
      <progress class="sk-progress" value="3" max="6">
      </progress>
    </div>
  </div>
</div>

Toolbar

A button group and independent actions in one row, pushed apart with sk-justify-between.

Show markup
<div class="sk-flex sk-flex-wrap sk-items-center sk-justify-between sk-gap-3">
  <div class="sk-button-group" role="group" aria-label="View">
    <button class="sk-button" type="button" data-variant="neutral" data-size="md" aria-pressed="true">
      Grid
    </button>
    <button class="sk-button" type="button" data-variant="neutral" data-size="md" aria-pressed="false">
      List
    </button>
  </div>
  <div class="sk-flex sk-items-center sk-gap-2">
    <button class="sk-button" type="button" data-variant="quiet" data-size="sm">
      Filter
    </button>
    <button class="sk-button" type="button" data-variant="solid" data-size="sm">
      New
    </button>
  </div>
</div>