Skip to main content
PaperStack
Buy Pro

Icons

PaperStack includes a purpose-built SVG icon set in @paperstack/icons. All icons are optimised stroked line icons at a 24×24 baseline grid, reflecting the hand-drawn, sketch-like quality of the paper aesthetic.

SVG Sprite Strategy

The recommended approach is to include the compiled SVG sprite once in your HTML and reference individual symbols with <use>. This gives you one HTTP request and efficient re-use of icons across the page.

<!-- Inject sprite once (e.g. in body or a portal) -->
<div id="ps-sprite-root" aria-hidden="true" style="display:none">
  <!-- sprite SVG content -->
</div>

<!-- Use an icon -->
<svg class="ps-icon" width="24" height="24" aria-hidden="true" focusable="false">
  <use href="#ps-icon-home"></use>
</svg>

Or inject the sprite dynamically using the injectSprite helper:

import { injectSprite } from "@paperstack/icons";
// Fetches /icons/sprite.svg and appends it to document.body
await injectSprite("/icons/sprite.svg");

Inline Usage

You can also reference an external sprite file with an absolute path. This works well with build tools that copy the sprite to a public folder:

<svg class="ps-icon" width="20" height="20" aria-label="Search" role="img">
  <use href="/icons/sprite.svg#ps-icon-search"></use>
</svg>

Accessibility

Follow these patterns to ensure icons are accessible:

ScenarioPattern
Decorative icon (label nearby) aria-hidden="true" focusable="false"
Icon is the only label (icon button) aria-label="..." on the <button>, icon gets aria-hidden
Icon stands alone as meaningful role="img" aria-label="..." on the <svg>
<!-- Decorative -->
<svg class="ps-icon" aria-hidden="true" focusable="false" width="24" height="24">
  <use href="#ps-icon-check"></use>
</svg>

<!-- Icon button -->
<button class="ps-button ps-button--icon" aria-label="Close dialog">
  <svg class="ps-icon" aria-hidden="true" focusable="false" width="24" height="24">
    <use href="#ps-icon-close"></use>
  </svg>
</button>

Sizing & Colour

Icons inherit color via currentColor on their strokes, so they automatically match surrounding text colour. Control size with width / height attributes or the --ps-icon-size custom property.

.my-large-icon {
  --ps-icon-size: 32px;
}

/* Force a specific colour */
.ps-icon--accent {
  color: var(--color-primary);
}

TypeScript API

The @paperstack/icons package exports typed helpers:

import {
  ICON_NAMES,   // readonly string[] of all valid icon names
  getIconId,    // (name) => "ps-icon-{name}"
  getSpriteHref, // (spritePath, name) => "{spritePath}#ps-icon-{name}"
  renderIcon,   // (name, options?) => SVG string
  injectSprite, // (spriteUrl) => Promise<void>
} from "@paperstack/icons";

// Type-safe icon name
import type { IconName } from "@paperstack/icons";
// Render an icon string (for server-side or template use)
const html = renderIcon("feather", { label: "Feather", size: 20 });

// Type-safe icon prop in React
interface Props { icon: IconName; }
function IconButton({ icon }: Props) { ... }

Icon Catalogue

Available icons grouped by category:

CategoryIcons
Navigation home, menu, close, search, chevron-up/down/left/right, arrow-left/right
Actions plus, minus, edit, trash, copy, save, download, upload, share, filter, sort
Status check, check-circle, alert-circle, alert-triangle, info, x-circle, loader
Content file, folder, image, link, external-link
UI Controls settings, more-horizontal, more-vertical, eye, eye-off, lock, unlock, user, bell, tag, bookmark, star, heart
Paper-specific layers, feather, pen, paperclip, scissors, notebook, stamp