π Quick Start
</> button that reveals usage code. Click to inspect implementation.
Basic Installation
Add to HTML:
<link rel="stylesheet" href="paper.css">
Styles apply globally; wrap content in optional containers/cards.
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<link rel="stylesheet" href="paper.css" />
</head>
<body>
<div class="container">
<div class="card">Your content</div>
</div>
<script src="paper.js"></script>
</body>
</html>
π Typography & Basics
Heading Scale
H1 Main Title
H2 Section
H3 Subsection
H4 Smaller Heading
H5 Lead Text
H6 Meta Info
<h1>H1 Main Title</h1>
<h2>H2 Section</h2>
<h3>H3 Subsection</h3>
<h4>H4 Smaller Heading</h4>
<h5>H5 Lead Text</h5>
<h6>H6 Meta Info</h6>
Paragraph & Inline Elements
This framework uses CSS Variables, a fluid scale and spacing utilities.
<div class="card layered">Card content</div>
Switch theme using data-theme="dark" on <html>.
<p>Normal paragraph with <code>inline code</code></p>
<p class="text-muted">Muted text</p>
<pre><code>Code block</code></pre>
<p class="text-center">Centered</p>
<p class="text-right">Right aligned</p>
π¨ Palette & CSS Variables
Core Colors
:root {
--paper-accent: #346cab;
--paper-accent-hover: #3e79bd;
--paper-success: #28a745;
--paper-warning: #ffc107;
--paper-danger: #dc3545;
--paper-info: #17a2b8;
}
[data-theme="dark"] {
--paper-bg: #1a1a1a;
--paper-ink: #e8e4d9;
}
π§© Core Components (Excerpt)
Translate & adapt remaining Polish demo sections similarly if needed.
See Polish `index.html` for full extended showcase (alerts, tabs, accordion, etc.).
π New Components
Skeleton Loading
Placeholder animation while content is loading.
This is normal content with text and images.
More content to hide behind skeleton.
<!-- HTML - content container -->
<div id="content-demo">
<p>Content to hide behind skeleton</p>
<div class="user-avatar"></div>
<p>More content</p>
</div>
<!-- Control buttons -->
<button onclick="PaperSkeleton.show('#content-demo')">
Show skeleton
</button>
<button onclick="PaperSkeleton.hide('#content-demo')">
Hide skeleton
</button>
<!-- JavaScript API -->
<script>
// Show skeleton on element
PaperSkeleton.show('#content-demo');
// Hide skeleton and restore content
PaperSkeleton.hide('#content-demo');
// Auto-hide after timeout
setTimeout(() => PaperSkeleton.hide('#content-demo'), 3000);
</script>
Offcanvas Panel
Side panel sliding from screen edge with animation and scroll lock.
<!-- Trigger button -->
<button class="btn btn-primary" data-open-offcanvas="demo-offcanvas">
Open panel
</button>
<!-- Offcanvas structure -->
<div class="offcanvas-backdrop" id="demo-offcanvas-backdrop">
<div class="offcanvas" id="demo-offcanvas">
<div class="offcanvas-header">
<h3>Panel title</h3>
<button class="offcanvas-close btn-ghost">×</button>
</div>
<div class="offcanvas-body">
<p>Side panel content</p>
<ul>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
<div class="mt-4">
<button class="btn btn-primary">Save</button>
<button class="btn btn-outline">Cancel</button>
</div>
</div>
</div>
</div>
<!-- JavaScript - automatic with data-open-offcanvas -->
Dropdown Menu
Dropdown menu with keyboard support and animations.
<!-- Dropdown with button and menu -->
<div class="dropdown">
<button class="btn btn-secondary" data-dropdown-toggle>
Menu options βΌ
</button>
<ul class="dropdown-menu">
<li><a href="#" class="dropdown-item">βοΈ Settings</a></li>
<li><a href="#" class="dropdown-item">π€ Profile</a></li>
<li><hr class="dropdown-divider"></li>
<li><a href="#" class="dropdown-item">πͺ Logout</a></li>
</ul>
</div>
<!-- Different button styles -->
<div class="dropdown">
<button class="btn btn-outline" data-dropdown-toggle>
More β―
</button>
<ul class="dropdown-menu">
<li><a href="#" class="dropdown-item">Edit</a></li>
<li><a href="#" class="dropdown-item">Delete</a></li>
</ul>
</div>
<!-- Automatic handling via data-dropdown-toggle -->
Chip/Tag System
Interactive tags with removal capability and different styles.
<!-- Chip/tag group -->
<div class="chip-group">
<!-- Basic chip -->
<div class="chip">
<span>React</span>
<button class="chip-close">×</button>
</div>
<!-- Outlined chip -->
<div class="chip chip-outlined">
<span>JavaScript</span>
<button class="chip-close">×</button>
</div>
<!-- Small chip -->
<div class="chip chip-small">
<span>CSS</span>
<button class="chip-close">×</button>
</div>
<!-- Chip without close button -->
<div class="chip">
<span>HTML</span>
</div>
</div>
<!-- JavaScript for chip removal -->
<script>
// Auto-removal on .chip-close click
document.addEventListener('click', (e) => {
if (e.target.classList.contains('chip-close')) {
e.target.closest('.chip').remove();
}
});
</script>
Progress Bars
Progress bars with animated filling and different styles.
Basic progress bars
<!-- Basic progress bar -->
<div class="progress-container">
<div class="progress-bar" id="progress1" style="width: 0%"></div>
</div>
<!-- Colored variants -->
<div class="progress-container">
<div class="progress-bar progress-success" style="width: 75%"></div>
</div>
<div class="progress-container">
<div class="progress-bar progress-warning" style="width: 60%"></div>
</div>
<div class="progress-container">
<div class="progress-bar progress-danger" style="width: 30%"></div>
</div>
<!-- Progress with label -->
<div class="flex justify-between items-center mb-1">
<label class="text-sm text-muted">Downloading file</label>
<span class="text-sm text-muted" id="progress-label">0%</span>
</div>
<div class="progress-container">
<div class="progress-bar progress-info" id="progress" style="width: 0%"></div>
</div>
<!-- Thin progress bars -->
<div class="progress-container progress-thin">
<div class="progress-bar" style="width: 40%"></div>
</div>
<div class="progress-container progress-xs">
<div class="progress-bar progress-success" style="width: 70%"></div>
</div>
<!-- JavaScript API -->
<script>
// Animate progress bar
PaperProgress.animate('#progress', 75, 2000);
// With callback after completion
PaperProgress.animate('#progress', 100, 1500, () => {
console.log('Progress complete!');
});
</script>
β Additional Components
Tooltip System
Contextual tooltips displayed on hover or keyboard focus.
Basic tooltips
Other elements with tooltips
<!-- Basic tooltips -->
<button class="btn btn-primary" data-tooltip="Main action button">
Primary Button
</button>
<button class="btn btn-success" data-tooltip="Operation successful">
β Success
</button>
<!-- Tooltips on other elements -->
<span class="badge" data-tooltip="Status information">New</span>
<code data-tooltip="CSS class description">.card</code>
<a href="#" data-tooltip="Link description">Link text</a>
<!-- Icon buttons with tooltips -->
<button class="btn btn-outline" data-tooltip="Application settings">
βοΈ
</button>
<button class="btn btn-outline" data-tooltip="User profile">
π€
</button>
<button class="btn btn-outline" data-tooltip="Notifications">
π
</button>
<!-- Automatic positioning and keyboard support included -->
Alert/Banner System
Static and dynamic alert components for user notifications.
<!-- Static alerts -->
<div class="alert alert-info">
<strong>Info:</strong> Informational message.
</div>
<div class="alert alert-success">
<strong>Success:</strong> Operation successful.
</div>
<div class="alert alert-warning">
<strong>Warning:</strong> Check your input.
</div>
<div class="alert alert-error">
<strong>Error:</strong> Something went wrong.
</div>
<!-- Dismissible alerts -->
<div class="alert alert-info alert-dismissible">
<strong>Info:</strong> This alert can be closed.
<button class="alert-close">×</button>
</div>
<!-- Dynamic alerts with JavaScript -->
<script>
function showDynamicAlert(message, type, dismissible) {
const container = document.getElementById('alerts-container');
const alert = document.createElement('div');
alert.className = `alert alert-${type}${dismissible ? ' alert-dismissible' : ''}`;
alert.innerHTML = `
<strong>${type.charAt(0).toUpperCase() + type.slice(1)}:</strong> ${message}
${dismissible ? '<button class="alert-close">×</button>' : ''}
`;
container.appendChild(alert);
}
</script>
Loading States
Various loading states in e-ink style.
<!-- Dots loading animation -->
<div class="loading-dots">
<span></span>
<span></span>
<span></span>
</div>
<!-- Spinner loading -->
<div class="loading-spinner"></div>
<!-- Pulse loading -->
<div class="loading-pulse"></div>
<!-- Loading text -->
<span class="text-muted">Loading...</span>
<!-- Usage in buttons -->
<button class="btn" disabled>
<div class="loading-dots">
<span></span>
<span></span>
<span></span>
</div>
Loading...
</button>
Empty State
No Data
No items found to display. Add a new item or change your search filters.
<!-- Empty state component -->
<div class="empty-state">
<div class="empty-state-icon">π</div>
<h3>No Data</h3>
<p>No items found to display. Add a new item or change your search filters.</p>
<button class="btn btn-primary">Add Item</button>
</div>
<!-- Different empty states -->
<div class="empty-state">
<div class="empty-state-icon">π</div>
<h3>No Search Results</h3>
<p>We couldn't find anything matching your search.</p>
<button class="btn btn-outline">Clear Search</button>
</div>
<div class="empty-state">
<div class="empty-state-icon">π</div>
<h3>No Tasks</h3>
<p>You're all caught up! No tasks to complete.</p>
<button class="btn btn-primary">Add New Task</button>
</div>
π Data Tables
Data Table with Actions
| # | Name | Status | Tag | Actions |
|---|---|---|---|---|
| 01 | Alpha Element | New | .alpha |
|
| 02 | Beta Element | Info | .beta |
|
| 03 | Gamma Element | New | .gamma |
<!-- Table wrapper for responsiveness -->
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>01</td>
<td>Element Name</td>
<td><span class="badge">New</span></td>
<td>
<button class="btn btn-ghost" data-tooltip="Preview">π</button>
<button class="btn btn-ghost" data-tooltip="Edit">β</button>
<button class="btn btn-ghost" data-tooltip="Delete">π</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Pagination -->
<nav class="pagination">
<button disabled>«</button>
<button class="active">1</button>
<button>2</button>
<button>3</button>
<button>»</button>
</nav>
<!-- Breadcrumb navigation -->
<nav class="breadcrumb" aria-label="breadcrumb">
<a href="#">Home</a> <span>/</span>
<a href="#">Section</a> <span>/</span>
<span>Current</span>
</nav>
π Forms with States
Form with Validation States
<form class="maxw-md">
<!-- Success state -->
<div class="form-group field-success">
<label for="name">First Name</label>
<input type="text" id="name" placeholder="Enter name" />
<div class="help-text">Looks good!</div>
</div>
<!-- Warning state -->
<div class="form-group field-warning">
<label for="email">Email</label>
<input type="email" id="email" />
<div class="help-text">Check format.</div>
</div>
<!-- Error state -->
<div class="form-group field-danger">
<label for="password">Password</label>
<input type="password" id="password" />
<div class="help-text">Too short.</div>
</div>
<!-- Select with info state -->
<div class="form-group field-info">
<label for="role">Role</label>
<select id="role">
<option>Choose...</option>
<option>Admin</option>
<option>User</option>
</select>
<div class="help-text">Select role.</div>
</div>
<!-- Textarea -->
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" rows="4"></textarea>
<div class="help-text">Optional.</div>
</div>
<!-- Inline fields -->
<div class="form-group input-inline">
<div>
<label for="inline1">Field 1</label>
<input type="text" id="inline1" />
</div>
<div>
<label for="inline2">Field 2</label>
<input type="text" id="inline2" />
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn">Submit</button>
<button type="reset" class="btn btn-outline">Reset</button>
</div>
</form>
π§ Useful Components
Toggle Switches
Modern toggle switches in paper style with animations.
Basic switches
Sizes
<!-- Basic toggle -->
<label class="switch">
<input type="checkbox">
<span class="switch-slider"></span>
</label>
<!-- Color variants -->
<label class="switch switch-success">
<input type="checkbox" checked>
<span class="switch-slider"></span>
</label>
<label class="switch switch-warning">
<input type="checkbox" checked>
<span class="switch-slider"></span>
</label>
<label class="switch switch-danger">
<input type="checkbox" checked>
<span class="switch-slider"></span>
</label>
<label class="switch switch-info">
<input type="checkbox" checked>
<span class="switch-slider"></span>
</label>
<!-- Different sizes -->
<label class="switch switch-sm">
<input type="checkbox">
<span class="switch-slider"></span>
</label>
<label class="switch switch-lg">
<input type="checkbox">
<span class="switch-slider"></span>
</label>
<!-- Disabled -->
<label class="switch">
<input type="checkbox" disabled>
<span class="switch-slider"></span>
</label>
Timeline
Paper-style timeline for chronological presentation.
<div class="timeline">
<div class="timeline-item">
<div class="timeline-header">
<div class="timeline-title">Event title</div>
<div class="timeline-date">January 2024</div>
</div>
<div class="timeline-content">
Event description in timeline.
</div>
</div>
<div class="timeline-item timeline-success">
<div class="timeline-header">
<div class="timeline-title">Success</div>
<div class="timeline-date">March 2024</div>
</div>
<div class="timeline-content">
Positive event.
</div>
</div>
<div class="timeline-item timeline-warning">
<div class="timeline-header">
<div class="timeline-title">Warning</div>
<div class="timeline-date">April 2024</div>
</div>
<div class="timeline-content">
Event requiring attention.
</div>
</div>
<div class="timeline-item timeline-danger">
<div class="timeline-header">
<div class="timeline-title">Issue</div>
<div class="timeline-date">May 2024</div>
</div>
<div class="timeline-content">
Critical event.
</div>
</div>
</div>
Date Picker
Paper-style date picker with calendar.
<div class="date-picker">
<input type="text" class="date-picker-input" placeholder="Select date" readonly>
<div class="date-picker-calendar">
<div class="date-picker-header">
<button class="date-picker-nav" data-action="prev">βΉ</button>
<div class="date-picker-month"></div>
<button class="date-picker-nav" data-action="next">βΊ</button>
</div>
<div class="date-picker-grid">
<div class="date-picker-weekday">Mo</div>
<div class="date-picker-weekday">Tu</div>
<div class="date-picker-weekday">We</div>
<div class="date-picker-weekday">Th</div>
<div class="date-picker-weekday">Fr</div>
<div class="date-picker-weekday">Sa</div>
<div class="date-picker-weekday">Su</div>
<!-- Days generated by JavaScript -->
</div>
</div>
</div>
<!-- Include JavaScript -->
<script src="datepicker.js"></script>
<!-- Auto-initialization - all .date-picker elements become active -->
<!-- Or manual initialization: -->
<script>
// Basic usage
const picker1 = new PaperDatePicker('#date-input');
// With options
const picker2 = new PaperDatePicker('#date-input-2', {
format: 'MM/DD/YYYY',
minDate: new Date(),
maxDate: new Date(2025, 11, 31),
onSelect: function(date) {
console.log('Selected date:', date);
}
});
// Set date programmatically
picker1.setDate(new Date());
// Get selected date
console.log(picker1.getDate());
</script>
Sidebar Navigation
Side navigation panel with animations and grouping.
<!-- Open button -->
<button onclick="openSidebar()">Open menu</button>
<!-- Sidebar structure -->
<div class="sidebar-overlay" id="sidebar-overlay"></div>
<div class="sidebar" id="sidebar">
<div class="sidebar-header">
<h3 class="sidebar-title">Menu</h3>
</div>
<nav class="sidebar-nav">
<div class="sidebar-nav-group">
<div class="sidebar-nav-group-title">Main</div>
<a href="#" class="sidebar-nav-item active">Dashboard</a>
<a href="#" class="sidebar-nav-item">Projects</a>
<a href="#" class="sidebar-nav-item">Tasks</a>
</div>
<div class="sidebar-nav-group">
<div class="sidebar-nav-group-title">Settings</div>
<a href="#" class="sidebar-nav-item">Profile</a>
<a href="#" class="sidebar-nav-item">Preferences</a>
</div>
</nav>
</div>
<!-- JavaScript -->
<script>
function openSidebar() {
document.getElementById('sidebar').classList.add('open');
document.getElementById('sidebar-overlay').classList.add('open');
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebar-overlay').classList.remove('open');
}
// Close on overlay click
document.getElementById('sidebar-overlay').addEventListener('click', closeSidebar);
</script>
Advanced Grid System
Extended grid system with more layout possibilities.
Grid with different columns
Column spans
<!-- Basic grids -->
<div class="grid grid-2 gap-3">
<div>Element 1</div>
<div>Element 2</div>
</div>
<div class="grid grid-3 gap-4">
<div>Element 1</div>
<div>Element 2</div>
<div>Element 3</div>
</div>
<!-- Grid with column spans -->
<div class="grid grid-6 gap-3">
<div class="col-span-2">Takes 2 columns</div>
<div class="col-span-3">Takes 3 columns</div>
<div>1 column</div>
<div class="col-span-full">Full width</div>
</div>
<!-- 12-column grid -->
<div class="grid grid-12 gap-2">
<div class="col-span-4">4/12</div>
<div class="col-span-8">8/12</div>
<div class="col-span-6">6/12</div>
<div class="col-span-6">6/12</div>
</div>
<!-- Positioning -->
<div class="grid grid-4 gap-2">
<div class="col-start-2 col-span-2">
Starts at 2nd column, takes 2
</div>
</div>
<!-- Responsive - automatically adjusts on smaller screens -->
Theme & Dark Mode
Toggle data-theme="dark" on <html>. Persistence via localStorage recommended.
Theme Setup
JS + CSS snippet for toggle & persistence.
- HTML attribute:
<html data-theme="light"> - JS toggles attribute and stores preference
- Override tokens before or after import (with specificity)
<html lang="en" data-theme="light">
...
<button id="themeToggle" class="btn btn-sm">Toggle theme</button>
/* CSS overrides */
:root {
--paper-accent: #346cab;
--paper-accent-hover: #3e79bd;
}
[data-theme="dark"] {
--paper-bg: #1a1a1a;
--paper-ink: #e8e4d9;
}
// JS persistence
const root = document.documentElement;
const saved = localStorage.getItem('theme');
if (saved) root.setAttribute('data-theme', saved);
document.getElementById('themeToggle').addEventListener('click', () => {
const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
root.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
PaperToast.show(`Theme: ${next}`, { title: 'Theme' });
});
Override variables
:root { --paper-accent: #346cab; }