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:
| Scenario | Pattern |
|---|---|
| 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:
| Category | Icons |
|---|---|
| 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 |