Components
PaperStack components are built CSS-first. Every component is a set of BEM-style class names that you apply to semantic HTML. JavaScript is only used where behaviour is required (tabs, modals, drawers, accordions).
Import the full component stylesheet or individual files:
/* Full bundle */
@use "@paperstack/components/src/index.scss";
/* Individual */
@use "@paperstack/components/src/button/button.scss";
@use "@paperstack/components/src/card/card.scss"; Paper
The foundational surface. All page content sits on paper sheets.
<div class="ps-paper">Default paper surface</div>
<div class="ps-paper ps-paper--raised">Raised sheet</div>
<div class="ps-paper ps-paper--textured">Grain texture</div>
<div class="ps-paper ps-paper--aged">Aged look</div> Modifier classes:
| Class | Effect |
|---|---|
ps-paper--flat | No shadow, no elevation |
ps-paper--raised | Elevated with shadow |
ps-paper--floating | High elevation (menus, tooltips) |
ps-paper--textured | Subtle SVG grain texture |
ps-paper--aged | Sepia-tinted, aged paper look |
ps-paper--soft-edge | Vignette / blurred edge |
Button
<button class="ps-button">Default</button>
<button class="ps-button ps-button--primary">Primary</button>
<button class="ps-button ps-button--ghost">Ghost</button>
<button class="ps-button ps-button--sm">Small</button>
<button class="ps-button ps-button--lg">Large</button>
<button class="ps-button" disabled>Disabled</button> | Class | Variant |
|---|---|
ps-button--primary | Filled primary action |
ps-button--secondary | Outlined secondary |
ps-button--ghost | Transparent with hover |
ps-button--danger | Destructive action |
ps-button--icon | Square icon-only button |
Card
<div class="ps-card">
<div class="ps-card__header">Title</div>
<div class="ps-card__body">Content</div>
<div class="ps-card__footer">Actions</div>
</div> Use ps-card--interactive to make the whole card a clickable surface.
Badge
<span class="ps-badge">Default</span>
<span class="ps-badge ps-badge--primary">Primary</span>
<span class="ps-badge ps-badge--success">Success</span>
<span class="ps-badge ps-badge--warning">Warning</span>
<span class="ps-badge ps-badge--error">Error</span>
<span class="ps-badge ps-badge--pill">Pill</span>
<span class="ps-badge ps-badge--dot" aria-label="3 notifications"></span> Divider
<hr class="ps-divider" />
<hr class="ps-divider ps-divider--strong" />
<div class="ps-divider ps-divider--label">
<span>or</span>
</div> Tabs
Tabs require the JS initializer from @paperstack/components:
import { initTabs } from "@paperstack/components";
initTabs(); // call after DOM ready <div class="ps-tabs" role="tablist" aria-label="Example tabs">
<button class="ps-tab ps-tab--active" role="tab" aria-selected="true" data-target="panel-1">Tab 1</button>
<button class="ps-tab" role="tab" aria-selected="false" data-target="panel-2">Tab 2</button>
</div>
<div id="panel-1" class="ps-tab-panel" role="tabpanel">Panel 1 content</div>
<div id="panel-2" class="ps-tab-panel ps-tab-panel--hidden" role="tabpanel">Panel 2</div> Modal
Requires initModal() from @paperstack/components.
<button data-modal-open="my-modal" class="ps-button ps-button--primary">Open Modal</button>
<div id="my-modal" class="ps-modal" role="dialog" aria-modal="true" aria-labelledby="modal-title" hidden>
<div class="ps-modal__backdrop"></div>
<div class="ps-modal__panel ps-paper ps-paper--raised">
<header class="ps-modal__header">
<h2 id="modal-title" class="ps-heading-sm">Modal Title</h2>
<button class="ps-modal__close ps-button ps-button--icon" aria-label="Close">×</button>
</header>
<div class="ps-modal__body">Content here</div>
<footer class="ps-modal__footer">
<button class="ps-button ps-button--primary" data-modal-close>Confirm</button>
</footer>
</div>
</div> Toast
<div class="ps-toast-region" aria-live="polite" aria-label="Notifications">
<div class="ps-toast ps-toast--success" role="status">
Changes saved successfully.
<button class="ps-toast__close" aria-label="Dismiss">×</button>
</div>
</div> Annotation
Inline editorial mark-up — like a pen annotation on paper.
<span class="ps-annotation">Note in the margin</span>
<span class="ps-annotation ps-annotation--stabilo">Highlighted</span> Alert
<div class="ps-alert ps-alert--info" role="note">
<strong>Info:</strong> This is an informational message.
</div>
<div class="ps-alert ps-alert--success" role="status">Saved!</div>
<div class="ps-alert ps-alert--warning" role="alert">Heads up.</div>
<div class="ps-alert ps-alert--error" role="alert">Something went wrong.</div> Layout Primitives
Semantic layout building blocks that compose the physical desk metaphor:
| Class | Description |
|---|---|
ps-box | Block-level box with token-driven padding/margin |
ps-flex-box | Flexbox wrapper with --align, --justify, --gap custom props |
ps-grid-box | CSS Grid wrapper with --grid-cols custom prop |
ps-stack-box | Vertical stack with gap presets |
ps-container-box | Max-width centered container |
ps-surface | Background surface (semantic wrapper for paper sheets) |
ps-layer | Layering context for overlays and portals |
ps-portal-root | Mount point for portaled content |
ps-visually-hidden | Screen-reader-only, visually hidden |
Framework Adapters
All components are available as typed wrappers for React, Vue, and Svelte via
@paperstack/adapters.
/* React */
import { PsButton, PsCard, PsModal } from "@paperstack/adapters/react";
/* Vue */
import { PsButton, PsCard, PsModal } from "@paperstack/adapters/vue";
/* Svelte */
import { PsButton, PsCard, PsModal } from "@paperstack/adapters/svelte"; Each adapter maps component props to the underlying CSS class API and handles event binding, accessibility attributes, and reactive state.