@papernote/ui
Version:
A modern React component library with a paper notebook aesthetic - minimal, professional, and expressive
589 lines (487 loc) • 13.1 kB
CSS
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
/* Component styles */
@import '../components/Spreadsheet.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
font-family: 'Inter', sans-serif;
@apply bg-paper-100 text-ink-600; /* Desk-like background for page contrast */
}
h1, h2, h3, h4, h5, h6 {
@apply text-ink-900 font-medium;
}
h1 { @apply text-3xl tracking-tight; }
h2 { @apply text-2xl tracking-tight; }
h3 { @apply text-xl; }
h4 { @apply text-lg; }
}
@layer components {
/* Button Styles - Minimal & Professional */
.btn {
@apply inline-flex items-center justify-center px-4 py-2.5 text-sm font-medium rounded-lg border transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-accent-400;
}
.btn-primary {
@apply bg-accent-500 text-white border-accent-500 hover:bg-accent-600 hover:shadow-sm active:scale-[0.98];
}
.btn-secondary {
@apply bg-white text-ink-700 border-paper-300 hover:bg-paper-50 hover:border-paper-400 shadow-xs hover:shadow-sm;
}
.btn-ghost {
@apply bg-transparent text-ink-600 border-transparent hover:text-ink-800 hover:bg-paper-100 active:bg-paper-200;
}
.btn:disabled {
@apply opacity-40 cursor-not-allowed;
}
/* Input Styles - Comfortable & Clear */
.input {
@apply block w-full px-4 py-2.5 border border-paper-300 rounded-lg text-sm text-ink-800 placeholder-ink-400 bg-white transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400 hover:border-paper-400;
}
.label {
@apply flex items-center text-sm font-medium text-ink-700 mb-2;
}
/* Card Styles - Paper-like */
.card {
@apply bg-white bg-subtle-grain rounded-xl shadow-card border border-paper-200 p-8 transition-shadow duration-200 hover:shadow-md;
}
.card-compact {
@apply bg-white bg-subtle-grain rounded-lg shadow-card border border-paper-200 p-5;
}
/* Badge Styles - Muted & Professional */
.badge {
@apply inline-flex items-center px-3 py-1 rounded-full text-xs font-medium;
}
.badge-success {
@apply bg-success-50 text-success-700 border border-success-200;
}
.badge-warning {
@apply bg-warning-50 text-warning-700 border border-warning-200;
}
.badge-error {
@apply bg-error-50 text-error-700 border border-error-200;
}
.badge-info {
@apply bg-primary-50 text-primary-700 border border-primary-200;
}
/* Table Styles - Clean & Minimal */
.table {
@apply min-w-full divide-y divide-paper-200;
}
.table-header {
@apply bg-paper-50;
}
.table-header-cell {
@apply px-6 py-3 text-left text-xs font-medium text-ink-500 uppercase tracking-wider;
}
.table-cell {
@apply px-6 py-4 whitespace-nowrap text-sm text-ink-700;
}
.table-row {
@apply hover:bg-paper-50 transition-colors duration-150;
}
/* Navigation Styles - Notebook-like */
.nav-link {
@apply flex items-center px-3 py-2.5 text-sm font-medium rounded-lg transition-all duration-200;
}
.nav-link-active {
@apply bg-accent-50 text-accent-900 border-l-2 border-accent-500 pl-2.5;
}
.nav-link-inactive {
@apply text-ink-600 hover:bg-paper-100 hover:text-ink-900;
}
/* Switch Toggle Styles - Refined */
.switch {
@apply relative inline-block w-11 h-6;
}
.switch input {
@apply opacity-0 w-0 h-0;
}
.slider {
@apply absolute cursor-pointer top-0 left-0 right-0 bottom-0 bg-paper-300 rounded-full transition-all duration-200;
}
.slider:before {
@apply absolute content-[''] h-5 w-5 left-0.5 bottom-0.5 bg-white rounded-full shadow-sm transition-all duration-200;
}
input:checked + .slider {
@apply bg-accent-500;
}
input:checked + .slider:before {
@apply transform translate-x-5;
}
/* Stat Card Styles - Paper-like */
.stat-card {
@apply bg-white bg-subtle-grain rounded-xl border border-paper-200 p-6 hover:shadow-md hover:border-paper-300 transition-all duration-200;
}
.stat-card-icon {
@apply h-10 w-10 mb-4 text-ink-600;
}
.stat-card-value {
@apply text-3xl font-semibold text-ink-900 tracking-tight;
}
.stat-card-label {
@apply text-sm text-ink-500 mt-2;
}
.stat-card-change {
@apply text-sm font-medium mt-3;
}
.stat-card-change-positive {
@apply text-success-600;
}
.stat-card-change-negative {
@apply text-error-600;
}
/* Notebook Page Container - Creates bounded page effect
NOTE: This class is deprecated - Page component now handles responsive layout via props
Keeping for backward compatibility only */
.notebook-page {
@apply bg-white bg-subtle-grain rounded-sm shadow-lg border-l-4 border-paper-300;
max-width: 1400px;
/* Responsive margins - fixed left/top, responsive right/bottom */
margin-top: 1rem;
margin-left: 1rem;
margin-right: 1rem;
margin-bottom: 1rem;
/* Responsive padding - fixed left/top, responsive right/bottom */
padding-top: 3rem;
padding-left: 5rem; /* Extra left padding for binding margin */
padding-right: 1rem;
padding-bottom: 1rem;
min-height: calc(100vh - 2rem);
position: relative;
}
/* Responsive padding/margin increases on larger screens */
@media (min-width: 640px) {
.notebook-page {
margin-right: 1.5rem;
margin-bottom: 2rem;
padding-right: 2rem;
padding-bottom: 2rem;
}
}
@media (min-width: 768px) {
.notebook-page {
margin-right: 2rem;
margin-bottom: 2rem;
padding-right: 3rem;
padding-bottom: 3rem;
}
}
@media (min-width: 1024px) {
.notebook-page {
margin-right: auto; /* Center on large screens */
padding-right: 4rem;
padding-bottom: 4rem;
}
}
/* Notebook binding effect on sidebar */
.notebook-binding {
position: relative;
}
.notebook-binding::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 3px;
background: linear-gradient(to bottom,
transparent 0%,
rgba(0,0,0,0.03) 10%,
rgba(0,0,0,0.05) 50%,
rgba(0,0,0,0.03) 90%,
transparent 100%
);
box-shadow: 2px 0 4px rgba(0,0,0,0.04);
}
/* Ruled lines for notebook page (very subtle) */
.notebook-ruled {
background-image:
linear-gradient(transparent 0%, transparent calc(100% - 1px), rgba(231, 229, 228, 0.3) 100%);
background-size: 100% 2rem;
background-position: 0 1.5rem;
}
/* Left margin line for notebook page */
.notebook-margin {
position: relative;
}
.notebook-margin::before {
content: '';
position: absolute;
left: 3rem;
top: 0;
bottom: 0;
width: 1px;
background-color: rgba(239, 68, 68, 0.05); /* Very subtle red margin line */
}
/* Page Navigation / TOC - Sits in gutter between sidebar and page */
.page-nav {
@apply fixed top-32 bottom-8;
left: 16.5rem; /* Sidebar (16rem) + gutter offset */
width: 2rem;
z-index: 5;
pointer-events: none;
}
.page-nav-inner {
@apply sticky top-32 flex flex-col gap-3;
pointer-events: auto;
}
/* Tab-style navigation items */
.page-nav-item {
@apply block w-full h-8 rounded-r-md border-r-2 border-transparent;
@apply text-xs text-ink-400 hover:text-ink-700 hover:border-accent-400 hover:bg-paper-50;
@apply transition-all duration-200;
writing-mode: vertical-rl;
text-orientation: mixed;
padding: 0.5rem 0.25rem;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-height: 8rem;
}
.page-nav-item.active {
@apply border-accent-500 bg-accent-50 text-accent-900 font-medium;
}
/* Alternative: Dot navigation style (simpler) */
.page-nav-dots {
@apply flex flex-col gap-2 items-center pointer-events-auto;
}
.page-nav-dot {
@apply w-2 h-2 rounded-full bg-paper-300 transition-all duration-200 cursor-pointer pointer-events-auto;
}
.page-nav-dot:hover {
@apply bg-accent-400;
transform: scale(1.2);
}
.page-nav-dot.active {
@apply bg-accent-500 w-2.5 h-2.5;
}
}
@layer utilities {
.line-clamp-1 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
.line-clamp-2 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.line-clamp-3 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.text-shadow {
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.bg-gradient-primary {
background: linear-gradient(135deg, #64748b, #475569);
}
.bg-gradient-success {
background: linear-gradient(135deg, #10b981, #059669);
}
.bg-gradient-warning {
background: linear-gradient(135deg, #f59e0b, #d97706);
}
.bg-gradient-accent {
background: linear-gradient(135deg, #f59e0b, #d97706);
}
}
/* Custom scrollbar - Subtle paper aesthetic */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #fafaf9;
}
::-webkit-scrollbar-thumb {
background: #d6d3d1;
border-radius: 4px;
border: 2px solid #fafaf9;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a29e;
}
/* Loading animations */
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: .5;
}
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Table styles - Clean & minimal */
.table {
@apply min-w-full divide-y divide-paper-200;
}
.table-header {
@apply bg-paper-50;
}
.table-header-cell {
@apply px-6 py-3 text-left text-xs font-medium text-ink-500 uppercase tracking-wider;
}
.table-row {
@apply bg-white hover:bg-paper-50 transition-colors;
}
.table-cell {
@apply px-6 py-4 whitespace-nowrap text-sm text-ink-700;
}
/* Prevent table layout shifts */
.table-fixed {
table-layout: fixed ;
}
.table-stable {
@apply table-fixed;
/* Removed min-height to allow content-based sizing for two-row layout */
}
.table-stable thead {
height: auto ;
}
.table-stable thead th {
position: sticky;
top: 0;
z-index: 10;
background-color: #fafaf9;
backdrop-filter: blur(8px);
padding: 4px 8px ;
height: 28px ;
min-height: 28px ;
max-height: 28px ;
line-height: 1 ;
vertical-align: middle ;
overflow: hidden ;
box-sizing: border-box ;
font-size: 0.75rem ;
border-bottom: 1px solid #e7e5e4;
}
/* Primary rows with normal content */
.table-stable tbody tr {
height: auto; /* Allow content-based sizing for two-row layout */
}
/* Secondary rows should be compact */
.table-stable tbody tr.secondary-row {
height: auto;
}
.table-stable tbody tr td {
vertical-align: top;
}
/* Smooth loading overlay */
.table-loading-overlay {
transition: opacity 0.3s ease-in-out;
backdrop-filter: blur(1px);
}
/* Prevent content jumping during loading */
.loading-stable {
min-height: inherit;
visibility: visible;
}
.content-stable {
transition: opacity 0.2s ease-in-out;
}
/* Custom tooltip for navigation dots using CSS */
.page-nav-dot {
position: relative;
}
.page-nav-dot::after {
content: attr(aria-label);
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
margin-left: 0.75rem;
padding: 0.375rem 0.75rem;
background-color: #1c1917;
color: white;
font-size: 0.75rem;
font-weight: 500;
border-radius: 0.375rem;
opacity: 0;
transition: opacity 200ms;
pointer-events: none;
white-space: nowrap;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
z-index: 50;
}
.page-nav-dot:hover::after {
opacity: 1;
}
/* DataTable Expanded Row Animation */
@keyframes expandRow {
from {
opacity: 0;
max-height: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
max-height: 1000px;
transform: translateY(0);
}
}
.animate-expand {
animation: expandRow 0.3s ease-out forwards;
overflow: hidden;
}
/* Drawer Slide Animations */
@keyframes slideInLeft {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideInRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideInTop {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
@keyframes slideInBottom {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.animate-slide-in-left {
animation: slideInLeft 0.3s ease-out;
}
.animate-slide-in-right {
animation: slideInRight 0.3s ease-out;
}
.animate-slide-in-top {
animation: slideInTop 0.3s ease-out;
}
.animate-slide-in-bottom {
animation: slideInBottom 0.3s ease-out;
}