# Phase 1: Foundation and Project Scaffold - Research

**Researched:** 2026-02-26
**Domain:** Tauri v2 + SolidJS monorepo scaffold, frameless window, IPC bridge, GPU acceleration, anti-aliased rendering on Windows
**Confidence:** HIGH

## Summary

Phase 1 establishes the working Tauri v2 + SolidJS monorepo that all subsequent phases build on. The research confirms that Tauri v2 (2.10.x) has first-class support for SolidJS via `create-tauri-app`, frameless/borderless windows via `decorations: false`, and bidirectional IPC via `#[tauri::command]` (frontend-to-Rust) and `app.emit()` (Rust-to-frontend). GPU hardware acceleration on Windows requires setting the `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS` environment variable to `--ignore-gpu-blocklist` before builder initialization -- this is a verified fix from Tauri issue #4891. Anti-aliasing is handled natively by WebView2's Chromium-based renderer; no special configuration is needed for text or CSS-rendered geometry, though CSS `transform` properties enable GPU-composited anti-aliased rendering for animations.

The scaffold should use the official `create-tauri-app` template as a starting point, then restructure into the project's monorepo layout. Tailwind CSS v4 integrates via `@tailwindcss/vite` plugin placed before `vite-plugin-solid` in the Vite config. The custom titlebar with drag region uses the `data-tauri-drag-region` HTML attribute, and window controls (close/minimize/maximize) call the `@tauri-apps/api/window` API. Tauri v2's capability/permission system requires explicit permissions in `src-tauri/capabilities/` for window operations.

**Primary recommendation:** Use `create-tauri-app` to bootstrap, then restructure. Set GPU flags in `main.rs` before Tauri builder. Implement a minimal visual test harness (CSS animations + typography/geometry samples) with an FPS counter using `requestAnimationFrame` to prove all success criteria in a single view.

<user_constraints>
## User Constraints (from CONTEXT.md)

### Locked Decisions
- Plain borderless frameless window -- no styled chrome in Phase 1
- Angled corners, glow lines, and full cyberpunk chrome deferred to Phase 3
- Minimal placeholder window controls (close/minimize/maximize) -- functional but unstyled
- Designated drag region at the top of the window (not drag-anywhere)
- Window is resizable from the start (no fixed size)
- Use Tauri's built-in `#[tauri::command]` system directly -- no custom abstraction layer
- Prove both directions: frontend-to-Rust (invoke) and Rust-to-frontend (events)
- Manual type definitions on each side (Rust and TypeScript separately) -- no generated/shared types yet
- Claude's discretion on what visible change to use for the IPC demo
- Combined demo: CSS animations (rotating, fading, translating elements) + typography/geometry samples (text at various sizes, diagonal lines, curves)
- Cyberpunk-flavored styling: dark background with bright magenta accent color
- Color direction for eventual theme: "Neon CMYK" -- vibrant sky blue (cyan), Cyberpunk 2077 yellow, bright magenta, white. Phase 1 uses magenta as the primary accent.
- FPS counter overlay included to visually confirm 60fps GPU acceleration

### Claude's Discretion
- Exact IPC demo interaction (color toggle, counter, etc.)
- Monorepo workspace structure and package layout
- Build tooling choices for SolidJS frontend
- Exact CSS animation choices for the GPU acceleration proof
- Anti-aliasing verification approach

### Deferred Ideas (OUT OF SCOPE)
None -- discussion stayed within phase scope
</user_constraints>

<phase_requirements>
## Phase Requirements

| ID | Description | Research Support |
|----|-------------|-----------------|
| WIN-05 | Borderless frameless windows via Tauri | Tauri v2 `decorations: false` in `tauri.conf.json` removes native window frame. Custom titlebar with `data-tauri-drag-region` for drag. Window controls via `@tauri-apps/api/window` (minimize/maximize/close). Resize works with undecorated windows (bug #8519 fixed in Tauri v2 stable). |
| PLAT-01 | Windows-only target (no cross-platform compatibility required) | Tauri v2 uses WebView2 (Chromium-based) on Windows. No need to test WebKitGTK or WKWebView. Simplifies GPU acceleration config to a single platform. |
| PLAT-02 | Consumable via `cargo add holohue` | Phase 1 establishes the monorepo structure with a Cargo workspace. The `holohue` crate wraps Tauri's `Builder` pattern. For v1 (demo), this is a workspace binary, not a published crate. Crate distribution strategy (rust-embed + custom protocol) is scaffolded but not fully implemented until later phases. |
| PLAT-03 | Anti-aliased rendering for all lines, fonts, and UI elements | WebView2 (Chromium) provides sub-pixel anti-aliased text rendering by default. CSS-rendered geometry (borders, box-shadow, gradients) is anti-aliased by the Chromium compositor. SVG elements use `geometricPrecision` for anti-aliased curves. GPU-composited layers (`transform`, `opacity`) get hardware anti-aliasing automatically. No special configuration needed on Windows/WebView2. |
</phase_requirements>

## Standard Stack

### Core

| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| Tauri | 2.10.x | Application shell, window management, IPC | Official Tauri v2 stable. Provides frameless windows, custom protocol, event system, command system. Windows uses WebView2 (Chromium). |
| SolidJS | 1.9.x | Frontend UI framework | Fine-grained reactivity without VDOM. 3.9KB. Official Tauri template support. Ideal for animation-heavy UIs requiring surgical DOM control. |
| Vite | 6.x | Frontend build tool | Official SolidJS integration via `vite-plugin-solid`. HMR for rapid iteration. Standard for Tauri v2 frontends. |
| TypeScript | 5.x | Frontend type safety | SolidJS has excellent TS support. Types the IPC boundary. |
| Tailwind CSS | 4.0.x | Utility-first CSS | v4 integrates as Vite plugin (`@tailwindcss/vite`). CSS-first config. `@property` support for animating custom properties. |
| @tauri-apps/api | 2.x | Frontend Tauri bindings | TypeScript API for invoking Rust commands, window management, events. |
| serde / serde_json | 1.x | Rust serialization | Required for all Tauri IPC command arguments and return values. |
| tauri-build | 2.x | Build-time Tauri setup | Required in `build-dependencies` for Tauri v2 projects. |

### Supporting

| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| vite-plugin-solid | 2.x | SolidJS Vite integration | Always -- required for SolidJS JSX compilation in Vite |
| @tailwindcss/vite | 4.x | Tailwind Vite plugin | Always -- v4's recommended integration path, replaces PostCSS config |

### Alternatives Considered

| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| Tailwind CSS v4 | Plain CSS | Tailwind accelerates prototyping and provides the utility foundation for the eventual theme system. Plain CSS is viable but slower to develop with. For Phase 1's simple demo, either works, but Tailwind establishes the foundation for later phases. |
| `create-tauri-app` template | Manual scaffold | Manual scaffold gives full control but is error-prone. The official template handles `tauri.conf.json`, `Cargo.toml`, `build.rs`, and capabilities correctly. Start from template, then restructure. |

**Installation:**

```bash
# Create initial project (interactive -- select SolidJS + TypeScript)
npm create tauri-app@latest holohue-gui

# Or non-interactive:
# npm create tauri-app@latest holohue-gui -- --template solid-ts

# Additional frontend dependencies
npm install -D @tailwindcss/vite
```

```toml
# src-tauri/Cargo.toml additions
[dependencies]
tauri = { version = "2", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[build-dependencies]
tauri-build = { version = "2", features = [] }
```

## Architecture Patterns

### Recommended Project Structure

```
holohue-gui/
├── src-tauri/                    # Rust backend
│   ├── Cargo.toml                # Tauri app crate
│   ├── build.rs                  # tauri-build
│   ├── tauri.conf.json           # Tauri configuration
│   ├── capabilities/
│   │   └── default.json          # Permissions for window ops
│   ├── src/
│   │   ├── main.rs               # Entry point (GPU flags + builder)
│   │   └── lib.rs                # Commands, setup, run()
│   └── icons/                    # App icons
├── src/                          # SolidJS frontend
│   ├── index.html                # HTML entry
│   ├── App.tsx                   # Root component
│   ├── App.css                   # Global styles (Tailwind import)
│   ├── index.tsx                 # SolidJS mount
│   └── components/               # Phase 1 components
│       ├── Titlebar.tsx          # Custom titlebar with drag region + controls
│       ├── FpsCounter.tsx        # FPS overlay using requestAnimationFrame
│       ├── AnimationDemo.tsx     # CSS animation proof (rotate, fade, translate)
│       ├── TypographyDemo.tsx    # Text at various sizes for anti-alias check
│       ├── GeometryDemo.tsx      # Diagonal lines, curves, SVG shapes
│       └── IpcDemo.tsx           # Bidirectional IPC demo widget
├── package.json
├── vite.config.ts
├── tsconfig.json
└── .planning/                    # Project planning docs
```

**Rationale for flat src-tauri/src structure in Phase 1:** The CONTEXT.md specifies no custom abstraction layer, manual types, and a simple IPC demo. A `lib.rs` + `main.rs` split is sufficient. Later phases will introduce modules (`theme/`, `commands/`, etc.) as complexity grows.

### Pattern 1: GPU Acceleration Initialization (Windows)

**What:** Set WebView2 browser arguments to bypass the GPU blocklist before Tauri builder initialization.
**When to use:** Always, on Windows. This is framework-level init code, not consumer-configurable.
**Confidence:** HIGH -- verified fix from Tauri issue #4891, confirmed working.

**Example:**
```rust
// src-tauri/src/main.rs
// Source: https://github.com/tauri-apps/tauri/issues/4891

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
    // MUST be set before tauri::Builder is constructed.
    // Bypasses WebView2's GPU compatibility blocklist to enable
    // hardware-accelerated CSS animations and compositing.
    std::env::set_var(
        "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS",
        "--ignore-gpu-blocklist",
    );

    holohue_gui_lib::run();
}
```

### Pattern 2: Frameless Window with Custom Titlebar

**What:** Remove native window decorations and implement a custom drag region + window controls in the frontend.
**When to use:** Required for WIN-05 (borderless frameless windows).
**Confidence:** HIGH -- official Tauri v2 documentation pattern.

**Rust setup (tauri.conf.json):**
```json
{
  "app": {
    "windows": [
      {
        "label": "main",
        "title": "HoloHue",
        "decorations": false,
        "transparent": false,
        "width": 1200,
        "height": 800,
        "minWidth": 640,
        "minHeight": 480,
        "resizable": true,
        "center": true
      }
    ]
  }
}
```

**Frontend titlebar component (SolidJS):**
```tsx
// Source: https://v2.tauri.app/learn/window-customization/
import { getCurrentWindow } from '@tauri-apps/api/window';

const appWindow = getCurrentWindow();

export function Titlebar() {
  return (
    <div data-tauri-drag-region class="titlebar">
      <span data-tauri-drag-region class="titlebar-title">HoloHue</span>
      <div class="titlebar-controls">
        <button onClick={() => appWindow.minimize()}>&#x2013;</button>
        <button onClick={() => appWindow.toggleMaximize()}>&#x25A1;</button>
        <button onClick={() => appWindow.close()}>&#x2715;</button>
      </div>
    </div>
  );
}
```

**Required capabilities (src-tauri/capabilities/default.json):**
```json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "Default capabilities for HoloHue",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "core:window:default",
    "core:window:allow-start-dragging",
    "core:window:allow-minimize",
    "core:window:allow-toggle-maximize",
    "core:window:allow-close"
  ]
}
```

**Important note on `data-tauri-drag-region`:** This attribute only works on the element it is directly applied to. Child elements do NOT inherit it. Each child that should be draggable needs the attribute added individually. The controls container should NOT have the attribute (so buttons remain clickable).

### Pattern 3: Bidirectional IPC (Commands + Events)

**What:** Frontend calls Rust via `invoke()` (commands), Rust pushes to frontend via `app.emit()` (events).
**When to use:** All Rust-frontend communication.
**Confidence:** HIGH -- official Tauri v2 documentation.

**Rust commands (src-tauri/src/lib.rs):**
```rust
// Source: https://v2.tauri.app/develop/calling-rust/
use tauri::Emitter;

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

#[tauri::command]
fn get_system_info() -> Result<String, String> {
    Ok(format!("Tauri {}", tauri::VERSION))
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet, get_system_info])
        .setup(|app| {
            // Example: emit event to frontend after setup
            let handle = app.handle().clone();
            std::thread::spawn(move || {
                std::thread::sleep(std::time::Duration::from_secs(1));
                handle.emit("rust-ready", "Backend initialized").unwrap();
            });
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

**Frontend IPC (SolidJS):**
```tsx
// Source: https://v2.tauri.app/develop/calling-rust/
// Source: https://v2.tauri.app/develop/calling-frontend/
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { createSignal, onMount, onCleanup } from 'solid-js';

export function IpcDemo() {
  const [message, setMessage] = createSignal('');
  const [rustEvent, setRustEvent] = createSignal('');

  // Frontend -> Rust (invoke command)
  const callRust = async () => {
    const result = await invoke<string>('greet', { name: 'HoloHue' });
    setMessage(result);
  };

  // Rust -> Frontend (listen for events)
  onMount(async () => {
    const unlisten = await listen<string>('rust-ready', (event) => {
      setRustEvent(event.payload);
    });
    onCleanup(() => unlisten());
  });

  return (
    <div>
      <button onClick={callRust}>Invoke Rust</button>
      <p>{message()}</p>
      <p>Rust event: {rustEvent()}</p>
    </div>
  );
}
```

**Key rules for IPC in Tauri v2:**
- Command arguments use **camelCase** in JS, **snake_case** in Rust (automatic conversion)
- All arguments and return values must be serializable (serde)
- Return `Result<T, String>` for error handling (use `.catch()` on frontend)
- Use `tauri::State<T>` to access managed state in commands
- Events require the `Emitter` trait imported: `use tauri::Emitter;`
- Event listeners return an `unlisten` function -- always call it in `onCleanup()`

### Pattern 4: FPS Counter via requestAnimationFrame

**What:** A lightweight FPS counter overlay that measures actual frame rate by counting `requestAnimationFrame` callbacks.
**When to use:** Phase 1 visual test harness. Required by CONTEXT.md.
**Confidence:** HIGH -- standard web performance measurement pattern.

**Example:**
```tsx
import { createSignal, onMount, onCleanup } from 'solid-js';

export function FpsCounter() {
  const [fps, setFps] = createSignal(0);

  onMount(() => {
    let frameCount = 0;
    let lastTime = performance.now();
    let rafId: number;

    const measure = (now: number) => {
      frameCount++;
      if (now - lastTime >= 1000) {
        setFps(frameCount);
        frameCount = 0;
        lastTime = now;
      }
      rafId = requestAnimationFrame(measure);
    };

    rafId = requestAnimationFrame(measure);
    onCleanup(() => cancelAnimationFrame(rafId));
  });

  return (
    <div class="fps-counter">
      {fps()} FPS
    </div>
  );
}
```

### Pattern 5: Tailwind CSS v4 + Vite + SolidJS Configuration

**What:** Tailwind v4 as a Vite plugin, placed before SolidJS plugin.
**When to use:** Always for this project.
**Confidence:** HIGH -- official Tailwind v4 docs + SolidJS guide.

**vite.config.ts:**
```typescript
// Source: https://tailwindcss.com/docs/installation/framework-guides/solidjs
import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [
    tailwindcss(),   // MUST be before solidPlugin
    solidPlugin(),
  ],
  server: {
    port: 1420,
    strictPort: true,
  },
  build: {
    target: 'esnext',
  },
  // Required for Tauri to find the dev server
  clearScreen: false,
});
```

**src/App.css (or index.css):**
```css
@import "tailwindcss";
```

### Anti-Patterns to Avoid

- **Polling for Rust state from frontend:** Do NOT use `setInterval` + `invoke()` to check Rust state. Use Tauri's event system (`app.emit()` + `listen()`) for push-based updates.
- **Skipping `onCleanup` for event listeners:** Every `listen()` call returns an `unlisten` function. Failing to call it in `onCleanup()` causes memory leaks and duplicate listeners.
- **Setting GPU flags after builder construction:** `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS` must be set BEFORE `tauri::Builder::default()` is called. Setting it after has no effect.
- **Using `transparent: true` without need:** Transparent windows have a performance cost (compositor must blend window with desktop). Phase 1 does not need transparency -- use `transparent: false` with a dark CSS background. Transparency is deferred to when window effects are needed (Phase 3+).
- **Adding `data-tauri-drag-region` to a parent and expecting children to inherit:** The attribute does NOT propagate. Each draggable child needs it individually. Buttons/controls in the titlebar must NOT have it.

## Don't Hand-Roll

| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| Window drag behavior | Custom mousedown/mousemove drag handler | `data-tauri-drag-region` attribute or `appWindow.startDragging()` | OS-native drag handling includes snap-to-edge, multi-monitor, and taskbar integration that custom handlers miss |
| Window minimize/maximize/close | Custom IPC commands to Rust | `@tauri-apps/api/window` methods (`minimize()`, `toggleMaximize()`, `close()`) | Official API handles platform differences and window state correctly |
| FPS measurement | Manual timestamp tracking with Date.now() | `requestAnimationFrame` + `performance.now()` | performance.now() has microsecond precision; Date.now() is millisecond and subject to clock skew |
| CSS utility classes | Hand-written utility CSS | Tailwind CSS v4 | Tailwind provides the utility foundation that later phases build the theme system on |
| Frontend build pipeline | Custom webpack/rollup config | Vite + vite-plugin-solid | Official SolidJS integration, HMR, and Tauri dev server integration work out of the box |

**Key insight:** Phase 1 is scaffolding. Every hand-rolled solution here creates maintenance burden in every subsequent phase. Use official tools and patterns.

## Common Pitfalls

### Pitfall 1: GPU Acceleration Disabled by Default on Windows

**What goes wrong:** CSS animations and transitions run at 15-30fps instead of 60fps. The visual test harness shows janky animations despite correct CSS.
**Why it happens:** WebView2 has a GPU blocklist that conservatively disables hardware acceleration for many GPU configurations. Tauri inherits this.
**How to avoid:** Set `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS` to `"--ignore-gpu-blocklist"` at the TOP of `main()`, BEFORE constructing the Tauri builder. This is a one-line fix.
**Warning signs:** FPS counter shows below 60 during simple CSS transform/opacity animations. CPU usage spikes during animations.
**Confidence:** HIGH -- verified fix from Tauri issue #4891, confirmed working by reporter.

### Pitfall 2: Undecorated Window Resize Bug (Historical)

**What goes wrong:** Setting `decorations: false` previously caused the window to be non-resizable even with `resizable: true`.
**Why it happens:** A bug in Tauri's core window handling for undecorated windows on Windows.
**How to avoid:** This was fixed in Tauri v2 stable (commit 9a4d46e, issue #8519). Ensure you are on Tauri 2.10.x or later. If resize still fails, verify `resizable: true` is set in `tauri.conf.json`.
**Warning signs:** Window cannot be resized by dragging edges. No resize cursor appears at window borders.
**Confidence:** HIGH -- fix merged and released.

### Pitfall 3: data-tauri-drag-region Not Working on Child Elements

**What goes wrong:** The titlebar drag region stops working when clicking on text labels or other child elements within the drag region div.
**Why it happens:** The `data-tauri-drag-region` attribute only applies to the exact element it is set on. It does NOT propagate to children. This is an explicit limitation documented by Tauri.
**How to avoid:** Add `data-tauri-drag-region` to every child element in the titlebar that should be draggable (e.g., the title text span). Do NOT add it to interactive elements (buttons).
**Warning signs:** Dragging works on the background of the titlebar but not when clicking directly on text.
**Confidence:** HIGH -- official Tauri documentation.

### Pitfall 4: Missing Capabilities/Permissions for Window Operations

**What goes wrong:** Window control buttons (minimize, maximize, close) silently fail. No error in the console, no action occurs.
**Why it happens:** Tauri v2 requires explicit permissions for all operations. Without `core:window:allow-minimize`, `core:window:allow-toggle-maximize`, `core:window:allow-close`, and `core:window:allow-start-dragging` in the capabilities file, these operations are blocked.
**How to avoid:** Define a capabilities file at `src-tauri/capabilities/default.json` with all required window permissions. See Pattern 2 above for the exact JSON.
**Warning signs:** Button click handlers execute (console.log works) but the window operation does not occur. No error thrown.
**Confidence:** HIGH -- official Tauri v2 security model.

### Pitfall 5: Tauri Event Listener Memory Leaks

**What goes wrong:** Event listeners accumulate over time, especially in components that mount/unmount. Each mount adds a new listener without removing the old one.
**Why it happens:** `listen()` from `@tauri-apps/api/event` returns an async `unlisten` function that must be called on cleanup. SolidJS components need explicit `onCleanup()` handling.
**How to avoid:** Always capture the `unlisten` return value and call it in `onCleanup()`. Use the pattern shown in Pattern 3 above.
**Warning signs:** Console logs from event handlers fire multiple times for a single event. Memory usage grows over time.
**Confidence:** HIGH -- standard web pattern, confirmed by Tauri docs.

### Pitfall 6: Vite Dev Server Port Mismatch

**What goes wrong:** Tauri opens a blank white window during development. The webview cannot connect to the Vite dev server.
**Why it happens:** The Vite dev server port in `vite.config.ts` does not match the `devUrl` in `tauri.conf.json`. Default Vite port is 5173, but Tauri templates often use 1420.
**How to avoid:** Set `server.port` in `vite.config.ts` to match `devUrl` in `tauri.conf.json`. Use `strictPort: true` to fail fast if the port is taken.
**Warning signs:** Blank white window on `cargo tauri dev`. Console shows connection refused.
**Confidence:** HIGH -- common developer experience issue.

## Code Examples

### Complete Rust Entry Point (main.rs + lib.rs)

```rust
// src-tauri/src/main.rs
// Source: Tauri issue #4891 (GPU fix) + Tauri v2 docs

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
    // Enable GPU hardware acceleration in WebView2.
    // Must be set BEFORE tauri::Builder construction.
    std::env::set_var(
        "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS",
        "--ignore-gpu-blocklist",
    );

    holohue_gui_lib::run();
}
```

```rust
// src-tauri/src/lib.rs
// Source: https://v2.tauri.app/develop/calling-rust/
// Source: https://v2.tauri.app/develop/calling-frontend/

use serde::Serialize;
use tauri::Emitter;

#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct IpcPayload {
    message: String,
    counter: u32,
}

// Frontend -> Rust command
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello from Rust, {}!", name)
}

// Frontend -> Rust command with structured return
#[tauri::command]
fn ping(count: u32) -> IpcPayload {
    IpcPayload {
        message: "pong".to_string(),
        counter: count + 1,
    }
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet, ping])
        .setup(|app| {
            // Rust -> Frontend event example
            let handle = app.handle().clone();
            std::thread::spawn(move || {
                // Simulate async backend work, then notify frontend
                std::thread::sleep(std::time::Duration::from_millis(500));
                let _ = handle.emit("backend-ready", IpcPayload {
                    message: "Rust backend initialized".to_string(),
                    counter: 0,
                });
            });
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

### SolidJS Root App Component

```tsx
// src/App.tsx
import { Titlebar } from './components/Titlebar';
import { FpsCounter } from './components/FpsCounter';
import { AnimationDemo } from './components/AnimationDemo';
import { TypographyDemo } from './components/TypographyDemo';
import { GeometryDemo } from './components/GeometryDemo';
import { IpcDemo } from './components/IpcDemo';

export function App() {
  return (
    <div class="app-container">
      <Titlebar />
      <FpsCounter />
      <main class="content">
        <IpcDemo />
        <AnimationDemo />
        <TypographyDemo />
        <GeometryDemo />
      </main>
    </div>
  );
}
```

### CSS Animation GPU Proof Pattern

```css
/* Animations that MUST use transform/opacity for GPU compositing */
/* Source: MDN Animation Performance guide */

/* GOOD: GPU-accelerated (compositor-only properties) */
@keyframes rotate-gpu {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes fade-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

@keyframes translate-slide {
  0% { transform: translateX(0); }
  50% { transform: translateX(100px); }
  100% { transform: translateX(0); }
}

/* BAD: triggers CPU repaints -- avoid in animation-heavy contexts */
/* DO NOT animate: background-color, box-shadow, border-color, width, height */
```

### Anti-Aliasing Verification Elements

```html
<!-- Text at various sizes to verify font anti-aliasing -->
<p style="font-size: 10px">Small text - check for jagged edges</p>
<p style="font-size: 14px">Body text - standard reading size</p>
<p style="font-size: 24px">Heading text - should be smooth</p>
<p style="font-size: 48px">Display text - verify at 1x and 2x DPI</p>

<!-- SVG geometry for anti-aliasing check -->
<svg width="200" height="200">
  <!-- Diagonal line - most visible anti-aliasing test -->
  <line x1="10" y1="10" x2="190" y2="190"
        stroke="magenta" stroke-width="1" />
  <!-- Curve - should be smooth, not stepped -->
  <path d="M10,100 Q100,10 190,100"
        stroke="magenta" fill="none" stroke-width="1" />
  <!-- Circle - edge should be smooth -->
  <circle cx="100" cy="100" r="50"
          stroke="magenta" fill="none" stroke-width="1" />
</svg>
```

## State of the Art

| Old Approach | Current Approach | When Changed | Impact |
|--------------|------------------|--------------|--------|
| Tauri v1 `tauri.conf.json` structure | Tauri v2 restructured config (`app.windows[]` instead of `tauri.windows[]`) | Oct 2024 (v2 stable) | Config path is different. v1 examples will not work. |
| No permission system | Tauri v2 capabilities/permissions in `src-tauri/capabilities/` | Oct 2024 (v2 stable) | Window operations silently fail without explicit permissions. Major gotcha for v1 users migrating. |
| Tailwind CSS v3 with `tailwind.config.js` + PostCSS | Tailwind CSS v4 with `@tailwindcss/vite` plugin, CSS-first config | Jan 2025 (v4 release) | No more config file needed. `@import "tailwindcss"` in CSS replaces old directives. Plugin must come before framework plugin in Vite config. |
| `window.__TAURI__` global object | `import { invoke } from '@tauri-apps/api/core'` | Tauri v2 | Direct imports are the standard. Global object still exists but is not recommended. |
| `tauri::api` module | Separate `@tauri-apps/api` npm package | Tauri v2 | Frontend API is a separate npm package, not bundled. Must be installed explicitly. |

**Deprecated/outdated:**
- `@tauri-apps/api/tauri` import path: Use `@tauri-apps/api/core` in v2
- `appWindow` from `@tauri-apps/api/window`: Use `getCurrentWindow()` in v2
- PostCSS-based Tailwind setup: Use `@tailwindcss/vite` plugin in v4
- `tauri.conf.json` with `"tauri": { "windows": [...] }` structure: v2 uses `"app": { "windows": [...] }`

## Open Questions

1. **IPC Demo Interaction Choice (Claude's Discretion)**
   - What we know: Need a visible change triggered by IPC in both directions
   - Recommendation: A counter/ping-pong demo -- frontend sends a count via `invoke()`, Rust increments and returns it (invoke direction), plus Rust emits a periodic "heartbeat" event that the frontend displays (event direction). Simple, clearly demonstrates both directions, and the counter value changing is an unambiguous visible change.

2. **Monorepo Structure for v1 Demo (Claude's Discretion)**
   - What we know: v1 is a demo app, not a published crate. The full `crates/holohue/` + `crates/holohue-types/` + `frontend/` structure is for the distribution model.
   - Recommendation: For Phase 1, use the standard Tauri template structure (`src-tauri/` + `src/`). This avoids premature abstraction while matching Tauri's expected layout. Restructure into workspace crates when the distribution model is needed (later phases).

3. **Build Tooling (Claude's Discretion)**
   - What we know: Vite + vite-plugin-solid is the standard. Tailwind v4 via @tailwindcss/vite.
   - Recommendation: Use exactly these tools. No additional build tooling needed for Phase 1.

## Sources

### Primary (HIGH confidence)
- [Tauri v2 Window Customization](https://v2.tauri.app/learn/window-customization/) - frameless windows, drag regions, custom titlebar, capabilities
- [Tauri v2 Calling Rust from Frontend](https://v2.tauri.app/develop/calling-rust/) - commands, invoke, arguments, return values, error handling, async, state
- [Tauri v2 Calling Frontend from Rust](https://v2.tauri.app/develop/calling-frontend/) - events, emit, emit_to, emit_filter, listen, once, unlisten
- [Tauri v2 Create Project](https://v2.tauri.app/start/create-project/) - official project creation with SolidJS template
- [Tauri v2 Configuration Reference](https://v2.tauri.app/reference/config/) - tauri.conf.json schema
- [Tauri v2 Capabilities](https://v2.tauri.app/security/capabilities/) - permission system
- [Tauri v2 Core Permissions](https://v2.tauri.app/reference/acl/core-permissions/) - window operation permissions
- [Tauri Issue #4891](https://github.com/tauri-apps/tauri/issues/4891) - GPU acceleration fix via WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS, verified working
- [Tauri Issue #8519](https://github.com/tauri-apps/tauri/issues/8519) - undecorated window resize fix, merged in v2 stable
- [Tailwind CSS SolidJS Installation Guide](https://tailwindcss.com/docs/installation/framework-guides/solidjs) - official v4 + SolidJS + Vite setup
- [MDN Animation Performance](https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Animation_performance_and_frame_rate) - GPU-composited properties (transform, opacity)
- [MDN font-smooth](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/font-smooth) - font anti-aliasing CSS properties
- [WebView2 Browser Flags](https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/webview-features-flags) - WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS documentation

### Secondary (MEDIUM confidence)
- [Tailwind CSS v4 Vite Plugin](https://tailwindcss.com/docs/guides/vite) - general Vite integration guide (not SolidJS-specific but same pattern)
- [CSS GPU Acceleration Best Practices](https://www.lexo.ch/blog/2025/01/boost-css-performance-with-will-change-and-transform-translate3d-why-gpu-acceleration-matters/) - will-change and transform GPU acceleration
- [Tauri + SolidJS Community Templates](https://github.com/riipandi/tauri-start-solid) - reference implementation patterns

### Tertiary (LOW confidence)
- None -- all findings verified against primary sources

## Metadata

**Confidence breakdown:**
- Standard stack: HIGH - all tools are official/mainstream with verified documentation
- Architecture: HIGH - uses official Tauri template structure, standard SolidJS patterns
- Pitfalls: HIGH - all pitfalls verified against Tauri issue tracker and official docs
- IPC patterns: HIGH - code examples sourced directly from Tauri v2 official documentation

**Research date:** 2026-02-26
**Valid until:** 2026-03-28 (stable stack, 30-day validity)
