Files
web-app-launcher/plans/phase-4-7-full-expansion/phase-4-visual-styling.md
T
alexei.dolgolyov 1c0a7cb850 feat: Phases 4-7 — Full Feature Expansion (26 features)
Phase 4 — New Widget Types:
- Clock/Weather, System Stats, RSS/Feed, Calendar, Markdown,
  Metric/Counter, Link Group, Camera/Stream widgets
- Backend services with caching for each data source
- Full creation form with dynamic config fields per type

Phase 5 — Visual & Styling Enhancements:
- Glassmorphism card style (solid/glass/outline)
- Board-level themes with per-board hue/saturation
- Animated SVG status rings replacing static dots
- Card size options (compact/medium/large)
- Custom CSS injection (admin + per-board, sanitized)
- Wallpaper backgrounds with blur/overlay/parallax

Phase 6 — Functional Features:
- Favorites bar with drag-and-drop reordering
- Recent apps tracking with privacy toggle
- Uptime dashboard page (/status, guest-accessible)
- Notifications system (Discord/Slack/Telegram/HTTP webhooks)
- App tags with filtering in board view
- Multi-URL app cards with expandable sub-links
- Personal API tokens with scoped permissions
- Audit log with retention and admin viewer

Phase 7 — Quality of Life:
- Onboarding wizard (5-step first-launch setup)
- App URL health preview with favicon/title detection
- Board templates (4 built-in + custom import/export)
- Keyboard shortcut overlay (j/k nav, 1-9 boards, ? help)

212 files changed, 15641 insertions, 980 deletions.
Build, lint, type check, and 222 tests all pass.
2026-03-25 14:18:10 +03:00

10 KiB

Phase 4: Visual & Styling Enhancements

Status: Complete Parent plan: PLAN.md Domain: frontend

Objective

Implement all 6 visual/styling features: glassmorphism cards, board-level themes, animated status rings, card size options, custom CSS injection, and wallpaper backgrounds.

Tasks

4.1 Glassmorphism Card Style

  • Add card style system to theme store — extend src/lib/stores/theme.svelte.ts:
    • New property: cardStyle: 'solid' | 'glass' | 'outline' (default: 'solid')
    • Persist to localStorage, broadcast across tabs
  • Add CSS classes in src/app.css:
    • .card-solid — current default card style
    • .card-glassbackdrop-filter: blur(12px); background: hsl(var(--card) / 0.6); border: 1px solid hsl(var(--border) / 0.3)
    • .card-outlinebackground: transparent; border: 1px solid hsl(var(--border))
  • Update widget/card components to use dynamic card style class
  • Add card style picker to theme settings UI (3-way toggle: solid/glass/outline)

4.2 Board-Level Themes

  • Create src/lib/components/board/BoardThemeProvider.svelte
    • Reads board's themeHue, themeSaturation, backgroundType from board data
    • Overrides CSS variables when viewing that board (--primary-h, --primary-s)
    • Smooth transition when switching boards (CSS transition on :root variables)
    • Restores global theme when navigating away from the board
  • Update board edit form to include theme settings:
    • Hue slider (0-360 with color preview)
    • Saturation slider (0-100)
    • Background type selector (mesh/particles/aurora/none/wallpaper)
  • Update board data loading to include theme fields
  • Fix updateBoard server action to extract theme fields from formData
  • Fix backgroundType validator to accept all background types (mesh/particles/aurora/wallpaper/none)

4.3 Animated SVG Status Ring

  • Create src/lib/components/app/AnimatedStatusRing.svelte
    • SVG circle around app icon with status-dependent animation:
      • Online: animated green fill sweep (stroke-dashoffset animation)
      • Offline: pulsing red ring (opacity animation)
      • Degraded: partial yellow arc (75% fill, subtle pulse)
      • Unknown: gray dashed ring (rotating dash pattern)
    • Props: status, size (scales with card size), animated (boolean)
  • Replace static status dots in AppWidget.svelte with AnimatedStatusRing
  • Ensure ring scales appropriately with compact/medium/large card sizes

4.4 Card Size Options

  • Add CardSize support to section and board levels:
    • Per-section: section.cardSize overrides board default
    • Per-board: board.cardSize as fallback
    • Global default: 'medium'
  • Create card size variants in widget components:
    • compact — icon + name only, smaller padding, single row grid
    • medium — current default (icon + name + status + description on hover)
    • large — icon + name + description + sparkline + tags, more padding
  • Add card size picker to section edit form (DraggableSection) and board settings
  • Update WidgetGrid to adjust grid columns based on card size
  • Wire up onUpdateSection handler through DraggableBoard to board edit page

4.5 Custom CSS Injection

  • Create src/lib/components/settings/CustomCssEditor.svelte
    • Textarea with monospace font for custom CSS
    • Live preview toggle
    • Sanitization: strip <script> tags, limit selectors to .app-scope or descendant selectors
  • Add custom CSS field to admin system settings form (SettingsForm.svelte)
  • Add per-board custom CSS field to board edit form
  • Create src/lib/components/layout/CustomCssInjector.svelte
    • Injects <style> tag with sanitized CSS from system settings + current board
    • Wraps CSS in .custom-css-scope to prevent breaking critical UI
  • Add CustomCssInjector to root layout

4.6 Wallpaper Backgrounds

  • Create src/lib/components/background/WallpaperBackground.svelte
    • Displays uploaded image or Unsplash URL as board background
    • Configurable: blur amount (0-20px), overlay opacity (0-1), parallax (boolean), position (fixed/scroll)
    • Fallback to procedural background if wallpaper fails to load
  • Add wallpaper upload endpoint: src/routes/api/wallpaper/+server.ts
    • Accept image upload (PNG, JPG, WebP), save to static/uploads/wallpapers/
    • Return URL path
    • Max file size: 5MB
  • Add wallpaper configuration to board edit form:
    • Image upload button or URL input
    • Blur slider, overlay opacity slider
    • Parallax toggle
  • Integrate WallpaperBackground into AmbientBackground component (new background type)
  • Optional Unsplash integration (deferred — requires external API key infrastructure)

Files to Modify/Create

  • src/lib/stores/theme.svelte.ts — extend with cardStyle
  • src/app.css — add glassmorphism classes
  • src/lib/components/board/BoardThemeProvider.svelte — new
  • src/lib/components/app/AnimatedStatusRing.svelte — new
  • src/lib/components/widget/AppWidget.svelte — modify (use AnimatedStatusRing)
  • src/lib/components/widget/WidgetGrid.svelte — modify (card size grid)
  • src/lib/components/settings/CustomCssEditor.svelte — new
  • src/lib/components/layout/CustomCssInjector.svelte — new
  • src/lib/components/background/WallpaperBackground.svelte — new
  • src/routes/api/wallpaper/+server.ts — new
  • Board edit form components — modify
  • Section edit form components — modify
  • Root layout — modify (add CustomCssInjector)

Acceptance Criteria

  • Glassmorphism effect works in both light and dark mode, ambient bg bleeds through
  • Board themes override global theme smoothly, restore on navigation
  • Status rings animate correctly for all 4 statuses
  • Card sizes adjust grid layout and widget content appropriately
  • Custom CSS is properly sandboxed (cannot break critical UI elements)
  • Wallpaper backgrounds display correctly with all configuration options
  • All visual changes respect dark/light mode

Notes

  • Glassmorphism requires backdrop-filter support (all modern browsers)
  • Board theme transitions: use CSS transition: --primary-h 0.3s, --primary-s 0.3s on :root
  • Custom CSS sanitization: use a simple regex-based approach to strip dangerous selectors, or wrap all custom CSS in a scoped parent selector
  • Wallpaper upload: reuse existing upload infrastructure if available (check static/uploads/)

Review Checklist

  • All tasks completed
  • Code follows project conventions
  • No unintended side effects
  • Build passes (Big Bang: code quality check only)
  • Tests pass (Big Bang: skipped for intermediate phase)

Handoff to Next Phase

What was done

  • 4.1 Glassmorphism: Theme store already had cardStyle property with localStorage persistence and broadcast sync. CSS classes .card-solid, .card-glass, .card-outline exist in app.css with dark mode variants. AppWidget uses dynamic card-${theme.cardStyle} class. ThemeCustomizer has 3-way card style picker.
  • 4.2 Board-Level Themes: BoardThemeProvider applies board-specific --primary-h/--primary-s CSS variables and now properly restores global theme values on cleanup. Board edit form has hue slider, saturation slider, background type selector, and card size picker. Fixed the updateBoard server action to extract all theme fields from formData. Fixed updateBoardSchema backgroundType enum to accept mesh/particles/aurora/wallpaper/none.
  • 4.3 Animated SVG Status Ring: AnimatedStatusRing component renders an SVG circle with 4 status-dependent animations (fill sweep for online, pulse opacity for offline, degraded pulse for degraded, rotating dash for unknown). Replaces status dots in AppWidget at all 3 card sizes.
  • 4.4 Card Size Options: Section-level cardSize overrides board-level default. WidgetGrid adjusts grid columns per card size. AppWidget renders compact/medium/large variants. Added card size picker dropdown to DraggableSection with onUpdateSection handler wired through DraggableBoard to the board edit page.
  • 4.5 Custom CSS Injection: CustomCssEditor with validation, sanitization, and live preview. CustomCssInjector sanitizes and injects <style> tag scoped to .custom-css-scope. Added to root layout for system-level CSS and board view page for board-level CSS. Added CustomCssEditor to admin SettingsForm for system-wide CSS.
  • 4.6 Wallpaper Backgrounds: WallpaperBackground component with blur, overlay, parallax, and position options. Upload API endpoint at /api/wallpaper with type/size validation. Board edit form has upload, URL input, blur slider, overlay slider, and parallax toggle. Integrated into AmbientBackground. Unsplash integration deferred.

What the next phase needs to know

  • All visual/styling features are implemented and wired end-to-end
  • The updateBoardSchema backgroundType now uses inline string enum ['mesh', 'particles', 'aurora', 'wallpaper', 'none'] instead of the BackgroundType constant (which only had none/color/wallpaper)
  • BoardThemeProvider now imports theme store to restore global values on cleanup
  • DraggableSection and DraggableBoard now support an optional onUpdateSection callback for section-level edits (currently used for cardSize)
  • System-level custom CSS is loaded in the root layout server data and injected via CustomCssInjector
  • Board-level custom CSS is injected on the board view page via CustomCssInjector
  • Unsplash integration was deferred as it requires external API key management infrastructure

Potential concerns

  • The BackgroundType constant in constants.ts (none/color/wallpaper) does not match the actual background types used by the theme system (mesh/particles/aurora/wallpaper/none). The validator was fixed to use inline strings, but the constant may cause confusion if used elsewhere.
  • The board edit form uses native HTML forms with use:enhance — theme fields are now extracted in the server action but numeric parsing from formData strings could produce NaN if invalid input sneaks through. The Zod schema provides a safety net.
  • Custom CSS sanitization is regex-based. It blocks common XSS vectors but is not a full CSS parser. A determined attacker with admin access could potentially craft CSS that affects layout outside .custom-css-scope.