- SPEC.md: Complete UI/UX specification with design principles, layout architecture, interaction patterns, data flow, and accessibility requirements - VIEWS.md: Text-based wireframes for Dashboard, Document Viewer, Document Editor, and Library Browser views (desktop and mobile) - COMPONENTS.md: Reusable component library with buttons, inputs, cards, tags, navigation, modals, and feedback components - STYLES.md: Style guide with design tokens (colors, typography, spacing), global styles, animations, responsive breakpoints, and dark mode implementation Design follows dark mode first approach inspired by Mission Control dashboard, with full mobile responsiveness.
1139 lines
26 KiB
Markdown
1139 lines
26 KiB
Markdown
# SimpleNote Web - Reusable Components
|
||
|
||
Component library for SimpleNote Web. All components use CSS custom properties for theming.
|
||
|
||
---
|
||
|
||
## 1. Buttons
|
||
|
||
### 1.1 Primary Button
|
||
```html
|
||
<button class="btn btn-primary">Save</button>
|
||
```
|
||
```css
|
||
.btn-primary {
|
||
background: var(--color-accent);
|
||
color: var(--color-bg);
|
||
border: none;
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 6px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: background 0.15s, transform 0.1s;
|
||
}
|
||
.btn-primary:hover { background: var(--color-accent-hover); }
|
||
.btn-primary:active { transform: scale(0.98); }
|
||
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
||
```
|
||
|
||
### 1.2 Secondary Button
|
||
```html
|
||
<button class="btn btn-secondary">Cancel</button>
|
||
```
|
||
```css
|
||
.btn-secondary {
|
||
background: transparent;
|
||
color: var(--color-text);
|
||
border: 1px solid var(--color-border);
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
transition: background 0.15s, border-color 0.15s;
|
||
}
|
||
.btn-secondary:hover {
|
||
background: var(--color-hover);
|
||
border-color: var(--color-text-muted);
|
||
}
|
||
```
|
||
|
||
### 1.3 Ghost Button (icon-only)
|
||
```html
|
||
<button class="btn btn-ghost" aria-label="Edit">
|
||
<svg><!-- pencil icon --></svg>
|
||
</button>
|
||
```
|
||
```css
|
||
.btn-ghost {
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
padding: 0.5rem;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background 0.15s, color 0.15s;
|
||
}
|
||
.btn-ghost:hover {
|
||
background: var(--color-hover);
|
||
color: var(--color-text);
|
||
}
|
||
```
|
||
|
||
### 1.4 Danger Button
|
||
```html
|
||
<button class="btn btn-danger">Delete</button>
|
||
```
|
||
```css
|
||
.btn-danger {
|
||
background: var(--color-danger);
|
||
color: white;
|
||
border: none;
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 6px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
}
|
||
.btn-danger:hover { background: var(--color-danger-hover); }
|
||
```
|
||
|
||
### 1.5 Icon Button with Label
|
||
```html
|
||
<button class="btn btn-icon-label">
|
||
<svg><!-- plus icon --></svg>
|
||
<span>New Doc</span>
|
||
</button>
|
||
```
|
||
```css
|
||
.btn-icon-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
background: var(--color-accent);
|
||
color: var(--color-bg);
|
||
border: none;
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 6px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Inputs
|
||
|
||
### 2.1 Text Input
|
||
```html
|
||
<div class="input-group">
|
||
<label for="title">Title</label>
|
||
<input type="text" id="title" class="input" placeholder="Document title...">
|
||
</div>
|
||
```
|
||
```css
|
||
.input-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
}
|
||
.input-group label {
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
color: var(--color-text-muted);
|
||
}
|
||
.input {
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
color: var(--color-text);
|
||
padding: 0.625rem 0.875rem;
|
||
border-radius: 6px;
|
||
font-size: 1rem;
|
||
transition: border-color 0.15s, box-shadow 0.15s;
|
||
}
|
||
.input:focus {
|
||
outline: none;
|
||
border-color: var(--color-accent);
|
||
box-shadow: 0 0 0 3px var(--color-accent-alpha);
|
||
}
|
||
.input::placeholder { color: var(--color-text-muted); }
|
||
.input:disabled { opacity: 0.5; cursor: not-allowed; }
|
||
.input.error { border-color: var(--color-danger); }
|
||
```
|
||
|
||
### 2.2 Search Input
|
||
```html
|
||
<div class="search-input">
|
||
<svg class="search-icon"><!-- search icon --></svg>
|
||
<input type="text" placeholder="Search..." class="input">
|
||
<button class="search-clear" aria-label="Clear">✕</button>
|
||
</div>
|
||
```
|
||
```css
|
||
.search-input {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.search-input .input {
|
||
padding-left: 2.5rem;
|
||
padding-right: 2.5rem;
|
||
width: 100%;
|
||
}
|
||
.search-icon {
|
||
position: absolute;
|
||
left: 0.75rem;
|
||
color: var(--color-text-muted);
|
||
pointer-events: none;
|
||
}
|
||
.search-clear {
|
||
position: absolute;
|
||
right: 0.5rem;
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
cursor: pointer;
|
||
padding: 0.25rem;
|
||
border-radius: 4px;
|
||
display: none;
|
||
}
|
||
.search-input.has-value .search-clear { display: block; }
|
||
.search-clear:hover { color: var(--color-text); }
|
||
```
|
||
|
||
### 2.3 Select / Dropdown
|
||
```html
|
||
<div class="select-group">
|
||
<label for="type">Type</label>
|
||
<select id="type" class="select">
|
||
<option value="requirement">Requirement</option>
|
||
<option value="note">Note</option>
|
||
<option value="spec">Specification</option>
|
||
<option value="general">General</option>
|
||
</select>
|
||
</div>
|
||
```
|
||
```css
|
||
.select {
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
color: var(--color-text);
|
||
padding: 0.625rem 2rem 0.625rem 0.875rem;
|
||
border-radius: 6px;
|
||
font-size: 1rem;
|
||
cursor: pointer;
|
||
appearance: none;
|
||
background-image: url("data:image/svg+xml,...");
|
||
background-repeat: no-repeat;
|
||
background-position: right 0.75rem center;
|
||
background-size: 1rem;
|
||
}
|
||
.select:focus {
|
||
outline: none;
|
||
border-color: var(--color-accent);
|
||
box-shadow: 0 0 0 3px var(--color-accent-alpha);
|
||
}
|
||
```
|
||
|
||
### 2.4 Tags Input (Chip-style)
|
||
```html
|
||
<div class="tags-input" id="tags-input">
|
||
<div class="tags-chips">
|
||
<span class="tag-chip">
|
||
backend
|
||
<button class="tag-remove" data-tag="backend">×</button>
|
||
</span>
|
||
<span class="tag-chip">
|
||
api
|
||
<button class="tag-remove" data-tag="api">×</button>
|
||
</span>
|
||
</div>
|
||
<input type="text" class="tags-input-field" placeholder="Add tag...">
|
||
</div>
|
||
```
|
||
```css
|
||
.tags-input {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem;
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 6px;
|
||
min-height: 42px;
|
||
}
|
||
.tags-input:focus-within {
|
||
border-color: var(--color-accent);
|
||
box-shadow: 0 0 0 3px var(--color-accent-alpha);
|
||
}
|
||
.tags-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
||
.tag-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
background: var(--color-accent-alpha);
|
||
color: var(--color-accent);
|
||
padding: 0.25rem 0.5rem;
|
||
border-radius: 4px;
|
||
font-size: 0.875rem;
|
||
}
|
||
.tag-remove {
|
||
background: transparent;
|
||
border: none;
|
||
color: inherit;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
font-size: 1rem;
|
||
line-height: 1;
|
||
}
|
||
.tag-remove:hover { color: var(--color-danger); }
|
||
.tags-input-field {
|
||
flex: 1;
|
||
min-width: 100px;
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text);
|
||
padding: 0.25rem;
|
||
font-size: 0.875rem;
|
||
}
|
||
.tags-input-field:focus { outline: none; }
|
||
```
|
||
|
||
### 2.5 Textarea (Markdown Editor)
|
||
```html
|
||
<textarea class="textarea-editor" id="editor" rows="20">
|
||
## Markdown content
|
||
</textarea>
|
||
```
|
||
```css
|
||
.textarea-editor {
|
||
width: 100%;
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
color: var(--color-text);
|
||
padding: 1rem;
|
||
border-radius: 6px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.9375rem;
|
||
line-height: 1.6;
|
||
resize: vertical;
|
||
tab-size: 2;
|
||
}
|
||
.textarea-editor:focus {
|
||
outline: none;
|
||
border-color: var(--color-accent);
|
||
box-shadow: 0 0 0 3px var(--color-accent-alpha);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Cards
|
||
|
||
### 3.1 Document Card
|
||
```html
|
||
<article class="doc-card" data-id="uuid">
|
||
<div class="doc-card-header">
|
||
<span class="doc-type-badge">REQ</span>
|
||
<h3 class="doc-title">API Authentication Design</h3>
|
||
</div>
|
||
<div class="doc-tags">
|
||
<button class="tag-pill" data-tag="backend">backend</button>
|
||
<button class="tag-pill" data-tag="api">api</button>
|
||
<button class="tag-pill" data-tag="auth">auth</button>
|
||
</div>
|
||
<div class="doc-meta">
|
||
<span class="doc-date">📅 Mar 28</span>
|
||
<span class="doc-author">👤 agent-001</span>
|
||
<span class="doc-status status-draft">✓ draft</span>
|
||
<span class="doc-priority priority-high">🔴 high</span>
|
||
</div>
|
||
</article>
|
||
```
|
||
```css
|
||
.doc-card {
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
cursor: pointer;
|
||
transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
|
||
}
|
||
.doc-card:hover {
|
||
border-color: var(--color-accent);
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
transform: translateY(-1px);
|
||
}
|
||
.doc-card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
.doc-type-badge {
|
||
background: var(--color-accent-alpha);
|
||
color: var(--color-accent);
|
||
font-size: 0.75rem;
|
||
font-weight: 700;
|
||
padding: 0.125rem 0.5rem;
|
||
border-radius: 4px;
|
||
flex-shrink: 0;
|
||
}
|
||
.doc-title {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
color: var(--color-text);
|
||
margin: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.doc-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
.doc-meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.75rem;
|
||
font-size: 0.8125rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
```
|
||
|
||
### 3.2 Library Card
|
||
```html
|
||
<article class="lib-card" data-id="uuid">
|
||
<div class="lib-icon">📁</div>
|
||
<div class="lib-info">
|
||
<h3 class="lib-name">Backend Requirements</h3>
|
||
<span class="lib-count">12 documents · 3 sub-libraries</span>
|
||
</div>
|
||
<button class="btn-ghost lib-menu" aria-label="Library menu">⋯</button>
|
||
</article>
|
||
```
|
||
```css
|
||
.lib-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
}
|
||
.lib-card:hover { background: var(--color-hover); }
|
||
.lib-icon { font-size: 1.5rem; }
|
||
.lib-info { flex: 1; min-width: 0; }
|
||
.lib-name {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
margin: 0 0 0.25rem;
|
||
}
|
||
.lib-count { font-size: 0.8125rem; color: var(--color-text-muted); }
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Tags & Badges
|
||
|
||
### 4.1 Tag Pill (Clickable)
|
||
```html
|
||
<button class="tag-pill" data-tag="backend">backend</button>
|
||
```
|
||
```css
|
||
.tag-pill {
|
||
background: var(--color-accent-alpha);
|
||
color: var(--color-accent);
|
||
border: none;
|
||
padding: 0.25rem 0.625rem;
|
||
border-radius: 9999px;
|
||
font-size: 0.8125rem;
|
||
cursor: pointer;
|
||
transition: background 0.15s, transform 0.1s;
|
||
}
|
||
.tag-pill:hover {
|
||
background: var(--color-accent);
|
||
color: var(--color-bg);
|
||
transform: scale(1.05);
|
||
}
|
||
.tag-pill.active {
|
||
background: var(--color-accent);
|
||
color: var(--color-bg);
|
||
}
|
||
```
|
||
|
||
### 4.2 Status Badge
|
||
```html
|
||
<span class="status-badge status-draft">draft</span>
|
||
<span class="status-badge status-approved">approved</span>
|
||
<span class="status-badge status-implemented">implemented</span>
|
||
```
|
||
```css
|
||
.status-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
padding: 0.125rem 0.5rem;
|
||
border-radius: 4px;
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
}
|
||
.status-draft {
|
||
background: var(--color-warning-bg);
|
||
color: var(--color-warning);
|
||
}
|
||
.status-approved {
|
||
background: var(--color-info-bg);
|
||
color: var(--color-info);
|
||
}
|
||
.status-implemented {
|
||
background: var(--color-success-bg);
|
||
color: var(--color-success);
|
||
}
|
||
```
|
||
|
||
### 4.3 Priority Indicator
|
||
```html
|
||
<span class="priority priority-high" title="High priority">🔴</span>
|
||
<span class="priority priority-medium" title="Medium priority">🟡</span>
|
||
<span class="priority priority-low" title="Low priority">🟢</span>
|
||
```
|
||
```css
|
||
.priority { font-size: 0.875rem; }
|
||
```
|
||
|
||
### 4.4 Type Badge
|
||
```html
|
||
<span class="type-badge type-requirement">REQ</span>
|
||
<span class="type-badge type-note">NOTE</span>
|
||
<span class="type-badge type-spec">SPEC</span>
|
||
<span class="type-badge type-general">DOC</span>
|
||
```
|
||
```css
|
||
.type-badge {
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
padding: 0.125rem 0.375rem;
|
||
border-radius: 4px;
|
||
font-size: 0.6875rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
.type-requirement { border-color: var(--color-accent); color: var(--color-accent); }
|
||
.type-note { border-color: var(--color-info); color: var(--color-info); }
|
||
.type-spec { border-color: var(--color-purple); color: var(--color-purple); }
|
||
.type-general { border-color: var(--color-text-muted); color: var(--color-text-muted); }
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Navigation
|
||
|
||
### 5.1 Sidebar
|
||
```html
|
||
<aside class="sidebar">
|
||
<div class="sidebar-section">
|
||
<h4 class="sidebar-title">📁 Libraries</h4>
|
||
<nav class="library-tree">
|
||
<!-- Tree items -->
|
||
</nav>
|
||
</div>
|
||
<div class="sidebar-section">
|
||
<h4 class="sidebar-title">🏷️ Tags</h4>
|
||
<nav class="tag-list">
|
||
<!-- Tag items -->
|
||
</nav>
|
||
</div>
|
||
</aside>
|
||
```
|
||
```css
|
||
.sidebar {
|
||
width: 280px;
|
||
height: 100%;
|
||
background: var(--color-surface);
|
||
border-right: 1px solid var(--color-border);
|
||
overflow-y: auto;
|
||
padding: 1rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1.5rem;
|
||
}
|
||
.sidebar-section { display: flex; flex-direction: column; gap: 0.5rem; }
|
||
.sidebar-title {
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
color: var(--color-text-muted);
|
||
margin: 0;
|
||
}
|
||
@media (max-width: 768px) {
|
||
.sidebar {
|
||
display: none; /* Hidden on mobile, shown via bottom nav */
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.2 Library Tree Item
|
||
```html
|
||
<div class="tree-item tree-item-library" data-id="uuid">
|
||
<button class="tree-toggle" aria-expanded="true">▼</button>
|
||
<span class="tree-icon">📁</span>
|
||
<span class="tree-name">Backend Requirements</span>
|
||
<span class="tree-count">(12)</span>
|
||
</div>
|
||
<div class="tree-children" style="display: block;">
|
||
<!-- Nested items -->
|
||
</div>
|
||
```
|
||
```css
|
||
.tree-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
padding: 0.375rem 0.5rem;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 0.875rem;
|
||
color: var(--color-text);
|
||
}
|
||
.tree-item:hover { background: var(--color-hover); }
|
||
.tree-item.active { background: var(--color-accent-alpha); color: var(--color-accent); }
|
||
.tree-toggle {
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
cursor: pointer;
|
||
padding: 0;
|
||
font-size: 0.625rem;
|
||
width: 1rem;
|
||
transition: transform 0.15s;
|
||
}
|
||
.tree-toggle.collapsed { transform: rotate(-90deg); }
|
||
.tree-icon { font-size: 1rem; }
|
||
.tree-name { flex: 1; }
|
||
.tree-count { color: var(--color-text-muted); font-size: 0.75rem; }
|
||
.tree-children {
|
||
padding-left: 1.25rem;
|
||
border-left: 1px solid var(--color-border);
|
||
margin-left: 0.625rem;
|
||
}
|
||
```
|
||
|
||
### 5.3 Breadcrumbs
|
||
```html
|
||
<nav class="breadcrumbs" aria-label="Breadcrumb">
|
||
<a href="#" class="breadcrumb">Home</a>
|
||
<span class="breadcrumb-sep">/</span>
|
||
<a href="#" class="breadcrumb">Backend</a>
|
||
<span class="breadcrumb-sep">/</span>
|
||
<span class="breadcrumb-current">API</span>
|
||
</nav>
|
||
```
|
||
```css
|
||
.breadcrumbs {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
font-size: 0.875rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.breadcrumb {
|
||
color: var(--color-text-muted);
|
||
text-decoration: none;
|
||
}
|
||
.breadcrumb:hover { color: var(--color-accent); }
|
||
.breadcrumb-sep { color: var(--color-border); }
|
||
.breadcrumb-current {
|
||
color: var(--color-text);
|
||
font-weight: 500;
|
||
}
|
||
```
|
||
|
||
### 5.4 Bottom Navigation (Mobile)
|
||
```html
|
||
<nav class="bottom-nav">
|
||
<button class="bottom-nav-item active">
|
||
<svg><!-- home icon --></svg>
|
||
<span>Home</span>
|
||
</button>
|
||
<button class="bottom-nav-item">
|
||
<svg><!-- folder icon --></svg>
|
||
<span>Libraries</span>
|
||
</button>
|
||
<button class="bottom-nav-item bottom-nav-item-primary">
|
||
<svg><!-- plus icon --></svg>
|
||
</button>
|
||
<button class="bottom-nav-item">
|
||
<svg><!-- settings icon --></svg>
|
||
<span>Settings</span>
|
||
</button>
|
||
</nav>
|
||
```
|
||
```css
|
||
.bottom-nav {
|
||
display: none;
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: var(--color-surface);
|
||
border-top: 1px solid var(--color-border);
|
||
padding: 0.5rem 1rem;
|
||
justify-content: space-around;
|
||
z-index: 100;
|
||
}
|
||
@media (max-width: 768px) {
|
||
.bottom-nav { display: flex; }
|
||
}
|
||
.bottom-nav-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
padding: 0.5rem;
|
||
cursor: pointer;
|
||
font-size: 0.6875rem;
|
||
}
|
||
.bottom-nav-item.active { color: var(--color-accent); }
|
||
.bottom-nav-item svg { width: 1.5rem; height: 1.5rem; }
|
||
.bottom-nav-item-primary {
|
||
background: var(--color-accent);
|
||
color: var(--color-bg);
|
||
border-radius: 50%;
|
||
width: 3rem;
|
||
height: 3rem;
|
||
margin-top: -1rem;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||
}
|
||
.bottom-nav-item-primary svg { width: 1.75rem; height: 1.75rem; }
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Modals
|
||
|
||
### 6.1 Modal Base
|
||
```html
|
||
<div class="modal-overlay" id="modal-confirm-delete">
|
||
<div class="modal" role="dialog" aria-modal="true">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">⚠️ Confirm Delete</h3>
|
||
<button class="btn-ghost modal-close" aria-label="Close">✕</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>Delete "Document Title"?</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary modal-cancel">Cancel</button>
|
||
<button class="btn btn-danger modal-confirm">Delete</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
```css
|
||
.modal-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.6);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
padding: 1rem;
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transition: opacity 0.2s, visibility 0.2s;
|
||
}
|
||
.modal-overlay.active {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
}
|
||
.modal {
|
||
background: var(--color-surface);
|
||
border-radius: 12px;
|
||
width: 100%;
|
||
max-width: 400px;
|
||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||
transform: scale(0.95);
|
||
transition: transform 0.2s;
|
||
}
|
||
.modal-overlay.active .modal { transform: scale(1); }
|
||
.modal-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 1rem 1.25rem;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
.modal-title { margin: 0; font-size: 1.125rem; }
|
||
.modal-body { padding: 1.25rem; }
|
||
.modal-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 0.75rem;
|
||
padding: 1rem 1.25rem;
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Feedback
|
||
|
||
### 7.1 Toast Notification
|
||
```html
|
||
<div class="toast toast-success" role="alert">
|
||
<span class="toast-icon">✓</span>
|
||
<span class="toast-message">Document saved</span>
|
||
<button class="toast-close" aria-label="Dismiss">✕</button>
|
||
</div>
|
||
```
|
||
```css
|
||
.toast {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 8px;
|
||
padding: 0.875rem 1rem;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||
min-width: 280px;
|
||
max-width: 400px;
|
||
animation: slideIn 0.2s ease-out;
|
||
}
|
||
@keyframes slideIn {
|
||
from { transform: translateX(100%); opacity: 0; }
|
||
to { transform: translateX(0); opacity: 1; }
|
||
}
|
||
.toast-success { border-left: 4px solid var(--color-success); }
|
||
.toast-error { border-left: 4px solid var(--color-danger); }
|
||
.toast-info { border-left: 4px solid var(--color-info); }
|
||
.toast-icon { font-size: 1.125rem; }
|
||
.toast-message { flex: 1; }
|
||
.toast-close {
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
cursor: pointer;
|
||
padding: 0.25rem;
|
||
}
|
||
.toast-close:hover { color: var(--color-text); }
|
||
```
|
||
|
||
### 7.2 Loading Spinner
|
||
```html
|
||
<div class="spinner" role="status">
|
||
<span class="sr-only">Loading...</span>
|
||
</div>
|
||
```
|
||
```css
|
||
.spinner {
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
border: 2px solid var(--color-border);
|
||
border-top-color: var(--color-accent);
|
||
border-radius: 50%;
|
||
animation: spin 0.8s linear infinite;
|
||
}
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
.sr-only {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: -1px;
|
||
overflow: hidden;
|
||
clip: rect(0,0,0,0);
|
||
border: 0;
|
||
}
|
||
```
|
||
|
||
### 7.3 Empty State
|
||
```html
|
||
<div class="empty-state">
|
||
<div class="empty-icon">📄</div>
|
||
<h3 class="empty-title">No documents yet</h3>
|
||
<p class="empty-description">Create your first document to get started</p>
|
||
<button class="btn btn-primary">Create Document</button>
|
||
</div>
|
||
```
|
||
```css
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
padding: 3rem 1.5rem;
|
||
gap: 1rem;
|
||
}
|
||
.empty-icon { font-size: 3rem; opacity: 0.5; }
|
||
.empty-title { margin: 0; font-size: 1.25rem; color: var(--color-text); }
|
||
.empty-description { margin: 0; color: var(--color-text-muted); }
|
||
```
|
||
|
||
### 7.4 Skeleton Loader
|
||
```html
|
||
<div class="skeleton-card">
|
||
<div class="skeleton skeleton-badge"></div>
|
||
<div class="skeleton skeleton-title"></div>
|
||
<div class="skeleton skeleton-tags"></div>
|
||
<div class="skeleton skeleton-meta"></div>
|
||
</div>
|
||
```
|
||
```css
|
||
.skeleton {
|
||
background: linear-gradient(
|
||
90deg,
|
||
var(--color-border) 25%,
|
||
var(--color-hover) 50%,
|
||
var(--color-border) 75%
|
||
);
|
||
background-size: 200% 100%;
|
||
animation: shimmer 1.5s infinite;
|
||
border-radius: 4px;
|
||
}
|
||
@keyframes shimmer {
|
||
0% { background-position: 200% 0; }
|
||
100% { background-position: -200% 0; }
|
||
}
|
||
.skeleton-badge { width: 3rem; height: 1.25rem; }
|
||
.skeleton-title { width: 70%; height: 1.5rem; }
|
||
.skeleton-tags { width: 50%; height: 1.25rem; }
|
||
.skeleton-meta { width: 40%; height: 1rem; }
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Layout
|
||
|
||
### 8.1 App Layout
|
||
```html
|
||
<div class="app-layout">
|
||
<header class="app-header">...</header>
|
||
<div class="app-body">
|
||
<aside class="sidebar">...</aside>
|
||
<main class="main-content">...</main>
|
||
</div>
|
||
</div>
|
||
```
|
||
```css
|
||
.app-layout {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
.app-body {
|
||
display: flex;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
}
|
||
.main-content {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 1.5rem;
|
||
background: var(--color-bg);
|
||
}
|
||
@media (max-width: 768px) {
|
||
.main-content {
|
||
padding: 1rem;
|
||
padding-bottom: 5rem; /* space for bottom nav */
|
||
}
|
||
}
|
||
```
|
||
|
||
### 8.2 Toolbar
|
||
```html
|
||
<div class="toolbar">
|
||
<div class="toolbar-left">
|
||
<h2 class="toolbar-title">Documents</h2>
|
||
<span class="toolbar-count">42 documents</span>
|
||
</div>
|
||
<div class="toolbar-right">
|
||
<button class="btn btn-icon-label">
|
||
<svg><!-- plus icon --></svg>
|
||
<span>New Doc</span>
|
||
</button>
|
||
<button class="btn btn-icon-label">
|
||
<svg><!-- folder-plus icon --></svg>
|
||
<span>New Lib</span>
|
||
</button>
|
||
<div class="toolbar-divider"></div>
|
||
<button class="btn-ghost" aria-label="Grid view">⊞</button>
|
||
<button class="btn-ghost" aria-label="List view">≡</button>
|
||
</div>
|
||
</div>
|
||
```
|
||
```css
|
||
.toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.toolbar-left { display: flex; align-items: baseline; gap: 0.75rem; }
|
||
.toolbar-title { margin: 0; font-size: 1.5rem; font-weight: 600; }
|
||
.toolbar-count { color: var(--color-text-muted); font-size: 0.875rem; }
|
||
.toolbar-right { display: flex; align-items: center; gap: 0.5rem; }
|
||
.toolbar-divider { width: 1px; height: 1.5rem; background: var(--color-border); margin: 0 0.5rem; }
|
||
@media (max-width: 768px) {
|
||
.toolbar-right .btn span { display: none; }
|
||
}
|
||
```
|
||
|
||
### 8.3 Split Pane (Editor + Preview)
|
||
```html
|
||
<div class="split-pane">
|
||
<div class="split-editor">
|
||
<textarea class="textarea-editor">...</textarea>
|
||
</div>
|
||
<div class="split-resize-handle"></div>
|
||
<div class="split-preview">
|
||
<!-- Rendered markdown -->
|
||
</div>
|
||
</div>
|
||
```
|
||
```css
|
||
.split-pane {
|
||
display: flex;
|
||
height: 100%;
|
||
gap: 1px;
|
||
background: var(--color-border);
|
||
}
|
||
.split-editor,
|
||
.split-preview {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
background: var(--color-bg);
|
||
}
|
||
.split-resize-handle {
|
||
width: 4px;
|
||
background: var(--color-border);
|
||
cursor: col-resize;
|
||
}
|
||
.split-resize-handle:hover { background: var(--color-accent); }
|
||
@media (max-width: 768px) {
|
||
.split-pane { flex-direction: column; }
|
||
.split-resize-handle {
|
||
width: 100%;
|
||
height: 4px;
|
||
cursor: row-resize;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Form Components
|
||
|
||
### 9.1 Metadata Panel
|
||
```html
|
||
<aside class="metadata-panel">
|
||
<div class="meta-section">
|
||
<h4 class="meta-title">Details</h4>
|
||
<div class="meta-row">
|
||
<span class="meta-label">Type</span>
|
||
<select class="select">...</select>
|
||
</div>
|
||
<div class="meta-row">
|
||
<span class="meta-label">Status</span>
|
||
<select class="select">...</select>
|
||
</div>
|
||
<div class="meta-row">
|
||
<span class="meta-label">Priority</span>
|
||
<select class="select">...</select>
|
||
</div>
|
||
</div>
|
||
<div class="meta-section">
|
||
<h4 class="meta-title">Tags</h4>
|
||
<div class="tags-input">...</div>
|
||
</div>
|
||
<div class="meta-section">
|
||
<h4 class="meta-title">Timestamps</h4>
|
||
<div class="meta-row">
|
||
<span class="meta-label">Created</span>
|
||
<span class="meta-value">Mar 28, 2026 10:00</span>
|
||
</div>
|
||
<div class="meta-row">
|
||
<span class="meta-label">Updated</span>
|
||
<span class="meta-value">Mar 28, 2026 12:30</span>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
```
|
||
```css
|
||
.metadata-panel {
|
||
width: 280px;
|
||
background: var(--color-surface);
|
||
border-left: 1px solid var(--color-border);
|
||
padding: 1rem;
|
||
overflow-y: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
.meta-section { display: flex; flex-direction: column; gap: 0.75rem; }
|
||
.meta-title {
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
color: var(--color-text-muted);
|
||
margin: 0;
|
||
}
|
||
.meta-row {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
}
|
||
.meta-label {
|
||
font-size: 0.8125rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
.meta-value {
|
||
font-size: 0.875rem;
|
||
color: var(--color-text);
|
||
}
|
||
@media (max-width: 1024px) {
|
||
.metadata-panel {
|
||
width: 100%;
|
||
border-left: none;
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Utility Classes
|
||
|
||
```css
|
||
/* Text utilities */
|
||
.text-muted { color: var(--color-text-muted); }
|
||
.text-accent { color: var(--color-accent); }
|
||
.text-danger { color: var(--color-danger); }
|
||
.text-success { color: var(--color-success); }
|
||
.text-sm { font-size: 0.875rem; }
|
||
.text-xs { font-size: 0.75rem; }
|
||
.font-mono { font-family: var(--font-mono); }
|
||
.font-bold { font-weight: 600; }
|
||
|
||
/* Spacing */
|
||
.gap-1 { gap: 0.25rem; }
|
||
.gap-2 { gap: 0.5rem; }
|
||
.gap-3 { gap: 0.75rem; }
|
||
.gap-4 { gap: 1rem; }
|
||
.p-2 { padding: 0.5rem; }
|
||
.p-4 { padding: 1rem; }
|
||
.mt-2 { margin-top: 0.5rem; }
|
||
.mt-4 { margin-top: 1rem; }
|
||
.mb-2 { margin-bottom: 0.5rem; }
|
||
.mb-4 { margin-bottom: 1rem; }
|
||
|
||
/* |