Implemented a three-layer theming architecture to standardize admin component styling and prepare for future dark mode support. **Architecture:** - Layer 1: Base colors ($gray-80, $red-60) in variables.scss - Layer 2: Semantic SCSS variables ($input-bg, $error-bg) in variables.scss - Layer 3: CSS custom properties (--input-bg, --error-bg) in themes.scss **New semantic variables (~30 added):** - Inputs & forms (bg, hover, focus, text, border states) - State messages (error, success, warning with bg/text/border) - Empty states (text, heading colors) - Cards, dropdowns, popovers, modals (bg, border, shadow) **New reusable components:** - EmptyState.svelte - Supports icon and action snippets - ErrorMessage.svelte - Supports dismissible errors **Pages refactored:** - /admin/projects - Uses EmptyState and ErrorMessage (~30 lines removed) - /admin/posts - Uses EmptyState and ErrorMessage with icon (~30 lines removed) **Benefits:** - 60+ lines of duplicate styles removed (just 2 pages) - Future dark mode = remap CSS variables in themes.scss only - Guaranteed visual consistency for errors and empty states - $unit-based spacing system enforced **Remaining work (Phase 2):** - Replace hardcoded colors in ~40 files - Fix hardcoded spacing in ~20 files - Expand EmptyState/ErrorMessage to remaining pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
8 KiB
Task 7: Styling & Theming Harmonization Plan
Status: 🚧 IN PROGRESS
Architecture Overview
Three-layer system for future theming:
- Base colors (
variables.scss):$gray-80,$red-60, etc. - Semantic SCSS variables (
variables.scss):$input-bg: $gray-90,$error-bg: rgba($red-60, 0.1) - CSS custom properties (
themes.scss):--input-bg: #{$input-bg}(ready for dark mode)
Component usage: Components import variables.scss and use SCSS variables (background: $input-bg)
Future dark mode: Remap CSS custom properties in [data-theme='dark'] block without touching components
Current State (Audit Results)
Hardcoded Values Found:
- 18 hardcoded
padding: Xpxvalues - 2 hardcoded
margin: Xpxvalues - 91
rgba()color definitions - 127 hex color values (
#xxx)
Existing Foundation (Good):
- ✅ $unit system (8px base with $unit-half, $unit-2x, etc.)
- ✅ Color scales ($gray-00 through $gray-100, etc.)
- ✅ Some semantic variables ($bg-color, $text-color, $accent-color)
- ✅ themes.scss already maps SCSS → CSS variables
Implementation Plan
Step 1: Add Semantic SCSS Variables to variables.scss
Add component-specific semantic mappings (SCSS only, no double dashes):
/* Component-Specific Semantic Colors
* These map base colors to component usage
* Will be exposed as CSS custom properties in themes.scss
* -------------------------------------------------------------------------- */
// Inputs & Forms
$input-bg: $gray-90;
$input-bg-hover: $gray-85;
$input-bg-focus: $white;
$input-text: $gray-20;
$input-text-hover: $gray-10;
$input-border: $gray-80;
$input-border-focus: $blue-40;
// States (errors, success, warnings)
$error-bg: rgba($red-60, 0.1);
$error-text: $red-error; // Already defined as #dc2626
$error-border: rgba($red-60, 0.2);
$success-bg: rgba($green-40, 0.1);
$success-text: $green-30;
$success-border: rgba($green-40, 0.2);
$warning-bg: rgba($yellow-50, 0.1);
$warning-text: $yellow-10;
$warning-border: rgba($yellow-50, 0.2);
// Empty states
$empty-state-text: $gray-40;
$empty-state-heading: $gray-20;
// Cards & Containers
$card-bg: $white;
$card-border: $gray-80;
$card-shadow: rgba($black, 0.08);
$card-shadow-hover: rgba($black, 0.12);
// Dropdowns & Popovers
$dropdown-bg: $white;
$dropdown-border: $gray-80;
$dropdown-shadow: rgba($black, 0.12);
$dropdown-item-hover: $gray-95;
// Modals
$modal-overlay: rgba($black, 0.5);
$modal-bg: $white;
$modal-shadow: rgba($black, 0.15);
Step 2: Map to CSS Custom Properties in themes.scss
Extend existing themes.scss with new mappings:
:root {
// Existing mappings
--bg-color: #{$gray-80};
--page-color: #{$gray-100};
--card-color: #{$gray-90};
--mention-bg-color: #{$gray-90};
--text-color: #{$gray-20};
// New semantic mappings
--input-bg: #{$input-bg};
--input-bg-hover: #{$input-bg-hover};
--input-bg-focus: #{$input-bg-focus};
--input-text: #{$input-text};
--input-border: #{$input-border};
--error-bg: #{$error-bg};
--error-text: #{$error-text};
--error-border: #{$error-border};
--success-bg: #{$success-bg};
--success-text: #{$success-text};
--empty-state-text: #{$empty-state-text};
--empty-state-heading: #{$empty-state-heading};
--card-bg: #{$card-bg};
--card-border: #{$card-border};
--card-shadow: #{$card-shadow};
--dropdown-bg: #{$dropdown-bg};
--dropdown-shadow: #{$dropdown-shadow};
// ... etc
}
[data-theme='dark'] {
// Future: remap for dark mode without touching component code
// --input-bg: #{$dark-input-bg};
// --card-bg: #{$dark-card-bg};
}
Step 3: Fix Hardcoded Spacing (Use $unit System)
Replace hardcoded px values with $unit-based values:
// ❌ Before
padding: 24px;
margin: 12px 16px;
border-radius: 6px;
// ✅ After
padding: $unit-3x; // 24px = 8px * 3
margin: calc($unit * 1.5) $unit-2x; // 12px 16px
border-radius: $corner-radius-sm; // Already defined as 6px
Files to update: ~20 files with hardcoded spacing
Step 4: Replace Hardcoded Colors (Use Semantic SCSS)
Replace inline rgba/hex with semantic SCSS variables:
// ❌ Before
.error {
background: rgba(239, 68, 68, 0.1);
color: #dc2626;
border: 1px solid rgba(239, 68, 68, 0.2);
}
// ✅ After
.error {
background: $error-bg;
color: $error-text;
border: $unit-1px solid $error-border;
}
Files to update: 40 files with hardcoded colors
Step 5: Extract Reusable Components
A. EmptyState.svelte (~10 usages)
<script lang="ts">
import type { Snippet } from 'svelte'
interface Props {
title: string
message: string
icon?: Snippet
action?: Snippet
}
let { title, message, icon, action }: Props = $props()
</script>
<div class="empty-state">
{#if icon}
<div class="empty-icon">{@render icon()}</div>
{/if}
<h3>{title}</h3>
<p>{message}</p>
{#if action}
<div class="empty-action">{@render action()}</div>
{/if}
</div>
<style lang="scss">
@import '$styles/variables.scss';
.empty-state {
text-align: center;
padding: $unit-8x $unit-4x;
color: $empty-state-text;
h3 {
font-size: calc($unit * 2.5); // 20px
font-weight: 600;
margin: 0 0 $unit-2x;
color: $empty-state-heading;
}
p {
margin: 0;
line-height: 1.5;
}
.empty-action {
margin-top: $unit-3x;
}
}
</style>
B. ErrorMessage.svelte (~4 usages)
<script lang="ts">
interface Props {
message: string
dismissible?: boolean
onDismiss?: () => void
}
let { message, dismissible = false, onDismiss }: Props = $props()
</script>
<div class="error-message">
<span class="error-text">{message}</span>
{#if dismissible && onDismiss}
<button type="button" class="dismiss-btn" onclick={onDismiss}>×</button>
{/if}
</div>
<style lang="scss">
@import '$styles/variables.scss';
.error-message {
background: $error-bg;
color: $error-text;
padding: $unit-3x;
border-radius: $unit-2x;
border: $unit-1px solid $error-border;
text-align: center;
margin-bottom: $unit-4x;
display: flex;
align-items: center;
justify-content: center;
gap: $unit-2x;
.error-text {
flex: 1;
}
.dismiss-btn {
background: none;
border: none;
color: $error-text;
font-size: calc($unit * 3);
cursor: pointer;
padding: 0;
line-height: 1;
opacity: 0.6;
&:hover {
opacity: 1;
}
}
}
</style>
Step 6: Documentation
Create docs/task-7-styling-harmonization-completion.md with:
- Architecture explanation (3-layer system)
- Semantic variable naming conventions
- How to add new semantic mappings
- Component usage patterns
- Future dark mode approach
Implementation Order
- Add semantic SCSS variables to
variables.scss(~30 new variables) - Map to CSS custom properties in
themes.scss(~30 new mappings) - Fix spacing in high-impact files (projects/posts pages, forms, modals)
- Replace hardcoded colors with semantic SCSS variables
- Create EmptyState component and replace ~10 usages
- Create ErrorMessage component and replace ~4 usages
- Document approach in task-7 completion doc
- Update admin modernization plan to mark Task 7 complete
Success Criteria
- ~30 semantic SCSS variables added to variables.scss
- ~30 CSS custom properties mapped in themes.scss
- All hardcoded spacing uses $unit system (20 files)
- All colors use semantic SCSS variables (40 files)
- EmptyState component created and integrated (10 usages)
- ErrorMessage component created and integrated (4 usages)
- No rgba() or hex in admin components (use SCSS variables)
- Documentation complete
- Build passes, manual QA complete
Benefits
✅ Theme-ready: Dark mode = remap CSS vars in themes.scss only ✅ Maintainability: Change semantic variable once, updates everywhere ✅ Consistency: All empty states/errors look identical ✅ DX: Autocomplete for semantic variable names ✅ Reduced duplication: ~200-300 lines of styles removed