Skip to main content
PaperStack
Buy Pro

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:

ClassEffect
ps-paper--flatNo shadow, no elevation
ps-paper--raisedElevated with shadow
ps-paper--floatingHigh elevation (menus, tooltips)
ps-paper--texturedSubtle SVG grain texture
ps-paper--agedSepia-tinted, aged paper look
ps-paper--soft-edgeVignette / 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>
ClassVariant
ps-button--primaryFilled primary action
ps-button--secondaryOutlined secondary
ps-button--ghostTransparent with hover
ps-button--dangerDestructive action
ps-button--iconSquare 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>

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">&times;</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">&times;</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:

ClassDescription
ps-boxBlock-level box with token-driven padding/margin
ps-flex-boxFlexbox wrapper with --align, --justify, --gap custom props
ps-grid-boxCSS Grid wrapper with --grid-cols custom prop
ps-stack-boxVertical stack with gap presets
ps-container-boxMax-width centered container
ps-surfaceBackground surface (semantic wrapper for paper sheets)
ps-layerLayering context for overlays and portals
ps-portal-rootMount point for portaled content
ps-visually-hiddenScreen-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.