17 KiB
PROJECT: Aether App — Comprehensive Style Review
Status: Phase 1 & 2 Complete — Phase 3 Deferred (post-April 2026 conference)
Priority: Medium
Created: 2026-03-13
Updated: 2026-03-16
Related: src/app.css, src/routes/+layout.svelte, documentation/AE__UI_Component_Patterns.md
1. Objective
Audit and unify the visual design system across all Aether modules. The goal is consistent:
- Color token usage (Skeleton
preset-*/surface-*/ semantic tokens) - Button and interactive element styling
- Dark mode handling
- Typography hierarchy
- Layout patterns (cards, lists, tables, modals, banners)
- Icon system (Lucide only)
This review covers all modules and their distinct use cases. Changes must be sequenced to avoid breaking live-production systems (Events Launcher, IDAA).
Scope of Modules
| Module | Routes | Primary Users | Notes |
|---|---|---|---|
| Core / App Shell | +layout.svelte, e_app_sys_bar.svelte |
All users | Foundation — impacts everything |
| Core Admin | /core/ |
OSIT staff / managers | Manager-only section |
| Journals | /journals/ |
Authenticated users | Canonical/frontier model |
| Events — General | /events/, /events/[id]/ |
Event staff, attendees | Hub page |
| Events — Pres Mgmt | /events/[id]/(pres_mgmt)/ |
Event coordinators | Operations tool |
| Events — Launcher | /events/[id]/(launcher)/ |
AV/tech staff (Electron kiosk) | ⚠️ Live production — April 2026 conference |
| Events — Badges | /events/[id]/(badges)/ |
Registration desk staff | On-site kiosk use |
| Events — Leads | /events/[id]/(leads)/ |
Exhibitor booth staff | On-site kiosk use |
| IDAA | /idaa/ |
IDAA members (iframe) | ⚠️ Privacy-critical, iframe context |
2. Current State: Dual-Generation Problem
The codebase has two parallel style generations that co-exist:
| Era | Pattern | Where Used |
|---|---|---|
| Modern | preset-tonal-*, preset-filled-*, Lucide icons, Svelte 5 runes |
sys_bar, Journals, IDAA dashboard |
| Legacy | variant-soft-*, variant-filled-*, FontAwesome fas fa-* icons |
Events routes (most), some Core |
The Journals module is the canonical reference for modern patterns — when in doubt, match it.
3. Color Token Architecture
Three overlapping systems are in use. The goal is to consolidate to two:
✅ System 1: Skeleton Semantic Tokens (keep, expand usage)
Used for colored/semantic elements.
primary, secondary, tertiary, success, warning, error, surface
Usage: preset-tonal-primary, bg-primary-500/10, text-error-500, border-warning-500
✅ System 2: Plain Tailwind Grayscale (keep for neutrals)
Used for neutral backgrounds, borders, and text. Predictable across all Skeleton themes.
gray-50/100/200/300/400/500/600/700/800/900
Usage: bg-gray-50 dark:bg-gray-900, border-gray-200 dark:border-gray-700
❌ System 3: Hardcoded RGB/HSL (eliminate)
bg-orange-600/90 — root layout banner
hsla(0, 100%, 50%, .5) — journal entry eye icon
rgb(243 244 246) — form dark mode hacks in <style> blocks
These are brittle, not theme-aware, and create maintenance debt.
Decision Rule
Semantic color needed? → Use Skeleton token (
preset-*,text-primary-*, etc.) Neutral background/border/text? → Usegray-*withdark:pair Hardcoded color? → Replace with token. No exceptions.
4. Dark Mode Architecture
Current Setup (already correct in app.css)
@custom-variant dark (&:where(.dark, .dark *)); /* Tailwind v4 class-based dark mode */
html.dark { color-scheme: dark; } /* Native controls follow app theme */
html.light { color-scheme: light; }
Gap: Skeleton Form Classes Lack Dark Mode
Skeleton's .input, .select, .textarea classes do not include dark mode styles. This causes white text on white backgrounds in dark mode. Currently patched with inline <style> blocks per-component (see e_app_sys_bar.svelte lines 693–707).
Fix: Global utility in app.css (see Phase 1, Step 1). Once added, remove per-component patches.
5. Button System
Standard: preset-* (Skeleton v4 pattern) ✅
<button class="btn preset-tonal-secondary">Secondary</button>
<button class="btn btn-sm preset-filled-primary">Primary</button>
<button class="btn preset-outlined-surface">Outlined</button>
Note: All legacy
variant-*classes have been fully removed from the codebase. Use onlypreset-*classes for all buttons and interactive elements.
Custom ae_btn_* Classes (app.css)
These exist in app.css and wrap the preset-* system. They are valid but underused. Consider adopting where button groups need reuse.
6. Icon System
Standard: Lucide (@lucide/svelte) — SVG, tree-shakeable, consistent stroke weight
Legacy: FontAwesome (fas fa-* / far fa-*) — CSS class-based, heavier
FontAwesome is still imported (likely via global CSS). Goal: complete removal from all new work; migrate existing usage to Lucide progressively.
FontAwesome → Lucide Reference Map (events module)
| FontAwesome | Lucide Component | Notes |
|---|---|---|
fa-cogs |
Settings |
Settings/config |
fa-chart-line |
TrendingUp |
Reports/charts |
fa-map-marked-alt |
MapPinned |
Location with map |
fa-map-marker-alt |
MapPin |
Location marker |
fa-tools |
Wrench |
Tools/maintenance |
fa-search |
Search |
Search |
fa-chalkboard-teacher |
PresentationIcon or GraduationCap |
Presenter |
fa-plane |
Plane |
Travel/remote |
fa-sync-alt fa-spin |
RefreshCw + CSS animation |
Spinner |
fa-arrow-up |
ArrowUp |
Up arrow |
fa-arrow-down |
ArrowDown |
Down arrow |
fa-list-ol |
ListOrdered |
Ordered list |
fa-file-csv |
FileSpreadsheet |
CSV file |
fa-toggle-on |
ToggleRight |
Toggle |
fa-calendar-alt |
CalendarDays |
Calendar |
fa-exclamation-triangle |
TriangleAlert |
Warning |
fa-lock |
Lock |
Locked |
fa-unlock |
Unlock |
Unlocked |
fa-eye |
Eye |
Visible |
fa-eye-slash |
EyeOff |
Hidden |
fa-trash |
Trash2 |
Delete |
fa-edit / fa-pencil |
Pencil |
Edit |
fa-plus |
Plus |
Add |
fa-times / fa-close |
X |
Close/remove |
fa-check |
Check |
Confirm |
fa-ban |
Ban |
Denied/blocked |
fa-user |
User |
Person |
fa-users |
Users |
Group |
fa-tag |
Tag |
Tag/label |
fa-print |
Printer |
|
fa-download |
Download |
Download |
fa-upload |
Upload |
Upload |
fa-copy |
Copy |
Copy |
fa-qrcode |
QrCode |
QR code |
fa-id-card |
IdCard |
Badge/ID |
fa-file-alt |
FileText |
File |
fa-compress-arrows-alt |
Minimize2 |
Collapse |
fa-expand |
Maximize2 |
Expand |
fa-angle-right |
ChevronRight |
Nav arrow |
fa-angle-down |
ChevronDown |
Accordion |
fa-clock |
Clock |
Time |
Lucide Usage Pattern
<script>
import { Settings, Search, MapPin } from '@lucide/svelte';
</script>
<!-- Decorative icon with adjacent label -->
<Settings size="1em" class="shrink-0" aria-hidden="true" />
<!-- Icon-only button — MUST have aria-label -->
<button aria-label="Settings" class="btn btn-sm preset-tonal-surface">
<Settings size="1.1em" />
</button>
7. Typography
Standard Hierarchy
<!-- Page title -->
<h1 class="text-3xl sm:text-4xl font-black tracking-tight">Title</h1>
<!-- Section heading -->
<h2 class="text-xl font-bold">Section</h2>
<!-- Card/item heading -->
<h3 class="text-lg font-semibold">Item</h3>
<!-- Label / eyebrow -->
<span class="text-xs font-bold uppercase tracking-wide opacity-40">Label</span>
<!-- Muted secondary text -->
<span class="text-sm opacity-60">Secondary info</span>
<!-- Hint / placeholder text -->
<span class="text-xs opacity-40 italic">Hint</span>
Rule: Opacity Over Fixed Colors for Muted Text
<!-- ✅ Theme-aware muted text -->
<span class="text-sm opacity-60">Note</span>
<!-- ❌ Fixed muted text — breaks in dark mode -->
<span class="text-sm text-gray-500">Note</span>
Exception:
text-gray-*is acceptable in components that intentionally use plain Tailwind grayscale for neutrality (e.g., Journals list cards), as long asdark:text-gray-*counterpart is always included.
8. Card & Layout Patterns
See documentation/AE__UI_Component_Patterns.md for the full pattern reference.
Key standard patterns:
- List item card:
border border-gray-200 dark:border-gray-700 border-l-4 border-l-primary-500/40with hover intensification - Content card:
rounded-lg border border-surface-200-800 bg-surface-50-900 px-4 py-3 - Glow accent:
absolute -inset-1 bg-linear-to-r from-primary-500 to-secondary-500 rounded-2xl blur opacity-25 dark:opacity-40 group-hover:opacity-60 transition duration-1000 group-hover:duration-200 pointer-events-none - Empty state:
preset-tonal-warning p-6 rounded-xlcentered, with icon + heading + description
9. Accessibility Rules
- Never remove focus rings.
focus:ring-0on text inputs fails WCAG 2.1 AA. Usefocus:ring-2 focus:ring-primary-500instead. - Icon-only buttons must have
aria-label. No exceptions. - Decorative icons must have
aria-hidden="true". This applies to ALL FontAwesome<span class="fas ...">elements too. - Color is not the only status indicator. Pair color with text or icon shape.
- Form labels must be explicit. Use
<label for="...">oraria-label.
10. Module-by-Module Status
Core / App Shell
| Item | Status | Notes |
|---|---|---|
| Root layout banners (offline, expired) | ✅ Done | bg-orange-600/90 → preset-tonal-warning (2026-03-16) |
e_app_sys_bar.svelte |
✅ Modern | Best-practice reference component |
e_app_theme.svelte |
🟡 Legacy | Redundant with sys_bar theme section; keep but no new work |
core/+layout.svelte |
✅ Done | variant-* → preset-* (2026-03-16) |
core/+page.svelte |
✅ Good | Excellent card grid template |
All /core/ files (21 files) |
✅ Done | variant-* → preset-*, FA → Lucide (2026-03-16) |
Journals
| Item | Status | Notes |
|---|---|---|
| Overall | ✅ Canonical reference | Use as template for all new work |
ae_comp__journal_obj_li.svelte |
✅ Excellent | Card pattern, icon sizing, hover states |
ae_comp__journal_entry_obj_li.svelte |
✅ Done | bg-slate-* → bg-gray-*; hardcoded HSL → Tailwind tokens (2026-03-16) |
ae_comp__journal_entry_header.svelte |
✅ Done | focus:ring-0 restored to focus:ring-2 (2026-03-16) |
Events — General
| Item | Status | Notes |
|---|---|---|
events/+page.svelte |
✅ Done | FA → Lucide; variant-ghost-surface → preset-outlined-surface (2026-03-16) |
events/+layout.svelte |
✅ Done | FA → Lucide (spinners, arrows) (2026-03-16) |
events/ae_comp__events_menu_nav.svelte |
✅ Done | FA → Lucide (2026-03-16) |
events/[id]/+page.svelte |
✅ Done | FA → Lucide; variant-* → preset-* (2026-03-16) |
events/ae_comp__event_file_obj_tbl.svelte |
✅ Done | FA → Lucide (2026-03-16) |
events/ae_comp__event_presentation_obj_li.svelte |
✅ Done | FA → Lucide (2026-03-16) |
| Lucide inline flow | ✅ Done | Global svg.lucide { display: inline } rule in app.css (2026-03-16) |
Events — Pres Mgmt
| Item | Status | Notes |
|---|---|---|
| All 24 pres_mgmt files | ✅ Done | FA → Lucide; variant-* → preset-* (2026-03-16) |
| Card styling for session/presenter lists | 🔒 Phase 3 | Deferred to post-April 2026 |
Events — Launcher ⚠️ Live Production
| Item | Status | Notes |
|---|---|---|
| FA → Lucide | ✅ Done | All FA spans were already in HTML comments — launcher is clean (verified 2026-03-16) |
variant-* |
✅ Done | The variant-soft-secondary at line 329 is inside a comment block — no live variants remain |
| Card styling / UX polish | 🔒 Phase 3 | Deferred to post-April 2026 conference |
Events — Badges
| Item | Status | Notes |
|---|---|---|
| FA → Lucide | ✅ Done | All badge files migrated (2026-03-16) |
badge_upload_form.svelte |
✅ Done | variant-* → preset-* (2026-03-16) |
badge_template_form.svelte |
✅ Done | variant-* → preset-* (2026-03-16) |
code_to_html |
✅ Refactored | FA HTML string dict → code_to_icon Lucide component map (2026-03-16) |
Events — Leads
| Item | Status | Notes |
|---|---|---|
| FA → Lucide | ✅ Done | All leads files migrated (2026-03-16) |
ae_comp__exhibit_signin.svelte |
✅ Done | variant-* → preset-* (2026-03-16) |
ae_comp__lead_qr_scanner.svelte |
✅ Done | variant-* → preset-* (2026-03-16) |
ae_tab__add.svelte |
✅ Done | variant-* → preset-* (2026-03-16) |
IDAA ⚠️ Privacy-Critical
| Item | Status | Notes |
|---|---|---|
(idaa)/+page.svelte |
✅ Modern | Semantic tokens, good access gate |
| FA CDN | ✅ Scoped | Moved from app.html → idaa/+layout.svelte <svelte:head> (2026-03-16) |
| Archives, BB, Recovery Meetings | 🔒 Deferred | Full style review deferred to Phase 3 |
| All IDAA FA → Lucide | 🔒 Last priority | Review only after non-IDAA modules are complete |
11. Issues Ranked by Priority
| # | Severity | Issue | Location | Phase |
|---|---|---|---|---|
| 1 | 🔴 A11y | focus:ring-0 removes focus indicator on journal name input |
ae_comp__journal_entry_header.svelte:108 |
1 |
| 2 | 🔴 Maintenance | No global dark mode form fix — per-component patches scattered | e_app_sys_bar.svelte lines 693–707; others |
1 |
| 3 | 🟡 Consistency | FontAwesome icons throughout events module | 61 event files | 1–2 |
| 4 | 🟡 Consistency | variant-* buttons used instead of preset-* |
Events, Badges, Leads routes | 1–2 |
| 5 | 🟡 Theme | Hardcoded bg-orange-600/90 on root layout offline banner |
+layout.svelte |
2 |
| 6 | 🟡 Theme | Hardcoded HSL colors on journal entry eye icon | ae_comp__journal_entry_obj_li.svelte |
2 |
| 7 | 🟢 Consistency | bg-slate-* used in journal entry instead of bg-gray-* |
ae_comp__journal_entry_obj_li.svelte |
2 |
| 8 | 🟢 Polish | Pres Mgmt pages lack card styling (bare <ul> lists) |
pres_mgmt/+page.svelte and sub-pages |
3 |
| 9 | 🟢 Polish | Root layout banner uses direct font-semibold text-white instead of preset |
+layout.svelte |
2 |
12. Implementation Plan
Phase 1: Quick Wins (current)
Step 1 — Global form dark mode utility ✅ Target: src/app.css
Add a global utility so Skeleton .input, .select, .textarea classes render correctly in dark mode. This eliminates all per-component <style> patches.
Step 2 — FontAwesome → Lucide in events nav/layout files
Scope: Non-Launcher, non-IDAA files only. Priority order:
src/routes/events/ae_comp__events_menu_nav.svelte— top-level navigationsrc/routes/events/+layout.svelte— spinner and sort icons
Step 3 — Standardize variant-* → preset-* in events
Scope: +page.svelte, settings/+page.svelte, sign_in_out.svelte
Skip: Launcher files (frozen), IDAA files (deferred)
Phase 2: Consolidation
- Replace hardcoded banner color in root layout (
bg-orange-600/90→preset-tonal-warning) - Fix journal entry eye icon hardcoded HSL colors
- Fix
bg-slate-*inconsistency in journal entry - Migrate
variant-*in remaining events files: Pres Mgmt, Badges, Leads - Remove per-component
<style>dark mode patches (now covered by global utility) - Add responsive typography to events hub and pres mgmt pages
Phase 3: Module Refactors (post-April 2026 conference)
- Events Launcher: FontAwesome → Lucide,
variant-*→preset-* - Events Pres Mgmt: Card styling for session/presenter lists
- IDAA: Full style review (Archives, BB, Recovery Meetings)
- Create
.prose-journalutility in app.css to centralize markdown prose overrides
13. Files to Modify (Phase 1)
| File | Change |
|---|---|
src/app.css |
Add global .dark form element utility |
src/routes/events/ae_comp__events_menu_nav.svelte |
Replace fas fa-* with Lucide imports |
src/routes/events/+layout.svelte |
Replace fas fa-sync-alt fa-spin and arrow icons with Lucide |
src/routes/events/+page.svelte |
Replace variant-ghost-surface → preset-outlined-surface |
src/routes/events/[event_id]/settings/+page.svelte |
Replace variant-filled-secondary/primary → preset-* |
src/routes/events/[event_id]/sign_in_out.svelte |
Replace variant-soft-warning → preset-tonal-warning |
14. Testing Notes
- Run
npx svelte-checkafter every file change - Test dark mode toggle after form utility added — confirm inputs render correctly in dark
- Test light mode — confirm no regressions
- Events nav: verify all Lucide icons render at correct size and with correct meaning
- Launcher: do not touch — verify no unintended changes via
git diff - IDAA: do not touch — verify no unintended changes via
git diff
15. What We Are NOT Changing (Phase 1)
- Events Launcher files — frozen until post-April 2026 conference
- All IDAA files — deferred to last phase
- Root layout banner hardcoded color — Phase 2
- Journal entry HSL eye icon colors — Phase 2
- Pres Mgmt card styling — Phase 3