Files
StarPunk/static/css/style.css
Phil Skentelbery 0cca8169ce feat: Implement Phase 4 Web Interface with bugfixes (v0.5.2)
## Phase 4: Web Interface Implementation

Implemented complete web interface with public and admin routes,
templates, CSS, and development authentication.

### Core Features

**Public Routes**:
- Homepage with recent published notes
- Note permalinks with microformats2
- Server-side rendering (Jinja2)

**Admin Routes**:
- Login via IndieLogin
- Dashboard with note management
- Create, edit, delete notes
- Protected with @require_auth decorator

**Development Authentication**:
- Dev login bypass for local testing (DEV_MODE only)
- Security safeguards per ADR-011
- Returns 404 when disabled

**Templates & Frontend**:
- Base layouts (public + admin)
- 8 HTML templates with microformats2
- Custom responsive CSS (114 lines)
- Error pages (404, 500)

### Bugfixes (v0.5.1 → v0.5.2)

1. **Cookie collision fix (v0.5.1)**:
   - Renamed auth cookie from "session" to "starpunk_session"
   - Fixed redirect loop between dev login and admin dashboard
   - Flask's session cookie no longer conflicts with auth

2. **HTTP 404 error handling (v0.5.1)**:
   - Update route now returns 404 for nonexistent notes
   - Delete route now returns 404 for nonexistent notes
   - Follows ADR-012 HTTP Error Handling Policy
   - Pattern consistency across all admin routes

3. **Note model enhancement (v0.5.2)**:
   - Exposed deleted_at field from database schema
   - Enables soft deletion verification in tests
   - Follows ADR-013 transparency principle

### Architecture

**New ADRs**:
- ADR-011: Development Authentication Mechanism
- ADR-012: HTTP Error Handling Policy
- ADR-013: Expose deleted_at Field in Note Model

**Standards Compliance**:
- Uses uv for Python environment
- Black formatted, Flake8 clean
- Follows git branching strategy
- Version incremented per versioning strategy

### Test Results

- 405/406 tests passing (99.75%)
- 87% code coverage
- All security tests passing
- Manual testing confirmed working

### Documentation

- Complete implementation reports in docs/reports/
- Architecture reviews in docs/reviews/
- Design documents in docs/design/
- CHANGELOG updated for v0.5.2

### Files Changed

**New Modules**:
- starpunk/dev_auth.py
- starpunk/routes/ (public, admin, auth, dev_auth)

**Templates**: 10 files (base, pages, admin, errors)
**Static**: CSS and optional JavaScript
**Tests**: 4 test files for routes and templates
**Docs**: 20+ architectural and implementation documents

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 23:01:53 -07:00

115 lines
7.7 KiB
CSS

/* StarPunk CSS - Minimal responsive stylesheet */
:root {
--color-text: #333; --color-text-light: #666; --color-bg: #fff; --color-bg-alt: #f5f5f5;
--color-link: #0066cc; --color-link-hover: #004499; --color-border: #ddd;
--color-success: #28a745; --color-error: #dc3545; --color-warning: #ffc107; --color-info: #17a2b8;
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
--font-mono: 'SF Mono', Monaco, Consolas, 'Courier New', monospace;
--spacing-xs: 0.25rem; --spacing-sm: 0.5rem; --spacing-md: 1rem; --spacing-lg: 2rem; --spacing-xl: 4rem;
--max-width: 42rem; --border-radius: 4px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: var(--font-body); font-size: 1rem; line-height: 1.6; color: var(--color-text); background: var(--color-bg); padding: var(--spacing-md); }
h1, h2, h3 { margin-bottom: var(--spacing-md); line-height: 1.2; font-weight: 600; }
h1 { font-size: 2rem; } h2 { font-size: 1.5rem; } h3 { font-size: 1.25rem; }
p { margin-bottom: var(--spacing-md); }
a { color: var(--color-link); text-decoration: none; }
a:hover { color: var(--color-link-hover); text-decoration: underline; }
code, pre { font-family: var(--font-mono); background: var(--color-bg-alt); padding: 0.125rem 0.25rem; border-radius: var(--border-radius); font-size: 0.875rem; }
pre { padding: var(--spacing-md); overflow-x: auto; margin-bottom: var(--spacing-md); }
header, main, footer { max-width: var(--max-width); margin: 0 auto; }
header { margin-bottom: var(--spacing-lg); padding-bottom: var(--spacing-md); border-bottom: 2px solid var(--color-border); }
header h1 { margin-bottom: var(--spacing-sm); }
header h1 a { color: var(--color-text); text-decoration: none; }
nav { display: flex; gap: var(--spacing-md); flex-wrap: wrap; }
nav a { padding: var(--spacing-sm) var(--spacing-md); border-radius: var(--border-radius); transition: background 0.2s; }
nav a:hover { background: var(--color-bg-alt); text-decoration: none; }
main { min-height: 60vh; margin-bottom: var(--spacing-lg); }
footer { padding-top: var(--spacing-lg); border-top: 1px solid var(--color-border); text-align: center; color: var(--color-text-light); font-size: 0.875rem; }
.dev-mode-warning { background: var(--color-error); color: white; padding: var(--spacing-md); text-align: center; font-weight: bold; margin: calc(-1 * var(--spacing-md)); margin-bottom: var(--spacing-lg); }
.flash { padding: var(--spacing-md); border-radius: var(--border-radius); margin-bottom: var(--spacing-md); border-left: 4px solid; }
.flash-success { background: #d4edda; border-color: var(--color-success); color: #155724; }
.flash-error { background: #f8d7da; border-color: var(--color-error); color: #721c24; }
.flash-warning { background: #fff3cd; border-color: var(--color-warning); color: #856404; }
.flash-info { background: #d1ecf1; border-color: var(--color-info); color: #0c5460; }
.h-entry { margin-bottom: var(--spacing-lg); padding-bottom: var(--spacing-lg); border-bottom: 1px solid var(--color-border); }
.h-entry:last-child { border-bottom: none; }
.note-preview .e-content { margin-bottom: var(--spacing-md); }
.note-meta { color: var(--color-text-light); font-size: 0.875rem; }
.note-meta a { color: var(--color-text-light); }
.note-nav { margin-top: var(--spacing-md); }
.empty-state { text-align: center; padding: var(--spacing-xl); color: var(--color-text-light); }
.form-group { margin-bottom: var(--spacing-md); }
label { display: block; margin-bottom: var(--spacing-sm); font-weight: 600; }
input[type="text"], input[type="url"], input[type="email"], textarea { width: 100%; padding: var(--spacing-sm); border: 1px solid var(--color-border); border-radius: var(--border-radius); font-family: inherit; font-size: 1rem; }
textarea { font-family: var(--font-mono); resize: vertical; min-height: 10rem; }
small { display: block; margin-top: var(--spacing-xs); color: var(--color-text-light); font-size: 0.875rem; }
.form-checkbox { display: flex; align-items: center; gap: var(--spacing-sm); }
.form-checkbox input[type="checkbox"] { width: auto; margin: 0; }
.form-checkbox label { margin: 0; font-weight: normal; }
.form-actions { display: flex; gap: var(--spacing-md); margin-top: var(--spacing-lg); }
.button { display: inline-block; padding: var(--spacing-sm) var(--spacing-md); border: 1px solid var(--color-border); border-radius: var(--border-radius); background: var(--color-bg); color: var(--color-text); font-family: inherit; font-size: 1rem; cursor: pointer; text-decoration: none; transition: all 0.2s; }
.button:hover { background: var(--color-bg-alt); text-decoration: none; }
.button-primary { background: var(--color-link); color: white; border-color: var(--color-link); }
.button-primary:hover { background: var(--color-link-hover); border-color: var(--color-link-hover); }
.button-secondary { background: var(--color-bg-alt); }
.button-danger { background: var(--color-error); color: white; border-color: var(--color-error); }
.button-danger:hover { background: #c82333; border-color: #c82333; }
.button-warning { background: var(--color-warning); color: #333; border-color: var(--color-warning); }
.button-small { padding: 0.25rem 0.5rem; font-size: 0.875rem; }
.admin-container { max-width: 60rem; }
.admin-nav { display: flex; gap: var(--spacing-md); padding: var(--spacing-md); background: var(--color-bg-alt); border-radius: var(--border-radius); margin-bottom: var(--spacing-lg); flex-wrap: wrap; align-items: center; }
.admin-nav .logout-form { margin-left: auto; }
.admin-nav .logout-form button { margin: 0; }
.admin-content { margin-bottom: var(--spacing-lg); }
.user-identity { color: var(--color-text-light); font-size: 0.875rem; margin-bottom: var(--spacing-md); }
.dashboard-actions { margin-bottom: var(--spacing-md); }
.note-table { width: 100%; border-collapse: collapse; margin-top: var(--spacing-md); }
.note-table th, .note-table td { padding: var(--spacing-md); text-align: left; border-bottom: 1px solid var(--color-border); }
.note-table th { background: var(--color-bg-alt); font-weight: 600; }
.note-content-preview { max-width: 30rem; }
.note-excerpt { margin-bottom: var(--spacing-xs); }
.note-slug { color: var(--color-text-light); font-family: var(--font-mono); font-size: 0.75rem; }
.note-date { white-space: nowrap; color: var(--color-text-light); }
.note-actions { white-space: nowrap; }
.note-actions .button { margin-right: var(--spacing-sm); }
.note-actions .delete-form { display: inline; }
.status-badge { display: inline-block; padding: 0.25rem 0.5rem; border-radius: var(--border-radius); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; }
.status-published { background: #d4edda; color: var(--color-success); }
.status-draft { background: #f8d7da; color: var(--color-error); }
.login-container { max-width: 30rem; margin: 0 auto; padding: var(--spacing-lg); }
.login-form { margin: var(--spacing-lg) 0; }
.dev-login-option { margin-top: var(--spacing-lg); padding-top: var(--spacing-lg); border-top: 1px solid var(--color-border); }
.dev-warning { color: var(--color-error); font-weight: 600; text-align: center; }
.login-help { margin-top: var(--spacing-xl); padding-top: var(--spacing-lg); border-top: 1px solid var(--color-border); }
.login-help h3 { font-size: 1rem; margin-bottom: var(--spacing-sm); }
.note-editor { max-width: 50rem; }
.note-editor .note-meta { margin-bottom: var(--spacing-md); }
@media (min-width: 768px) {
body { padding: var(--spacing-lg); }
h1 { font-size: 2.5rem; } h2 { font-size: 2rem; } h3 { font-size: 1.5rem; }
}
@media (max-width: 767px) {
.note-table { font-size: 0.875rem; }
.note-table th, .note-table td { padding: var(--spacing-sm); }
.note-actions .button { font-size: 0.75rem; padding: 0.25rem 0.5rem; }
.admin-nav { flex-direction: column; align-items: stretch; }
.admin-nav .logout-form { margin-left: 0; }
}