Refactor UI components and styles for improved consistency and aesthetics
This commit is contained in:
@@ -23,20 +23,18 @@ export function CreateButton({ onClick, disabled, icon, children }: CreateButton
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className="
|
||||
bg-pixel-purple-bright
|
||||
hover:bg-pixel-purple-deep
|
||||
bg-brand
|
||||
hover:bg-brand-dark
|
||||
text-white
|
||||
border-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel
|
||||
hover:shadow-pixel-hover
|
||||
border
|
||||
border-border
|
||||
shadow-card
|
||||
hover:shadow-float
|
||||
hover:-translate-y-0.5
|
||||
hover:-translate-x-0.5
|
||||
active:translate-y-0.5
|
||||
active:translate-x-0.5
|
||||
active:shadow-pixel-active
|
||||
active:shadow-soft
|
||||
transition-all
|
||||
duration-75
|
||||
duration-150
|
||||
font-sans
|
||||
font-semibold
|
||||
px-6
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import type { DocumentType } from '@/api/document';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { FileText, KanbanSquare, Trash2 } from 'lucide-react';
|
||||
import PixelIcon from '@/components/PixelIcon/PixelIcon';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
interface DocumentCardProps {
|
||||
@@ -24,15 +25,14 @@ export function DocumentCard({ doc, onDelete, isShared }: DocumentCardProps) {
|
||||
<Card className="
|
||||
group
|
||||
relative
|
||||
bg-pixel-white
|
||||
border-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-md
|
||||
hover:shadow-pixel-lg
|
||||
bg-surface
|
||||
border
|
||||
border-border
|
||||
shadow-card
|
||||
hover:shadow-float
|
||||
hover:-translate-y-0.5
|
||||
hover:-translate-x-0.5
|
||||
transition-all
|
||||
duration-100
|
||||
duration-150
|
||||
p-6
|
||||
flex
|
||||
flex-col
|
||||
@@ -42,42 +42,32 @@ export function DocumentCard({ doc, onDelete, isShared }: DocumentCardProps) {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="
|
||||
bg-pixel-cyan-bright
|
||||
p-2
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
bg-brand-teal
|
||||
p-2.5
|
||||
rounded-md
|
||||
shadow-soft
|
||||
">
|
||||
<Icon className="w-6 h-6 text-white" />
|
||||
<Icon className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-pixel text-sm text-pixel-text-primary mb-1">
|
||||
<h3 className="font-display text-base text-text-primary mb-1">
|
||||
{doc.name}
|
||||
</h3>
|
||||
<span className="font-sans text-xs text-pixel-text-muted">
|
||||
<span className="font-sans text-xs text-text-muted">
|
||||
{typeLabel}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{isShared && (
|
||||
<span className="
|
||||
bg-pixel-pink-vibrant
|
||||
text-white
|
||||
font-sans
|
||||
text-xs
|
||||
font-semibold
|
||||
px-2
|
||||
py-1
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
">
|
||||
<span className="shared-badge">
|
||||
<PixelIcon name="gem" size={12} color="hsl(var(--brand-teal))" />
|
||||
Shared
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="font-sans text-xs text-pixel-text-muted">
|
||||
<div className="font-sans text-xs text-text-muted">
|
||||
Created {new Date(doc.created_at).toLocaleDateString()}
|
||||
</div>
|
||||
|
||||
@@ -87,17 +77,16 @@ export function DocumentCard({ doc, onDelete, isShared }: DocumentCardProps) {
|
||||
onClick={handleOpen}
|
||||
className="
|
||||
flex-1
|
||||
bg-pixel-cyan-bright
|
||||
hover:bg-pixel-purple-bright
|
||||
bg-brand
|
||||
hover:bg-brand-dark
|
||||
text-white
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
hover:shadow-pixel-hover
|
||||
border
|
||||
border-border
|
||||
shadow-soft
|
||||
hover:shadow-card
|
||||
hover:-translate-y-0.5
|
||||
hover:-translate-x-0.5
|
||||
transition-all
|
||||
duration-75
|
||||
duration-150
|
||||
font-sans
|
||||
font-semibold
|
||||
"
|
||||
@@ -109,15 +98,14 @@ export function DocumentCard({ doc, onDelete, isShared }: DocumentCardProps) {
|
||||
onClick={() => onDelete(doc.id)}
|
||||
variant="outline"
|
||||
className="
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
hover:shadow-pixel-hover
|
||||
border
|
||||
border-border
|
||||
shadow-soft
|
||||
hover:shadow-card
|
||||
hover:-translate-y-0.5
|
||||
hover:-translate-x-0.5
|
||||
hover:bg-red-50
|
||||
transition-all
|
||||
duration-75
|
||||
duration-150
|
||||
"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import PixelIcon from '@/components/PixelIcon/PixelIcon';
|
||||
import { LogOut } from 'lucide-react';
|
||||
|
||||
function Navbar() {
|
||||
@@ -9,28 +10,33 @@ function Navbar() {
|
||||
|
||||
return (
|
||||
<nav className="
|
||||
bg-pixel-white
|
||||
border-b-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
bg-surface
|
||||
border-b
|
||||
border-border
|
||||
shadow-soft
|
||||
sticky
|
||||
top-0
|
||||
z-50
|
||||
backdrop-blur
|
||||
">
|
||||
<div className="max-w-7xl mx-auto px-8 py-4 flex items-center justify-between">
|
||||
{/* Brand */}
|
||||
<a
|
||||
href="/"
|
||||
className="
|
||||
font-pixel
|
||||
text-xl
|
||||
text-pixel-purple-bright
|
||||
hover:text-pixel-cyan-bright
|
||||
font-display
|
||||
text-lg
|
||||
text-text-primary
|
||||
hover:text-brand
|
||||
transition-colors
|
||||
duration-200
|
||||
flex
|
||||
items-center
|
||||
gap-2
|
||||
"
|
||||
>
|
||||
Realtime Collab
|
||||
<PixelIcon name="gem" size={18} color="hsl(var(--brand-teal))" />
|
||||
DocNest
|
||||
</a>
|
||||
|
||||
{/* User Section */}
|
||||
@@ -40,15 +46,17 @@ function Navbar() {
|
||||
src={user.avatar_url}
|
||||
alt={user.name}
|
||||
className="
|
||||
w-10
|
||||
h-10
|
||||
border-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
w-9
|
||||
h-9
|
||||
rounded-full
|
||||
border
|
||||
border-border
|
||||
shadow-soft
|
||||
object-cover
|
||||
"
|
||||
/>
|
||||
)}
|
||||
<span className="font-sans font-medium text-pixel-text-primary hidden sm:inline">
|
||||
<span className="font-sans font-medium text-text-primary hidden sm:inline">
|
||||
{user.name}
|
||||
</span>
|
||||
<Button
|
||||
@@ -56,14 +64,13 @@ function Navbar() {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="
|
||||
border-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
hover:shadow-pixel-hover
|
||||
border
|
||||
border-border
|
||||
shadow-soft
|
||||
hover:shadow-card
|
||||
hover:-translate-y-0.5
|
||||
hover:-translate-x-0.5
|
||||
transition-all
|
||||
duration-75
|
||||
duration-150
|
||||
font-sans
|
||||
"
|
||||
>
|
||||
|
||||
@@ -43,45 +43,37 @@ const UserList = ({ awareness }: UserListProps) => {
|
||||
}, [awareness]);
|
||||
|
||||
return (
|
||||
<div className="
|
||||
bg-pixel-white
|
||||
border-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-md
|
||||
p-4
|
||||
">
|
||||
<div className="p-2">
|
||||
{/* Header with online count */}
|
||||
<div className="
|
||||
flex
|
||||
items-center
|
||||
gap-2
|
||||
mb-4
|
||||
pb-3
|
||||
border-b-[2px]
|
||||
border-pixel-outline
|
||||
mb-3
|
||||
pb-2
|
||||
border-b
|
||||
border-border
|
||||
">
|
||||
<div className="
|
||||
w-3
|
||||
h-3
|
||||
bg-pixel-green-lime
|
||||
w-2.5
|
||||
h-2.5
|
||||
bg-brand-teal
|
||||
animate-pulse
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
rounded-full
|
||||
" />
|
||||
<h4 className="font-pixel text-xs text-pixel-text-primary">
|
||||
ONLINE
|
||||
<h4 className="font-display text-xs text-text-secondary uppercase tracking-wide">
|
||||
Online
|
||||
</h4>
|
||||
<span className="
|
||||
ml-auto
|
||||
font-sans
|
||||
text-sm
|
||||
font-bold
|
||||
text-pixel-purple-bright
|
||||
bg-pixel-panel
|
||||
font-semibold
|
||||
text-text-primary
|
||||
bg-surface-muted
|
||||
px-2
|
||||
py-1
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
py-0.5
|
||||
rounded-full
|
||||
">
|
||||
{users.length}
|
||||
</span>
|
||||
@@ -95,7 +87,7 @@ const UserList = ({ awareness }: UserListProps) => {
|
||||
py-4
|
||||
font-sans
|
||||
text-xs
|
||||
text-pixel-text-muted
|
||||
text-text-muted
|
||||
">
|
||||
No users online
|
||||
</div>
|
||||
@@ -108,16 +100,7 @@ const UserList = ({ awareness }: UserListProps) => {
|
||||
flex
|
||||
items-center
|
||||
gap-3
|
||||
p-2
|
||||
bg-pixel-panel
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
hover:shadow-pixel-hover
|
||||
hover:-translate-y-0.5
|
||||
hover:-translate-x-0.5
|
||||
transition-all
|
||||
duration-75
|
||||
py-2
|
||||
"
|
||||
>
|
||||
{/* Avatar with online indicator */}
|
||||
@@ -130,13 +113,14 @@ const UserList = ({ awareness }: UserListProps) => {
|
||||
className="
|
||||
w-10
|
||||
h-10
|
||||
border-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
rounded-full
|
||||
border
|
||||
border-border
|
||||
shadow-soft
|
||||
object-cover
|
||||
"
|
||||
onError={(e) => {
|
||||
// Fallback to colored square on image error
|
||||
// Fallback to colored circle on image error
|
||||
e.currentTarget.style.display = 'none';
|
||||
const fallback = e.currentTarget.nextElementSibling as HTMLElement;
|
||||
if (fallback) {
|
||||
@@ -146,16 +130,15 @@ const UserList = ({ awareness }: UserListProps) => {
|
||||
/>
|
||||
{/* Fallback colored square (hidden if avatar loads) */}
|
||||
<div
|
||||
className="w-10 h-10 border-[3px] border-pixel-outline shadow-pixel-sm items-center justify-center font-pixel text-xs text-white"
|
||||
style={{ backgroundColor: user.color, display: 'none' }}
|
||||
className="w-10 h-10 border border-border shadow-soft items-center justify-center font-display text-xs text-text-secondary rounded-full bg-surface-muted"
|
||||
style={{ display: 'none' }}
|
||||
>
|
||||
{user.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
className="w-10 h-10 border-[3px] border-pixel-outline shadow-pixel-sm flex items-center justify-center font-pixel text-xs text-white"
|
||||
style={{ backgroundColor: user.color }}
|
||||
className="w-10 h-10 border border-border shadow-soft flex items-center justify-center font-display text-xs text-text-secondary rounded-full bg-surface-muted"
|
||||
>
|
||||
{user.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
@@ -168,10 +151,11 @@ const UserList = ({ awareness }: UserListProps) => {
|
||||
-right-0.5
|
||||
w-3
|
||||
h-3
|
||||
bg-pixel-green-lime
|
||||
border-[2px]
|
||||
border-pixel-white
|
||||
shadow-pixel-sm
|
||||
bg-brand-teal
|
||||
border-2
|
||||
border-white
|
||||
shadow-soft
|
||||
rounded-full
|
||||
" />
|
||||
</div>
|
||||
|
||||
@@ -180,25 +164,12 @@ const UserList = ({ awareness }: UserListProps) => {
|
||||
font-sans
|
||||
text-sm
|
||||
font-medium
|
||||
text-pixel-text-primary
|
||||
text-text-primary
|
||||
truncate
|
||||
flex-1
|
||||
">
|
||||
{user.name}
|
||||
</span>
|
||||
|
||||
{/* User color indicator (small square) */}
|
||||
<div
|
||||
className="
|
||||
flex-shrink-0
|
||||
w-4
|
||||
h-4
|
||||
border-[2px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
"
|
||||
style={{ backgroundColor: user.color }}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
background: rgba(15, 23, 42, 0.45);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.2s ease;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
@@ -18,11 +19,12 @@
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
background: hsl(var(--surface));
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-lg);
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
max-width: 640px;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
@@ -46,20 +48,54 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
color: #1a202c;
|
||||
font-size: 18px;
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.role-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--radius-md);
|
||||
margin: 16px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.role-banner.owner {
|
||||
background: #ecfdf5;
|
||||
color: #166534;
|
||||
border: 1px solid #bbf7d0;
|
||||
}
|
||||
|
||||
.role-banner.editor {
|
||||
background: #fff7ed;
|
||||
color: #9a3412;
|
||||
border: 1px solid #fed7aa;
|
||||
}
|
||||
|
||||
.role-banner.viewer {
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
border: 1px solid #bfdbfe;
|
||||
}
|
||||
|
||||
.role-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 28px;
|
||||
color: #718096;
|
||||
font-size: 24px;
|
||||
color: hsl(var(--text-muted));
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 32px;
|
||||
@@ -67,40 +103,43 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
background: #f7fafc;
|
||||
color: #2d3748;
|
||||
background: hsl(var(--surface-muted));
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
padding: 0 24px;
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 12px 20px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #718096;
|
||||
padding: 8px 14px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--text-muted));
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
color: #2d3748;
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: #667eea;
|
||||
border-bottom-color: #667eea;
|
||||
color: white;
|
||||
background: hsl(var(--primary));
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
@@ -110,72 +149,78 @@
|
||||
|
||||
.message {
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.message.error {
|
||||
background: #fff5f5;
|
||||
color: #c53030;
|
||||
border: 1px solid #feb2b2;
|
||||
background: #fff1f2;
|
||||
color: #b91c1c;
|
||||
border: 1px solid #fecdd3;
|
||||
}
|
||||
|
||||
.message.success {
|
||||
background: #f0fff4;
|
||||
color: #22543d;
|
||||
border: 1px solid #9ae6b4;
|
||||
background: #ecfdf5;
|
||||
color: #166534;
|
||||
border: 1px solid #bbf7d0;
|
||||
}
|
||||
|
||||
.share-form {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.share-input {
|
||||
flex: 1;
|
||||
min-width: 220px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.share-input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
border-color: hsl(var(--primary));
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.share-select {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
background: white;
|
||||
background: hsl(var(--surface));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.share-select:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
border-color: hsl(var(--primary));
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.share-button {
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
padding: 10px 18px;
|
||||
background: hsl(var(--primary));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.share-button:hover:not(:disabled) {
|
||||
background: #5568d3;
|
||||
background: hsl(var(--primary) / 0.9);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.share-button:disabled {
|
||||
@@ -186,15 +231,19 @@
|
||||
.shares-list h3 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
color: hsl(var(--text-primary));
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 32px;
|
||||
padding: 28px;
|
||||
text-align: center;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-muted));
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.share-item {
|
||||
@@ -202,9 +251,10 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: 8px;
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.share-user {
|
||||
@@ -214,10 +264,11 @@
|
||||
}
|
||||
|
||||
.share-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.share-info {
|
||||
@@ -228,13 +279,13 @@
|
||||
|
||||
.share-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #2d3748;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.share-email {
|
||||
font-size: 13px;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
.share-actions {
|
||||
@@ -244,28 +295,28 @@
|
||||
}
|
||||
|
||||
.permission-badge {
|
||||
padding: 4px 12px;
|
||||
background: #edf2f7;
|
||||
color: #4a5568;
|
||||
border-radius: 12px;
|
||||
padding: 4px 10px;
|
||||
background: hsl(var(--surface-muted));
|
||||
color: hsl(var(--text-secondary));
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.remove-button {
|
||||
padding: 6px 12px;
|
||||
background: white;
|
||||
color: #e53e3e;
|
||||
border: 1px solid #feb2b2;
|
||||
border-radius: 6px;
|
||||
color: #b91c1c;
|
||||
border: 1px solid #fecdd3;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.remove-button:hover:not(:disabled) {
|
||||
background: #fff5f5;
|
||||
background: #fff1f2;
|
||||
}
|
||||
|
||||
.remove-button:disabled {
|
||||
@@ -278,7 +329,7 @@
|
||||
}
|
||||
|
||||
.link-creation p {
|
||||
color: #4a5568;
|
||||
color: hsl(var(--text-secondary));
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -287,6 +338,7 @@
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.link-display {
|
||||
@@ -296,40 +348,42 @@
|
||||
}
|
||||
|
||||
.link-display > p {
|
||||
color: #4a5568;
|
||||
color: hsl(var(--text-secondary));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.link-box {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.link-input {
|
||||
flex: 1;
|
||||
min-width: 240px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
background: #f7fafc;
|
||||
color: #2d3748;
|
||||
background: hsl(var(--surface-muted));
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
padding: 10px 20px;
|
||||
background: #48bb78;
|
||||
padding: 10px 18px;
|
||||
background: hsl(var(--secondary));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.copy-button:hover {
|
||||
background: #38a169;
|
||||
background: hsl(var(--secondary) / 0.9);
|
||||
}
|
||||
|
||||
.link-meta {
|
||||
@@ -340,23 +394,23 @@
|
||||
|
||||
.link-date {
|
||||
font-size: 13px;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
.revoke-button {
|
||||
padding: 10px 20px;
|
||||
background: white;
|
||||
color: #e53e3e;
|
||||
border: 1px solid #feb2b2;
|
||||
border-radius: 6px;
|
||||
color: #b91c1c;
|
||||
border: 1px solid #fecdd3;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.revoke-button:hover:not(:disabled) {
|
||||
background: #fff5f5;
|
||||
background: #fff1f2;
|
||||
}
|
||||
|
||||
.revoke-button:disabled {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { shareApi } from '../../api/share';
|
||||
import type { DocumentShareWithUser, ShareLink } from '../../types/share';
|
||||
import PixelIcon from '../PixelIcon/PixelIcon';
|
||||
import './ShareModal.css';
|
||||
|
||||
interface ShareModalProps {
|
||||
@@ -152,20 +153,8 @@ function ShareModal({ documentId, onClose, currentPermission, currentRole }: Sha
|
||||
</div>
|
||||
|
||||
{currentRole && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
padding: '12px 16px',
|
||||
backgroundColor: currentRole === 'owner' ? '#e8f5e9' : currentPermission === 'edit' ? '#fff3e0' : '#e3f2fd',
|
||||
borderRadius: '8px',
|
||||
margin: '16px 0',
|
||||
fontSize: '14px',
|
||||
fontWeight: '500',
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '18px' }}>
|
||||
<div className={`role-banner ${currentRole}`}>
|
||||
<span className="role-icon">
|
||||
{currentRole === 'owner' ? '👑' : currentPermission === 'edit' ? '✏️' : '👁️'}
|
||||
</span>
|
||||
<span>
|
||||
@@ -222,7 +211,10 @@ function ShareModal({ documentId, onClose, currentPermission, currentRole }: Sha
|
||||
<div className="shares-list">
|
||||
<h3>People with access</h3>
|
||||
{shares.length === 0 ? (
|
||||
<p className="empty-state">No users have been given access yet.</p>
|
||||
<div className="empty-state">
|
||||
<PixelIcon name="gem" size={18} color="hsl(var(--brand-teal))" />
|
||||
<p>No users have been given access yet.</p>
|
||||
</div>
|
||||
) : (
|
||||
shares.map((share) => (
|
||||
<div key={share.id} className="share-item">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
background: rgba(15, 23, 42, 0.35);
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
@@ -27,35 +27,42 @@
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 400px;
|
||||
width: 420px;
|
||||
height: 100vh;
|
||||
background: white;
|
||||
box-shadow: -4px 0 20px rgba(0, 0, 0, 0.15);
|
||||
background: hsl(var(--surface));
|
||||
border-left: 1px solid hsl(var(--border));
|
||||
box-shadow: -10px 0 30px rgba(15, 23, 42, 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
animation: slideIn 0.2s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.version-panel {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.version-panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.version-panel-header h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1a202c;
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 28px;
|
||||
color: #718096;
|
||||
font-size: 24px;
|
||||
color: hsl(var(--text-muted));
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 32px;
|
||||
@@ -63,35 +70,37 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
background: #f7fafc;
|
||||
color: #2d3748;
|
||||
background: hsl(var(--surface-muted));
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.version-panel-actions {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.create-version-btn {
|
||||
width: 100%;
|
||||
padding: 10px 16px;
|
||||
background: #667eea;
|
||||
background: hsl(var(--primary));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.create-version-btn:hover {
|
||||
background: #5568d3;
|
||||
background: hsl(var(--primary) / 0.9);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.version-panel-content {
|
||||
@@ -104,7 +113,14 @@
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
@@ -114,20 +130,20 @@
|
||||
|
||||
.empty-state small {
|
||||
font-size: 13px;
|
||||
color: #a0aec0;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.message.error {
|
||||
background: #fff5f5;
|
||||
color: #c53030;
|
||||
border: 1px solid #feb2b2;
|
||||
background: #fff1f2;
|
||||
color: #b91c1c;
|
||||
border: 1px solid #fecdd3;
|
||||
}
|
||||
|
||||
.version-list {
|
||||
@@ -137,16 +153,17 @@
|
||||
}
|
||||
|
||||
.version-item {
|
||||
background: #f7fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: hsl(var(--surface));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.version-item:hover {
|
||||
border-color: #cbd5e0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
border-color: hsl(var(--primary) / 0.25);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.version-header {
|
||||
@@ -160,23 +177,23 @@
|
||||
.version-number {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #2d3748;
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.auto-badge {
|
||||
background: #e8f4fd;
|
||||
color: #3182ce;
|
||||
background: hsl(var(--surface-muted));
|
||||
color: hsl(var(--text-secondary));
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.version-label {
|
||||
color: #667eea;
|
||||
color: hsl(var(--primary));
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.version-meta {
|
||||
@@ -184,7 +201,7 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-muted));
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -199,21 +216,22 @@
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.version-time {
|
||||
color: #a0aec0;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
.version-preview {
|
||||
font-size: 13px;
|
||||
color: #4a5568;
|
||||
color: hsl(var(--text-secondary));
|
||||
line-height: 1.5;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #edf2f7;
|
||||
background: hsl(var(--surface-muted));
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid hsl(var(--border));
|
||||
max-height: 60px;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -221,18 +239,18 @@
|
||||
.restore-btn {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
background: white;
|
||||
color: #667eea;
|
||||
border: 1px solid #667eea;
|
||||
border-radius: 6px;
|
||||
background: hsl(var(--surface));
|
||||
color: hsl(var(--primary));
|
||||
border: 1px solid hsl(var(--primary));
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.restore-btn:hover:not(:disabled) {
|
||||
background: #667eea;
|
||||
background: hsl(var(--primary));
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -247,21 +265,21 @@
|
||||
justify-content: space-between;
|
||||
padding: 16px 0;
|
||||
margin-top: 16px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
padding: 8px 16px;
|
||||
background: #f7fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: hsl(var(--surface-muted));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.pagination button:hover:not(:disabled) {
|
||||
background: #edf2f7;
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.pagination button:disabled {
|
||||
@@ -271,7 +289,7 @@
|
||||
|
||||
.pagination span {
|
||||
font-size: 13px;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
/* Create Version Modal */
|
||||
@@ -281,7 +299,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -289,39 +307,40 @@
|
||||
}
|
||||
|
||||
.create-modal {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
background: hsl(var(--surface));
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 24px;
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
max-width: 420px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.create-modal h3 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 18px;
|
||||
color: #1a202c;
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
|
||||
.create-modal p {
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 14px;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-secondary));
|
||||
}
|
||||
|
||||
.create-modal label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #2d3748;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--text-primary));
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.create-modal input {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
box-sizing: border-box;
|
||||
@@ -329,8 +348,8 @@
|
||||
|
||||
.create-modal input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
border-color: hsl(var(--primary));
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
@@ -341,31 +360,31 @@
|
||||
|
||||
.modal-buttons button {
|
||||
padding: 10px 20px;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-buttons button:not(.primary) {
|
||||
background: #f7fafc;
|
||||
color: #4a5568;
|
||||
border: 1px solid #e2e8f0;
|
||||
background: hsl(var(--surface-muted));
|
||||
color: hsl(var(--text-secondary));
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.modal-buttons button:not(.primary):hover {
|
||||
background: #edf2f7;
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.modal-buttons button.primary {
|
||||
background: #667eea;
|
||||
background: hsl(var(--primary));
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-buttons button.primary:hover:not(:disabled) {
|
||||
background: #5568d3;
|
||||
background: hsl(var(--primary) / 0.9);
|
||||
}
|
||||
|
||||
.modal-buttons button:disabled {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { versionsApi, type DocumentVersion } from '../../api/document';
|
||||
import * as Y from 'yjs';
|
||||
import PixelIcon from '../PixelIcon/PixelIcon';
|
||||
import './VersionHistoryPanel.css';
|
||||
|
||||
interface VersionHistoryPanelProps {
|
||||
@@ -180,6 +181,7 @@ function VersionHistoryPanel({ documentId, ydoc, canEdit, onClose }: VersionHist
|
||||
<div className="loading-state">Loading versions...</div>
|
||||
) : versions.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<PixelIcon name="gem" size={18} color="hsl(var(--brand-teal))" />
|
||||
<p>No versions yet</p>
|
||||
<small>Versions are created when you save manually or automatically over time</small>
|
||||
</div>
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Manrope:wght@500;600;700&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 232 217 243;
|
||||
--foreground: 43 27 56;
|
||||
--background: 220 33% 98%;
|
||||
--foreground: 222 47% 11%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 43 27 56;
|
||||
--card-foreground: 222 47% 11%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 43 27 56;
|
||||
--primary: 277 42% 52%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
--primary: 214 89% 52%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 190 100% 50%;
|
||||
--secondary-foreground: 43 27 56;
|
||||
--muted: 276 100% 97%;
|
||||
--muted-foreground: 240 13% 40%;
|
||||
--accent: 325 100% 71%;
|
||||
--secondary: 173 80% 40%;
|
||||
--secondary-foreground: 0 0% 100%;
|
||||
--muted: 220 16% 96%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
--accent: 188 92% 42%;
|
||||
--accent-foreground: 0 0% 100%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--border: 43 27 56;
|
||||
--input: 43 27 56;
|
||||
--ring: 190 100% 50%;
|
||||
--radius: 0rem;
|
||||
--border: 215 20% 90%;
|
||||
--input: 215 20% 90%;
|
||||
--ring: 214 89% 52%;
|
||||
--radius: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,41 +36,62 @@
|
||||
}
|
||||
|
||||
:root {
|
||||
/* Vibrant Fantasy Color Palette */
|
||||
--pixel-purple-deep: #4A1B6F;
|
||||
--pixel-purple-bright: #8B4FB9;
|
||||
--pixel-pink-vibrant: #FF6EC7;
|
||||
--pixel-cyan-bright: #00D9FF;
|
||||
--pixel-orange-warm: #FF8E3C;
|
||||
--pixel-yellow-gold: #FFD23F;
|
||||
--pixel-green-lime: #8EF048;
|
||||
--pixel-green-forest: #3FA54D;
|
||||
/* Modern SaaS Design Tokens */
|
||||
--surface: 0 0% 100%;
|
||||
--surface-muted: 220 16% 96%;
|
||||
--text-primary: 222 47% 11%;
|
||||
--text-secondary: 215 25% 27%;
|
||||
--text-muted: 215 16% 47%;
|
||||
--brand: 214 89% 52%;
|
||||
--brand-dark: 221 83% 45%;
|
||||
--brand-teal: 173 80% 40%;
|
||||
--brand-teal-dark: 173 80% 32%;
|
||||
|
||||
/* UI Backgrounds & Neutrals */
|
||||
--pixel-bg-dark: #2B1B38;
|
||||
--pixel-bg-medium: #4A3B5C;
|
||||
--pixel-bg-light: #E8D9F3;
|
||||
--pixel-panel: #F5F0FF;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08);
|
||||
--shadow-md: 0 12px 30px rgba(15, 23, 42, 0.10);
|
||||
--shadow-lg: 0 20px 50px rgba(15, 23, 42, 0.14);
|
||||
--focus-ring: 0 0 0 3px rgba(37, 99, 235, 0.25);
|
||||
--gradient-hero: linear-gradient(120deg, #eef6ff 0%, #f5f7fb 55%, #ffffff 100%);
|
||||
--gradient-accent: linear-gradient(120deg, #2563eb 0%, #14b8a6 100%);
|
||||
|
||||
/* Compatibility aliases (legacy pixel tokens mapped to modern palette) */
|
||||
--pixel-purple-deep: #1E40AF;
|
||||
--pixel-purple-bright: #2563EB;
|
||||
--pixel-pink-vibrant: #38BDF8;
|
||||
--pixel-cyan-bright: #14B8A6;
|
||||
--pixel-orange-warm: #F59E0B;
|
||||
--pixel-yellow-gold: #FBBF24;
|
||||
--pixel-green-lime: #22C55E;
|
||||
--pixel-green-forest: #16A34A;
|
||||
|
||||
--pixel-bg-dark: #0F172A;
|
||||
--pixel-bg-medium: #1E293B;
|
||||
--pixel-bg-light: #F5F7FB;
|
||||
--pixel-panel: #FFFFFF;
|
||||
--pixel-white: #FFFFFF;
|
||||
|
||||
/* Shadows & Outlines */
|
||||
--pixel-shadow-dark: #1A0E28;
|
||||
--pixel-outline: #2B1B38;
|
||||
--pixel-shadow-dark: rgba(15, 23, 42, 0.2);
|
||||
--pixel-outline: #E2E8F0;
|
||||
|
||||
/* Text Colors */
|
||||
--pixel-text-primary: #2B1B38;
|
||||
--pixel-text-secondary: #4A3B5C;
|
||||
--pixel-text-muted: #8B7B9C;
|
||||
--pixel-text-primary: #0F172A;
|
||||
--pixel-text-secondary: #334155;
|
||||
--pixel-text-muted: #64748B;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: var(--pixel-bg-light);
|
||||
background-image: url('/pixel-patterns.svg#pixel-checker-subtle');
|
||||
background: var(--gradient-hero);
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
font-family: 'Manrope', 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
#root {
|
||||
@@ -77,54 +100,47 @@
|
||||
|
||||
/* Pixel Art Utility Classes */
|
||||
.pixel-border {
|
||||
border: 3px solid var(--pixel-outline);
|
||||
box-shadow:
|
||||
4px 4px 0 var(--pixel-shadow-dark),
|
||||
4px 4px 0 3px var(--pixel-outline);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.pixel-card {
|
||||
border: 3px solid var(--pixel-outline);
|
||||
box-shadow:
|
||||
0 0 0 3px var(--pixel-outline),
|
||||
6px 6px 0 var(--pixel-shadow-dark),
|
||||
6px 6px 0 3px var(--pixel-outline);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
background: hsl(var(--surface));
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||||
}
|
||||
|
||||
.pixel-card:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow:
|
||||
0 0 0 3px var(--pixel-outline),
|
||||
8px 8px 0 var(--pixel-shadow-dark),
|
||||
8px 8px 0 3px var(--pixel-outline);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.pixel-button {
|
||||
border: 3px solid var(--pixel-outline);
|
||||
box-shadow:
|
||||
4px 4px 0 var(--pixel-shadow-dark);
|
||||
transition: transform 0.05s ease, box-shadow 0.05s ease;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.08s ease, box-shadow 0.08s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pixel-button:hover {
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow:
|
||||
5px 5px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.pixel-button:active {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow:
|
||||
2px 2px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
/* Focus states for accessibility */
|
||||
button:focus-visible,
|
||||
input:focus-visible {
|
||||
outline: 3px solid var(--pixel-yellow-gold);
|
||||
outline-offset: 2px;
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
/* Reduced motion support */
|
||||
@@ -196,29 +212,29 @@
|
||||
|
||||
.create-buttons button {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--pixel-purple-bright);
|
||||
color: white;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
border-radius: 0;
|
||||
background: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
box-shadow: 4px 4px 0 var(--pixel-shadow-dark);
|
||||
transition: transform 0.05s ease, box-shadow 0.05s ease;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.create-buttons button:hover {
|
||||
background: var(--pixel-purple-deep);
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 5px 5px 0 var(--pixel-shadow-dark);
|
||||
background: hsl(var(--primary) / 0.9);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.create-buttons button:active {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.create-buttons button:disabled {
|
||||
@@ -234,21 +250,17 @@
|
||||
}
|
||||
|
||||
.document-card {
|
||||
background: var(--pixel-white);
|
||||
background: hsl(var(--surface));
|
||||
padding: 1.5rem;
|
||||
border-radius: 0;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
box-shadow:
|
||||
6px 6px 0 var(--pixel-shadow-dark),
|
||||
6px 6px 0 3px var(--pixel-outline);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-md);
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||||
}
|
||||
|
||||
.document-card:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow:
|
||||
8px 8px 0 var(--pixel-shadow-dark),
|
||||
8px 8px 0 3px var(--pixel-outline);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.doc-info h3 {
|
||||
@@ -256,13 +268,13 @@
|
||||
}
|
||||
|
||||
.doc-type {
|
||||
color: var(--pixel-text-secondary);
|
||||
color: hsl(var(--text-secondary));
|
||||
text-transform: capitalize;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.doc-date {
|
||||
color: var(--pixel-text-muted);
|
||||
color: hsl(var(--text-muted));
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
@@ -274,37 +286,37 @@
|
||||
|
||||
.doc-actions button {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
background: var(--pixel-white);
|
||||
border-radius: 0;
|
||||
border: 1px solid hsl(var(--border));
|
||||
background: hsl(var(--surface));
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
box-shadow: 3px 3px 0 var(--pixel-shadow-dark);
|
||||
transition: transform 0.05s ease, box-shadow 0.05s ease;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.doc-actions button:hover {
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 4px 4px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.doc-actions button:active {
|
||||
transform: translate(1px, 1px);
|
||||
box-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.doc-actions button:first-child {
|
||||
background: var(--pixel-cyan-bright);
|
||||
color: white;
|
||||
border-color: var(--pixel-outline);
|
||||
background: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
|
||||
.doc-actions button:first-child:hover {
|
||||
background: var(--pixel-purple-bright);
|
||||
background: hsl(var(--primary));
|
||||
}
|
||||
|
||||
/* Editor Page */
|
||||
@@ -312,6 +324,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background: hsl(var(--background));
|
||||
}
|
||||
|
||||
.page-header {
|
||||
@@ -319,38 +332,104 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: var(--pixel-white);
|
||||
border-bottom: 3px solid var(--pixel-outline);
|
||||
background: hsl(var(--surface));
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.page-header button {
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--pixel-panel);
|
||||
border: 3px solid var(--pixel-outline);
|
||||
border-radius: 0;
|
||||
background: hsl(var(--surface));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
box-shadow: 3px 3px 0 var(--pixel-shadow-dark);
|
||||
transition: transform 0.05s ease, box-shadow 0.05s ease;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
|
||||
}
|
||||
|
||||
.page-header button:hover {
|
||||
background: var(--pixel-bg-light);
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 4px 4px 0 var(--pixel-shadow-dark);
|
||||
background: hsl(var(--surface-muted));
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.page-header button:active {
|
||||
transform: translate(1px, 1px);
|
||||
box-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.sync-status {
|
||||
color: var(--pixel-text-secondary);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: hsl(var(--text-secondary));
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.sync-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 3px;
|
||||
background: hsl(var(--surface-muted));
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.sync-dot.synced {
|
||||
background: hsl(var(--secondary));
|
||||
border-color: hsl(var(--secondary));
|
||||
box-shadow: 0 0 0 2px rgba(20, 184, 166, 0.15);
|
||||
}
|
||||
|
||||
.sync-dot.syncing {
|
||||
background: hsl(var(--primary));
|
||||
border-color: hsl(var(--primary));
|
||||
animation: pulse-dot 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-dot {
|
||||
0%, 100% { transform: scale(1); opacity: 0.7; }
|
||||
50% { transform: scale(1.2); opacity: 1; }
|
||||
}
|
||||
|
||||
.view-only-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 999px;
|
||||
background: hsl(var(--surface));
|
||||
color: hsl(var(--text-secondary));
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-sm);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.view-only-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
.shared-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: hsl(var(--text-secondary));
|
||||
background: hsl(var(--surface-muted));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 6px;
|
||||
box-shadow: 2px 2px 0 rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.page-content {
|
||||
display: flex;
|
||||
flex-direction: column; /* Mobile: stack vertically */
|
||||
@@ -384,9 +463,8 @@
|
||||
|
||||
.sidebar {
|
||||
width: 100%; /* Mobile: full width */
|
||||
background: var(--pixel-white);
|
||||
background-image: url('/pixel-patterns.svg#pixel-scanlines');
|
||||
border-top: 3px solid var(--pixel-outline); /* Mobile: top border */
|
||||
background: hsl(var(--surface));
|
||||
border-top: 1px solid hsl(var(--border)); /* Mobile: top border */
|
||||
border-left: none;
|
||||
padding: 1rem;
|
||||
max-height: 200px; /* Mobile: limit height */
|
||||
@@ -398,18 +476,16 @@
|
||||
width: 250px; /* Desktop: fixed width */
|
||||
max-height: none;
|
||||
border-top: none;
|
||||
border-left: 3px solid var(--pixel-outline);
|
||||
border-left: 1px solid hsl(var(--border));
|
||||
}
|
||||
}
|
||||
|
||||
/* Editor */
|
||||
.editor-container {
|
||||
background: var(--pixel-white);
|
||||
border-radius: 0;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
box-shadow:
|
||||
6px 6px 0 var(--pixel-shadow-dark),
|
||||
6px 6px 0 3px var(--pixel-outline);
|
||||
background: hsl(var(--surface));
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -417,41 +493,39 @@
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--pixel-panel);
|
||||
background-image: url('/pixel-patterns.svg#pixel-panel-texture');
|
||||
border-bottom: 3px solid var(--pixel-outline);
|
||||
background: hsl(var(--surface-muted));
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--pixel-white);
|
||||
border: 3px solid var(--pixel-outline);
|
||||
border-radius: 0;
|
||||
background: hsl(var(--surface));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
box-shadow: 3px 3px 0 var(--pixel-shadow-dark);
|
||||
transition: transform 0.05s ease, box-shadow 0.05s ease;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
|
||||
}
|
||||
|
||||
.toolbar button:hover {
|
||||
background: var(--pixel-bg-light);
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 4px 4px 0 var(--pixel-shadow-dark);
|
||||
background: hsl(var(--surface-muted));
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.toolbar button:active {
|
||||
transform: translate(1px, 1px);
|
||||
box-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.toolbar button.active {
|
||||
background: var(--pixel-cyan-bright);
|
||||
color: white;
|
||||
border-color: var(--pixel-outline);
|
||||
box-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
transform: translate(1px, 1px);
|
||||
background: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
border-color: hsl(var(--border));
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.editor-content {
|
||||
@@ -496,12 +570,13 @@
|
||||
/* Collaborative Cursors */
|
||||
.collaboration-cursor__caret {
|
||||
position: absolute;
|
||||
border-left: 2px solid;
|
||||
border-left: 3px solid;
|
||||
border-right: none;
|
||||
margin-left: -1px;
|
||||
margin-right: -1px;
|
||||
pointer-events: none;
|
||||
word-break: normal;
|
||||
box-shadow: 1px 0 0 currentColor;
|
||||
}
|
||||
|
||||
.collaboration-cursor__label {
|
||||
@@ -515,7 +590,8 @@
|
||||
user-select: none;
|
||||
color: #fff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
white-space: nowrap;
|
||||
box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -537,14 +613,14 @@
|
||||
}
|
||||
|
||||
.kanban-column {
|
||||
background: var(--pixel-panel);
|
||||
background-image: url('/pixel-patterns.svg#pixel-dither-diagonal');
|
||||
border-radius: 0;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
background: hsl(var(--surface));
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
padding: 1rem;
|
||||
width: 100%; /* Mobile: full width */
|
||||
min-width: unset;
|
||||
max-width: unset;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
@@ -574,22 +650,18 @@
|
||||
}
|
||||
|
||||
.kanban-card {
|
||||
background: var(--pixel-white);
|
||||
background: hsl(var(--surface));
|
||||
padding: 1rem;
|
||||
border-radius: 0;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
box-shadow:
|
||||
4px 4px 0 var(--pixel-shadow-dark),
|
||||
4px 4px 0 3px var(--pixel-outline);
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-sm);
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||||
}
|
||||
|
||||
.kanban-card:hover {
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow:
|
||||
5px 5px 0 var(--pixel-shadow-dark),
|
||||
5px 5px 0 3px var(--pixel-outline);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.kanban-card h4 {
|
||||
@@ -597,38 +669,38 @@
|
||||
}
|
||||
|
||||
.kanban-card p {
|
||||
color: var(--pixel-text-secondary);
|
||||
color: hsl(var(--text-secondary));
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.add-task-btn {
|
||||
padding: 0.75rem;
|
||||
background: var(--pixel-white);
|
||||
border: 3px dashed var(--pixel-outline);
|
||||
border-radius: 0;
|
||||
background: hsl(var(--surface));
|
||||
border: 1px dashed hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
color: var(--pixel-text-secondary);
|
||||
color: hsl(var(--text-secondary));
|
||||
}
|
||||
|
||||
.add-task-btn:hover {
|
||||
border-color: var(--pixel-purple-bright);
|
||||
color: var(--pixel-text-primary);
|
||||
background: var(--pixel-bg-light);
|
||||
border-color: hsl(var(--primary));
|
||||
color: hsl(var(--text-primary));
|
||||
background: hsl(var(--surface-muted));
|
||||
}
|
||||
|
||||
.add-task-form input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid var(--pixel-outline);
|
||||
border-radius: 0;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
margin-bottom: 0.5rem;
|
||||
background: var(--pixel-white);
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.add-task-form input:focus {
|
||||
outline: none;
|
||||
border-color: var(--pixel-cyan-bright);
|
||||
box-shadow: 0 0 0 2px var(--pixel-cyan-bright);
|
||||
border-color: hsl(var(--primary));
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
@@ -638,28 +710,28 @@
|
||||
|
||||
.form-actions button {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
border-radius: 0;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
background: var(--pixel-white);
|
||||
box-shadow: 3px 3px 0 var(--pixel-shadow-dark);
|
||||
transition: transform 0.05s ease, box-shadow 0.05s ease;
|
||||
background: hsl(var(--surface));
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
|
||||
}
|
||||
|
||||
.form-actions button:hover {
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 4px 4px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.form-actions button:active {
|
||||
transform: translate(1px, 1px);
|
||||
box-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.form-actions button:first-child {
|
||||
background: var(--pixel-green-lime);
|
||||
color: var(--pixel-text-primary);
|
||||
border-color: var(--pixel-outline);
|
||||
background: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
|
||||
/* User List */
|
||||
@@ -682,8 +754,8 @@
|
||||
.user-color {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--pixel-outline);
|
||||
border-radius: 999px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.user-name {
|
||||
@@ -696,5 +768,5 @@
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
font-size: 1.25rem;
|
||||
color: var(--pixel-text-secondary);
|
||||
color: hsl(var(--text-secondary));
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import UserList from "../components/Presence/UserList.tsx";
|
||||
import ShareModal from "../components/Share/ShareModal.tsx";
|
||||
import VersionHistoryPanel from "../components/VersionHistory/VersionHistoryPanel.tsx";
|
||||
import { useYjsDocument } from "../hooks/useYjsDocument.ts";
|
||||
import { Eye } from "lucide-react";
|
||||
|
||||
const EditorPage = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
@@ -27,14 +28,11 @@ const EditorPage = () => {
|
||||
<button onClick={() => navigate("/")}>← Back to Home</button>
|
||||
<div className="header-actions">
|
||||
{permission !== "edit" && permission !== null && (
|
||||
<div className="view-only-badge" style={{ display: 'flex', alignItems: 'center', gap: '4px', padding: '4px 12px', backgroundColor: '#f0f0f0', borderRadius: '4px', fontSize: '14px' }}>
|
||||
<span>👁️</span>
|
||||
<div className="view-only-badge">
|
||||
<Eye className="view-only-icon" />
|
||||
<span>View only</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="sync-status">
|
||||
{synced ? "✓ Synced" : "⟳ Syncing..."}
|
||||
</div>
|
||||
{!shareToken && (
|
||||
<button className="share-btn" onClick={() => setShowShareModal(true)}>
|
||||
Share
|
||||
|
||||
@@ -6,8 +6,8 @@ import { documentsApi } from '@/api/document';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import { DocumentCard } from '@/components/Home/DocumentCard';
|
||||
import { CreateButton } from '@/components/Home/CreateButton';
|
||||
import FloatingGem from '@/components/PixelSprites/FloatingGem';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import PixelIcon from '@/components/PixelIcon/PixelIcon';
|
||||
|
||||
const Home = () => {
|
||||
const { user } = useAuth();
|
||||
@@ -64,8 +64,8 @@ const Home = () => {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-pixel-bg-light">
|
||||
<div className="font-pixel text-pixel-purple-bright animate-pixel-bounce">
|
||||
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||
<div className="text-brand text-base font-medium">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,15 +75,11 @@ const Home = () => {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="max-w-7xl mx-auto px-8 py-12 relative min-h-screen bg-pixel-bg-light">
|
||||
{/* Decorative floating gems */}
|
||||
<FloatingGem position={{ top: '20px', right: '40px' }} delay={0} size={40} />
|
||||
<FloatingGem position={{ top: '60px', left: '60px' }} delay={1.5} size={32} />
|
||||
<FloatingGem position={{ bottom: '100px', right: '100px' }} delay={3} size={36} />
|
||||
<div className="max-w-7xl mx-auto px-8 py-12 relative min-h-screen bg-background">
|
||||
|
||||
{/* Page Header */}
|
||||
<div className="mb-12">
|
||||
<h1 className="font-pixel text-4xl text-pixel-purple-bright mb-6 tracking-wide">
|
||||
<h1 className="font-display text-3xl text-text-primary mb-4">
|
||||
My Workspace
|
||||
</h1>
|
||||
|
||||
@@ -109,10 +105,10 @@ const Home = () => {
|
||||
{/* Tabbed Interface */}
|
||||
<Tabs defaultValue="owned" className="w-full">
|
||||
<TabsList className="
|
||||
bg-pixel-panel
|
||||
border-[3px]
|
||||
border-pixel-outline
|
||||
shadow-pixel-sm
|
||||
bg-surface-muted
|
||||
border
|
||||
border-border
|
||||
shadow-soft
|
||||
p-1
|
||||
mb-8
|
||||
">
|
||||
@@ -121,9 +117,9 @@ const Home = () => {
|
||||
className="
|
||||
font-sans
|
||||
font-semibold
|
||||
data-[state=active]:bg-pixel-cyan-bright
|
||||
data-[state=active]:text-white
|
||||
data-[state=active]:shadow-pixel-sm
|
||||
data-[state=active]:bg-surface
|
||||
data-[state=active]:text-text-primary
|
||||
data-[state=active]:shadow-soft
|
||||
transition-all
|
||||
duration-100
|
||||
"
|
||||
@@ -135,9 +131,9 @@ const Home = () => {
|
||||
className="
|
||||
font-sans
|
||||
font-semibold
|
||||
data-[state=active]:bg-pixel-cyan-bright
|
||||
data-[state=active]:text-white
|
||||
data-[state=active]:shadow-pixel-sm
|
||||
data-[state=active]:bg-surface
|
||||
data-[state=active]:text-text-primary
|
||||
data-[state=active]:shadow-soft
|
||||
transition-all
|
||||
duration-100
|
||||
"
|
||||
@@ -150,9 +146,10 @@ const Home = () => {
|
||||
<TabsContent value="owned">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{ownedDocuments.length === 0 ? (
|
||||
<p className="col-span-full text-center text-pixel-text-muted font-sans py-12">
|
||||
No documents yet. Create one to get started!
|
||||
</p>
|
||||
<div className="col-span-full flex flex-col items-center gap-3 text-text-muted font-sans py-12">
|
||||
<PixelIcon name="gem" size={20} color="hsl(var(--brand-teal))" />
|
||||
<p>No documents yet. Create one to get started!</p>
|
||||
</div>
|
||||
) : (
|
||||
ownedDocuments.map((doc) => (
|
||||
<DocumentCard
|
||||
@@ -170,9 +167,10 @@ const Home = () => {
|
||||
<TabsContent value="shared">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{sharedDocuments.length === 0 ? (
|
||||
<p className="col-span-full text-center text-pixel-text-muted font-sans py-12">
|
||||
No shared documents yet.
|
||||
</p>
|
||||
<div className="col-span-full flex flex-col items-center gap-3 text-text-muted font-sans py-12">
|
||||
<PixelIcon name="gem" size={20} color="hsl(var(--brand-teal))" />
|
||||
<p>No shared documents yet.</p>
|
||||
</div>
|
||||
) : (
|
||||
sharedDocuments.map((doc) => (
|
||||
<DocumentCard
|
||||
|
||||
@@ -24,9 +24,6 @@ const KanbanPage = () => {
|
||||
<div className="page-header">
|
||||
<button onClick={() => navigate("/")}>← Back to Home</button>
|
||||
<div className="header-actions">
|
||||
<div className="sync-status">
|
||||
{synced ? "✓ Synced" : "⟳ Syncing..."}
|
||||
</div>
|
||||
{!shareToken && (
|
||||
<button className="share-btn" onClick={() => setShowShareModal(true)}>
|
||||
Share
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
.landing-page {
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
color: hsl(var(--text-primary));
|
||||
background: hsl(var(--background));
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
@@ -10,248 +11,313 @@
|
||||
======================================== */
|
||||
|
||||
.landing-hero {
|
||||
min-height: 100vh;
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 2rem;
|
||||
|
||||
/* Animated gradient background */
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--pixel-purple-deep) 0%,
|
||||
var(--pixel-purple-bright) 40%,
|
||||
var(--pixel-pink-vibrant) 100%
|
||||
);
|
||||
background-size: 200% 200%;
|
||||
animation: gradient-shift 12s ease infinite;
|
||||
padding: 6rem 2rem 5rem;
|
||||
background: var(--gradient-hero);
|
||||
}
|
||||
|
||||
@keyframes gradient-shift {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
.hero-gem svg {
|
||||
opacity: 0.18;
|
||||
filter: drop-shadow(0 10px 20px rgba(15, 23, 42, 0.2));
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 3rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
text-align: center;
|
||||
z-index: 10;
|
||||
max-width: 800px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.hero-logo {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 2rem;
|
||||
gap: 12px;
|
||||
padding: 0.4rem 0.85rem;
|
||||
border-radius: 999px;
|
||||
background: hsl(var(--surface));
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-sm);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.hero-brand {
|
||||
font-size: 3rem;
|
||||
font-weight: 800;
|
||||
color: var(--pixel-white);
|
||||
text-shadow:
|
||||
4px 4px 0 var(--pixel-shadow-dark),
|
||||
-1px -1px 0 var(--pixel-shadow-dark),
|
||||
1px -1px 0 var(--pixel-shadow-dark),
|
||||
-1px 1px 0 var(--pixel-shadow-dark);
|
||||
margin: 0;
|
||||
letter-spacing: -1px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: hsl(var(--text-secondary));
|
||||
}
|
||||
|
||||
.hero-headline {
|
||||
font-size: 2.5rem;
|
||||
font-size: 2.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--pixel-white);
|
||||
margin: 0 0 1.5rem 0;
|
||||
text-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
line-height: 1.2;
|
||||
color: hsl(var(--text-primary));
|
||||
margin: 0 0 1.25rem 0;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.hero-tagline {
|
||||
font-size: 1.25rem;
|
||||
color: var(--pixel-bg-light);
|
||||
margin: 0 0 3rem 0;
|
||||
line-height: 1.6;
|
||||
opacity: 0.95;
|
||||
font-size: 1.1rem;
|
||||
color: hsl(var(--text-secondary));
|
||||
margin: 0 0 2rem 0;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.hero-login-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero-scroll-hint {
|
||||
position: absolute;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: var(--pixel-white);
|
||||
opacity: 0.6;
|
||||
animation: bounce-hint 2s ease-in-out infinite;
|
||||
.hero-note {
|
||||
font-size: 0.9rem;
|
||||
color: hsl(var(--text-muted));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@keyframes bounce-hint {
|
||||
0%, 100% {
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-50%) translateY(10px);
|
||||
}
|
||||
.provider-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Login Buttons
|
||||
======================================== */
|
||||
|
||||
.landing-login-button {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 1rem 2rem;
|
||||
gap: 10px;
|
||||
padding: 0.9rem 1.4rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
background: hsl(var(--surface));
|
||||
color: hsl(var(--text-primary));
|
||||
cursor: pointer;
|
||||
transition: transform 0.05s ease, box-shadow 0.05s ease;
|
||||
min-width: 260px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
|
||||
}
|
||||
|
||||
.landing-login-button.google {
|
||||
background: var(--pixel-white);
|
||||
color: var(--pixel-text-primary);
|
||||
box-shadow: 4px 4px 0 var(--pixel-shadow-dark);
|
||||
.landing-login-button:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.landing-login-button.google:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 6px 6px 0 var(--pixel-shadow-dark);
|
||||
background: var(--pixel-panel);
|
||||
.landing-login-button.primary {
|
||||
background: var(--gradient-accent);
|
||||
color: white;
|
||||
border: none;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.landing-login-button.google:active {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
.landing-login-button.primary:hover {
|
||||
filter: brightness(0.98);
|
||||
}
|
||||
|
||||
.landing-login-button.github {
|
||||
background: var(--pixel-bg-dark);
|
||||
color: var(--pixel-white);
|
||||
box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.landing-login-button.github:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.5);
|
||||
background: var(--pixel-bg-medium);
|
||||
}
|
||||
|
||||
.landing-login-button.github:active {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.5);
|
||||
.landing-login-button.provider {
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.landing-login-button.large {
|
||||
padding: 1.25rem 2.5rem;
|
||||
font-size: 1.125rem;
|
||||
min-width: 300px;
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.oauth-icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hero-mock {
|
||||
background: hsl(var(--surface));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mock-topbar {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
background: hsl(var(--surface-muted));
|
||||
}
|
||||
|
||||
.mock-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: hsl(var(--border));
|
||||
}
|
||||
|
||||
.mock-body {
|
||||
padding: 20px 22px 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.mock-tabs {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
font-size: 0.85rem;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
|
||||
.mock-tab {
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.mock-tab.active {
|
||||
color: hsl(var(--text-primary));
|
||||
border-bottom-color: hsl(var(--primary));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mock-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mock-card {
|
||||
background: hsl(var(--surface-muted));
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.mock-line {
|
||||
height: 8px;
|
||||
background: hsl(var(--border));
|
||||
border-radius: 999px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mock-line.wide {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.mock-line.short {
|
||||
width: 55%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.mock-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mock-row {
|
||||
display: grid;
|
||||
grid-template-columns: 24px 1fr 80px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.mock-pill {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 999px;
|
||||
background: hsl(var(--secondary));
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Features Section
|
||||
======================================== */
|
||||
|
||||
.landing-features {
|
||||
padding: 6rem 2rem;
|
||||
background: var(--pixel-bg-light);
|
||||
position: relative;
|
||||
padding: 5rem 2rem;
|
||||
background: hsl(var(--background));
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 0 2rem;
|
||||
background: hsl(var(--background));
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: hsl(var(--border));
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
font-size: 2.5rem;
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--pixel-text-primary);
|
||||
margin: 0 0 4rem 0;
|
||||
text-shadow: 2px 2px 0 var(--pixel-white);
|
||||
color: hsl(var(--text-primary));
|
||||
margin: 0 0 3rem 0;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
gap: 1.75rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: var(--pixel-white);
|
||||
padding: 2.5rem 2rem;
|
||||
border: 3px solid var(--pixel-outline);
|
||||
box-shadow:
|
||||
0 0 0 3px var(--pixel-outline),
|
||||
6px 6px 0 var(--pixel-shadow-dark),
|
||||
6px 6px 0 3px var(--pixel-outline);
|
||||
text-align: center;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
opacity: 0;
|
||||
animation: fade-in-up 0.6s ease forwards;
|
||||
}
|
||||
|
||||
.feature-card:nth-child(1) { animation-delay: 0.1s; }
|
||||
.feature-card:nth-child(2) { animation-delay: 0.2s; }
|
||||
.feature-card:nth-child(3) { animation-delay: 0.3s; }
|
||||
.feature-card:nth-child(4) { animation-delay: 0.4s; }
|
||||
|
||||
@keyframes fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
background: hsl(var(--surface));
|
||||
padding: 2rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
text-align: left;
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translate(-3px, -3px);
|
||||
box-shadow:
|
||||
0 0 0 3px var(--pixel-outline),
|
||||
9px 9px 0 var(--pixel-shadow-dark),
|
||||
9px 9px 0 3px var(--pixel-outline);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
margin-bottom: 1.5rem;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 14px;
|
||||
background: hsl(var(--surface-muted));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.25rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-size: 1.25rem;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
color: var(--pixel-text-primary);
|
||||
margin: 0 0 0.75rem 0;
|
||||
color: hsl(var(--text-primary));
|
||||
margin: 0 0 0.6rem 0;
|
||||
}
|
||||
|
||||
.feature-description {
|
||||
font-size: 1rem;
|
||||
color: var(--pixel-text-secondary);
|
||||
color: hsl(var(--text-secondary));
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
@@ -259,44 +325,39 @@
|
||||
======================================== */
|
||||
|
||||
.landing-footer {
|
||||
padding: 6rem 2rem;
|
||||
background: var(--pixel-bg-dark);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 4.5rem 2rem;
|
||||
background: hsl(var(--surface-muted));
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.footer-headline {
|
||||
font-size: 2rem;
|
||||
font-size: 1.9rem;
|
||||
font-weight: 700;
|
||||
color: var(--pixel-white);
|
||||
margin: 0 0 1rem 0;
|
||||
text-shadow: 2px 2px 0 var(--pixel-shadow-dark);
|
||||
color: hsl(var(--text-primary));
|
||||
margin: 0 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.footer-tagline {
|
||||
font-size: 1.125rem;
|
||||
color: var(--pixel-bg-light);
|
||||
margin: 0 0 2.5rem 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1.05rem;
|
||||
color: hsl(var(--text-secondary));
|
||||
margin: 0 0 2rem 0;
|
||||
}
|
||||
|
||||
.footer-login-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 3rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.footer-tech {
|
||||
font-size: 0.875rem;
|
||||
color: var(--pixel-text-muted);
|
||||
font-size: 0.9rem;
|
||||
color: hsl(var(--text-muted));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -304,23 +365,17 @@
|
||||
Responsive Design
|
||||
======================================== */
|
||||
|
||||
/* Tablet (768px+) */
|
||||
@media (min-width: 768px) {
|
||||
.hero-brand {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.hero-headline {
|
||||
font-size: 3rem;
|
||||
font-size: 3.25rem;
|
||||
}
|
||||
|
||||
.hero-tagline {
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.hero-login-buttons {
|
||||
.provider-buttons {
|
||||
flex-direction: row;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
@@ -328,48 +383,23 @@
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 3rem;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop (1024px+) */
|
||||
@media (min-width: 1024px) {
|
||||
.hero-brand {
|
||||
font-size: 4.5rem;
|
||||
}
|
||||
|
||||
.hero-headline {
|
||||
font-size: 3.5rem;
|
||||
.hero-grid {
|
||||
grid-template-columns: 1.05fr 0.95fr;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Large Desktop (1280px+) */
|
||||
@media (min-width: 1280px) {
|
||||
.hero-headline {
|
||||
font-size: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduced Motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.landing-hero {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.hero-scroll-hint {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.landing-login-button,
|
||||
.feature-card {
|
||||
animation: none;
|
||||
opacity: 1;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,43 +16,93 @@ function LandingPage() {
|
||||
<div className="landing-page">
|
||||
{/* Hero Section */}
|
||||
<section className="landing-hero">
|
||||
<FloatingGem position={{ top: '10%', left: '8%' }} delay={0} size={44} />
|
||||
<FloatingGem position={{ top: '15%', right: '12%' }} delay={1.5} size={36} />
|
||||
<FloatingGem position={{ top: '45%', left: '5%' }} delay={2.5} size={28} />
|
||||
<FloatingGem position={{ bottom: '25%', right: '8%' }} delay={3.5} size={40} />
|
||||
<FloatingGem position={{ bottom: '15%', left: '15%' }} delay={4} size={32} />
|
||||
<FloatingGem position={{ top: '60%', right: '20%' }} delay={1} size={24} />
|
||||
|
||||
<div className="hero-content">
|
||||
<div className="hero-logo">
|
||||
<PixelIcon name="gem" size={56} color="var(--pixel-yellow-gold)" />
|
||||
<h1 className="hero-brand">DocNest</h1>
|
||||
</div>
|
||||
|
||||
<h2 className="hero-headline">Create Together. In Real-Time.</h2>
|
||||
<p className="hero-tagline">
|
||||
Collaborative documents and Kanban boards that sync instantly.
|
||||
<br />
|
||||
Work with your team from anywhere, even offline.
|
||||
</p>
|
||||
|
||||
<div className="hero-login-buttons">
|
||||
<button className="landing-login-button google" onClick={handleGoogleLogin}>
|
||||
<GoogleIcon />
|
||||
<span>Sign in with Google</span>
|
||||
</button>
|
||||
<button className="landing-login-button github" onClick={handleGitHubLogin}>
|
||||
<GitHubIcon />
|
||||
<span>Sign in with GitHub</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="hero-gem hero-gem-one">
|
||||
<FloatingGem position={{ top: '12%', left: '8%' }} delay={0} size={28} />
|
||||
</div>
|
||||
<div className="hero-gem hero-gem-two">
|
||||
<FloatingGem position={{ bottom: '18%', right: '10%' }} delay={1.5} size={24} />
|
||||
</div>
|
||||
|
||||
<div className="hero-scroll-hint">
|
||||
<PixelIcon name="back-arrow" size={24} style={{ transform: 'rotate(-90deg)' }} />
|
||||
<div className="hero-grid">
|
||||
<div className="hero-content">
|
||||
<div className="hero-logo">
|
||||
<PixelIcon name="gem" size={28} color="hsl(var(--brand-teal))" />
|
||||
<span className="hero-brand">DocNest</span>
|
||||
</div>
|
||||
|
||||
<h1 className="hero-headline">Create together. In real time.</h1>
|
||||
<p className="hero-tagline">
|
||||
Collaborative documents and Kanban boards that sync instantly.
|
||||
<br />
|
||||
Work with your team from anywhere, even offline.
|
||||
</p>
|
||||
|
||||
<div className="hero-login-buttons">
|
||||
<button className="landing-login-button primary" onClick={handleGoogleLogin}>
|
||||
Get started free
|
||||
<PixelIcon name="gem" size={16} color="white" />
|
||||
</button>
|
||||
<div className="provider-buttons">
|
||||
<button className="landing-login-button provider" onClick={handleGoogleLogin}>
|
||||
<GoogleIcon />
|
||||
<span>Continue with Google</span>
|
||||
</button>
|
||||
<button className="landing-login-button provider" onClick={handleGitHubLogin}>
|
||||
<GitHubIcon />
|
||||
<span>Continue with GitHub</span>
|
||||
</button>
|
||||
</div>
|
||||
<p className="hero-note">No credit card required.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hero-mock">
|
||||
<div className="mock-topbar">
|
||||
<div className="mock-dot" />
|
||||
<div className="mock-dot" />
|
||||
<div className="mock-dot" />
|
||||
</div>
|
||||
<div className="mock-body">
|
||||
<div className="mock-tabs">
|
||||
<span className="mock-tab active">Docs</span>
|
||||
<span className="mock-tab">Boards</span>
|
||||
<span className="mock-tab">History</span>
|
||||
</div>
|
||||
<div className="mock-grid">
|
||||
<div className="mock-card">
|
||||
<div className="mock-line wide" />
|
||||
<div className="mock-line" />
|
||||
<div className="mock-line short" />
|
||||
</div>
|
||||
<div className="mock-card">
|
||||
<div className="mock-line wide" />
|
||||
<div className="mock-line" />
|
||||
<div className="mock-line short" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mock-rows">
|
||||
<div className="mock-row">
|
||||
<span className="mock-pill" />
|
||||
<span className="mock-line" />
|
||||
<span className="mock-line short" />
|
||||
</div>
|
||||
<div className="mock-row">
|
||||
<span className="mock-pill" />
|
||||
<span className="mock-line" />
|
||||
<span className="mock-line short" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="section-divider">
|
||||
<span className="divider-line" />
|
||||
<PixelIcon name="gem" size={16} color="hsl(var(--brand-teal))" />
|
||||
<span className="divider-line" />
|
||||
</div>
|
||||
|
||||
{/* Features Section */}
|
||||
<section className="landing-features">
|
||||
<h2 className="section-title">Why DocNest?</h2>
|
||||
@@ -61,42 +111,44 @@ function LandingPage() {
|
||||
icon="sync-arrows"
|
||||
title="Real-Time Collaboration"
|
||||
description="See changes as they happen. Multiple cursors show who's working where."
|
||||
color="var(--pixel-cyan-bright)"
|
||||
color="hsl(var(--brand-teal))"
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="document"
|
||||
title="Rich Documents"
|
||||
description="Create formatted documents with headings, lists, and more."
|
||||
color="var(--pixel-purple-bright)"
|
||||
color="hsl(var(--brand))"
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="kanban"
|
||||
title="Kanban Boards"
|
||||
description="Drag-and-drop task management with real-time updates."
|
||||
color="var(--pixel-orange-warm)"
|
||||
color="hsl(var(--accent))"
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="shield"
|
||||
title="Offline Support"
|
||||
description="Your work syncs automatically when you're back online."
|
||||
color="var(--pixel-green-lime)"
|
||||
color="hsl(var(--secondary))"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="section-divider">
|
||||
<span className="divider-line" />
|
||||
<PixelIcon name="gem" size={16} color="hsl(var(--brand-teal))" />
|
||||
<span className="divider-line" />
|
||||
</div>
|
||||
|
||||
{/* Footer CTA */}
|
||||
<footer className="landing-footer">
|
||||
<FloatingGem position={{ top: '20%', left: '10%' }} delay={0.5} size={28} />
|
||||
<FloatingGem position={{ bottom: '30%', right: '12%' }} delay={2} size={32} />
|
||||
|
||||
<div className="footer-content">
|
||||
<h3 className="footer-headline">Ready to collaborate?</h3>
|
||||
<p className="footer-tagline">Join thousands of teams creating together.</p>
|
||||
<p className="footer-tagline">Join teams building together in DocNest.</p>
|
||||
|
||||
<div className="footer-login-buttons">
|
||||
<button className="landing-login-button google large" onClick={handleGoogleLogin}>
|
||||
<GoogleIcon />
|
||||
<span>Get Started Free</span>
|
||||
<button className="landing-login-button primary large" onClick={handleGoogleLogin}>
|
||||
Get started free
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 20px;
|
||||
background: var(--gradient-hero);
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||||
background: hsl(var(--surface));
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: var(--shadow-lg);
|
||||
padding: 48px 40px;
|
||||
max-width: 440px;
|
||||
width: 100%;
|
||||
@@ -20,20 +21,20 @@
|
||||
.login-title {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #1a202c;
|
||||
color: hsl(var(--text-primary));
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
font-size: 16px;
|
||||
color: #718096;
|
||||
color: hsl(var(--text-secondary));
|
||||
margin: 0 0 32px 0;
|
||||
}
|
||||
|
||||
.login-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
@@ -41,14 +42,22 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 14px 24px;
|
||||
font-size: 16px;
|
||||
padding: 12px 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
|
||||
width: 100%;
|
||||
background: hsl(var(--surface));
|
||||
color: hsl(var(--text-primary));
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.login-button:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
@@ -57,27 +66,20 @@
|
||||
}
|
||||
|
||||
.google-button {
|
||||
background: #4285f4;
|
||||
background: var(--gradient-accent);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.google-button:hover {
|
||||
background: #357ae8;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(66, 133, 244, 0.3);
|
||||
filter: brightness(0.98);
|
||||
}
|
||||
|
||||
.github-button {
|
||||
background: #24292e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.github-button:hover {
|
||||
background: #1a1f23;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(36, 41, 46, 0.3);
|
||||
background: hsl(var(--surface));
|
||||
}
|
||||
|
||||
.login-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ function LoginPage() {
|
||||
return (
|
||||
<div className="login-page">
|
||||
<div className="login-container">
|
||||
<h1 className="login-title">Realtime Collab</h1>
|
||||
<p className="login-subtitle">Collaborate in real-time with your team</p>
|
||||
<h1 className="login-title">DocNest</h1>
|
||||
<p className="login-subtitle">Collaborate in real time with your team</p>
|
||||
|
||||
<div className="login-buttons">
|
||||
<button
|
||||
@@ -61,7 +61,7 @@ function LoginPage() {
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
/>
|
||||
</svg>
|
||||
Sign in with Google
|
||||
Continue with Google
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -74,7 +74,7 @@ function LoginPage() {
|
||||
d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34-.46-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.87 1.52 2.34 1.07 2.91.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.92 0-1.11.38-2 1.03-2.71-.1-.25-.45-1.29.1-2.64 0 0 .84-.27 2.75 1.02.79-.22 1.65-.33 2.5-.33.85 0 1.71.11 2.5.33 1.91-1.29 2.75-1.02 2.75-1.02.55 1.35.2 2.39.1 2.64.65.71 1.03 1.6 1.03 2.71 0 3.82-2.34 4.66-4.57 4.91.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2z"
|
||||
/>
|
||||
</svg>
|
||||
Sign in with GitHub
|
||||
Continue with GitHub
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user