docs(v1.2): complete project research — stack, features, architecture, pitfalls, summary
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+481
-328
@@ -1,440 +1,593 @@
|
||||
# Architecture Patterns
|
||||
|
||||
**Domain:** Pure frontend multi-step configuration wizard (static site, no backend)
|
||||
**Project:** Ready2Blob
|
||||
**Researched:** 2026-03-26
|
||||
**Confidence:** MEDIUM — rclone.conf format and Intune deployment patterns verified from training data (stable, well-documented domains); client-side download patterns are stable browser APIs; web research unavailable for cross-validation
|
||||
|
||||
---
|
||||
**Domain:** UI polish overhaul -- Material Design 3 theming, dark mode, and component refactoring for existing Tailwind v4 + React wizard app
|
||||
**Researched:** 2026-03-31
|
||||
|
||||
## Recommended Architecture
|
||||
|
||||
### High-Level System Diagram
|
||||
### Overview
|
||||
|
||||
The architecture centers on a **CSS custom properties layer** that bridges Material Design 3 color tokens with Tailwind v4's `@theme` directive. A thin React `ThemeProvider` context manages dark/light state and persists preference to `localStorage`. Components are refactored **bottom-up** from primitives (inputs, buttons, cards) to composed wizard steps, using semantic token names in Tailwind classes rather than hardcoded colors.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Browser (Static App) │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │
|
||||
│ │ Wizard UI │───▶│ Wizard State│───▶│ Config Builders │ │
|
||||
│ │ (Steps/Nav) │ │ (Form Data) │ │ (rclone.conf + │ │
|
||||
│ └──────────────┘ └──────────────┘ │ PS Scripts) │ │
|
||||
│ └────────┬───────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────▼───────────┐ │
|
||||
│ │ Download Manager │ │
|
||||
│ │ (individual files │ │
|
||||
│ │ or ZIP bundle) │ │
|
||||
│ └────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
(file download only)
|
||||
│
|
||||
User's disk
|
||||
+---------------------+
|
||||
| ThemeProvider | React Context
|
||||
| (dark/light state) | localStorage + prefers-color-scheme
|
||||
+---------------------+
|
||||
|
|
||||
sets .dark on <html>
|
||||
|
|
||||
+---------------------+
|
||||
| index.css | CSS custom properties layer
|
||||
| @theme { tokens } | MD3 color roles as --color-*
|
||||
| .dark { overrides }| Dark palette overrides
|
||||
+---------------------+
|
||||
|
|
||||
+---------------------+
|
||||
| Tailwind v4 | Consumes tokens via @theme
|
||||
| bg-surface | Semantic utility classes
|
||||
| text-on-surface |
|
||||
+---------------------+
|
||||
|
|
||||
+---------------+---------------+
|
||||
| | |
|
||||
+----------+ +-----------+ +------------+
|
||||
| ui/ | | wizard/ | | layout/ |
|
||||
| Button | | Steps | | AppShell |
|
||||
| Input | | Indicator | | ThemeToggle|
|
||||
| Card | | AuthToggles| +------------+
|
||||
+----------+ +-----------+
|
||||
```
|
||||
|
||||
No network requests leave the browser. All state is ephemeral (in-memory for the session lifetime).
|
||||
### Component Boundaries
|
||||
|
||||
---
|
||||
| Component | Responsibility | Communicates With | Status |
|
||||
|-----------|---------------|-------------------|--------|
|
||||
| `ThemeProvider` | Manages dark/light state, syncs to DOM and localStorage | `<html>` element class, `useTheme` consumers | **NEW** |
|
||||
| `useTheme` hook | Exposes `{ theme, toggleTheme, setTheme }` | ThemeProvider context | **NEW** |
|
||||
| `ThemeToggle` | UI control for dark/light switch | useTheme hook | **NEW** |
|
||||
| `AppShell` | Outer layout wrapper (background, max-width, header) | ThemeProvider, WizardShell | **NEW** (extracted from App.tsx `WizardShell`) |
|
||||
| `ui/Button` | MD3-styled button with variants (filled, outlined, text) | None (pure presentational) | **NEW** |
|
||||
| `ui/Input` | MD3-styled text input with label and error state | react-hook-form via register | **NEW** (replaces inline input markup in FieldRenderer) |
|
||||
| `ui/Select` | MD3-styled select dropdown | react-hook-form via register | **NEW** (replaces inline select markup in FieldRenderer) |
|
||||
| `ui/Card` | MD3 surface card with elevation | None (pure presentational) | **NEW** |
|
||||
| `ui/FieldRenderer` | Composes Input/Select/PasswordField based on FieldDef | ui/Input, ui/Select, ui/PasswordField | **MODIFIED** -- delegates to primitives |
|
||||
| `ui/PasswordField` | Password input with show/hide toggle | react-hook-form | **MODIFIED** -- uses ui/Input internally |
|
||||
| `ui/BackendCard` | Backend selection card | ui/Card | **MODIFIED** -- uses ui/Card internally |
|
||||
| `wizard/StepIndicator` | Breadcrumb navigation | useWizard | **MODIFIED** -- inline styles to Tailwind + MD3 tokens |
|
||||
| `wizard/*Step` | Step content | useWizard, ui components | **MODIFIED** -- swap hardcoded colors for semantic tokens |
|
||||
|
||||
## Component Boundaries
|
||||
### Data Flow
|
||||
|
||||
| Component | Responsibility | Inputs | Outputs | Communicates With |
|
||||
|-----------|---------------|--------|---------|-------------------|
|
||||
| **Wizard UI** | Render step forms, handle navigation (next/back/jump), validate per-step | User interactions | Step completion events | Wizard State |
|
||||
| **Wizard State** | Single source of truth for all collected form data; tracks current step and completion status | Step form submissions | Reactive state object | Wizard UI (reads), Config Builders (reads) |
|
||||
| **Backend Schema Registry** | Defines the fields required per rclone backend type (Azure Blob, S3, OneDrive, etc.) | Backend type selection | Field definitions for each step | Wizard UI (drives dynamic form rendering) |
|
||||
| **rclone.conf Builder** | Transforms wizard state into a valid rclone.conf string | Wizard State snapshot | `rclone.conf` string | Download Manager |
|
||||
| **PowerShell Script Builder** | Generates PS scripts (Intune Win32 or RMM) from wizard state + optional rclone install flag | Wizard State snapshot, script type selection | `.ps1` string(s) | Download Manager |
|
||||
| **Download Manager** | Packages one or more text files and triggers browser download (individual or ZIP) | File content strings + filenames | Browser file download | rclone.conf Builder, PS Script Builder |
|
||||
**Theme state flow:**
|
||||
|
||||
---
|
||||
1. On app mount, `ThemeProvider` reads `localStorage.getItem('r2b-theme')`
|
||||
2. If no stored preference, checks `window.matchMedia('(prefers-color-scheme: dark)').matches`
|
||||
3. Sets/removes `.dark` class on `document.documentElement`
|
||||
4. Tailwind's `@custom-variant dark` activates `dark:` prefix utilities
|
||||
5. CSS custom properties in `.dark` scope override light palette values
|
||||
6. All components using semantic tokens (`bg-surface`, `text-on-surface`) update automatically
|
||||
|
||||
## Data Flow
|
||||
**Color token flow:**
|
||||
|
||||
```
|
||||
User fills wizard step N
|
||||
│
|
||||
▼
|
||||
Wizard UI validates step N inputs
|
||||
│
|
||||
▼
|
||||
Wizard State updated (merge step N data into central store)
|
||||
│
|
||||
▼
|
||||
User reaches Review/Download step
|
||||
│
|
||||
├──▶ rclone.conf Builder
|
||||
│ reads: [remote_name, backend_type, ...backend-specific fields]
|
||||
│ produces: rclone.conf string
|
||||
│
|
||||
├──▶ PowerShell Script Builder (Intune)
|
||||
│ reads: [remote_name, mount_path, include_install_flag, install_source_url]
|
||||
│ produces: deploy-intune.ps1 string
|
||||
│
|
||||
└──▶ PowerShell Script Builder (RMM)
|
||||
reads: [remote_name, mount_path, include_install_flag]
|
||||
produces: deploy-rmm.ps1 string
|
||||
1. `index.css` defines MD3 color tokens as CSS custom properties in `@theme`
|
||||
2. Light values are defaults; `.dark` class overrides with dark palette values
|
||||
3. Tailwind v4 maps these to utility classes (e.g., `--color-surface` becomes `bg-surface`)
|
||||
4. Components reference semantic names, never raw hex values
|
||||
|
||||
All strings → Download Manager
|
||||
User selects files → individual download (Blob URL) or ZIP (JSZip)
|
||||
```
|
||||
## CSS Custom Properties Strategy for MD3 Color Tokens
|
||||
|
||||
Key invariant: builders are pure functions — same wizard state always produces the same file content. There is no side-effectful build step.
|
||||
### Token Definition in index.css
|
||||
|
||||
---
|
||||
This is the core architectural decision. Tailwind v4 uses `@theme` to define custom theme values consumed as utility classes. MD3 color roles map directly to CSS custom properties.
|
||||
|
||||
## rclone.conf Format (INI-like)
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
|
||||
**Confidence: HIGH** — rclone.conf format is stable and well-documented.
|
||||
/* Enable class-based dark mode toggle */
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
The rclone config file uses a simple INI-like format. Each remote is a named section.
|
||||
/* --- MD3 Color Tokens as Tailwind v4 theme --- */
|
||||
@theme {
|
||||
/* Primary */
|
||||
--color-primary: #1a73e8;
|
||||
--color-on-primary: #ffffff;
|
||||
--color-primary-container: #d3e3fd;
|
||||
--color-on-primary-container: #041e49;
|
||||
|
||||
```ini
|
||||
[remote-name]
|
||||
type = azureblob
|
||||
account = mystorageaccount
|
||||
key = base64encodedaccesskey==
|
||||
```
|
||||
/* Secondary */
|
||||
--color-secondary: #5f6368;
|
||||
--color-on-secondary: #ffffff;
|
||||
--color-secondary-container: #e8eaed;
|
||||
--color-on-secondary-container: #1f1f1f;
|
||||
|
||||
### Structure Rules
|
||||
/* Tertiary (accent) */
|
||||
--color-tertiary: #1a73e8;
|
||||
--color-on-tertiary: #ffffff;
|
||||
--color-tertiary-container: #d3e3fd;
|
||||
--color-on-tertiary-container: #041e49;
|
||||
|
||||
- Section header: `[remote-name]` — any identifier the user chooses; appears in rclone commands as `remote-name:`
|
||||
- Each key-value pair on its own line: `key = value` (spaces around `=` are conventional but optional)
|
||||
- No quoting of values needed (rclone parses raw strings)
|
||||
- Comments: lines starting with `#` or `;`
|
||||
- Multiple remotes = multiple sections in the same file
|
||||
/* Error */
|
||||
--color-error: #dc3545;
|
||||
--color-on-error: #ffffff;
|
||||
--color-error-container: #f9dedc;
|
||||
--color-on-error-container: #410e0b;
|
||||
|
||||
### Required Fields Per Backend
|
||||
/* Surface & Background */
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-dim: #f1f3f4;
|
||||
--color-surface-container: #f8f9fa;
|
||||
--color-surface-container-low: #f1f3f4;
|
||||
--color-surface-container-high: #e8eaed;
|
||||
--color-on-surface: #1f1f1f;
|
||||
--color-on-surface-variant: #5f6368;
|
||||
|
||||
| Backend | `type` value | Minimum required fields | Common optional fields |
|
||||
|---------|-------------|------------------------|------------------------|
|
||||
| Azure Blob | `azureblob` | `account`, then one of: `key`, `sas_url`, or `client_id`+`client_secret`+`tenant` | `endpoint`, `chunk_size`, `upload_cutoff` |
|
||||
| AWS S3 | `s3` | `provider = AWS`, `access_key_id`, `secret_access_key`, `region` | `storage_class`, `server_side_encryption` |
|
||||
| S3-compatible | `s3` | `provider = Other`, `access_key_id`, `secret_access_key`, `endpoint` | varies by provider |
|
||||
| OneDrive | `onedrive` | `client_id`, `client_secret`, `token` (OAuth flow) | `drive_id`, `drive_type` |
|
||||
| SFTP | `sftp` | `host`, `user`, then one of `pass` or `key_file` | `port`, `use_insecure_cipher` |
|
||||
| Google Drive | `drive` | `client_id`, `client_secret`, `token` (OAuth flow) | `team_drive`, `shared_with_me` |
|
||||
/* Outline */
|
||||
--color-outline: #dadce0;
|
||||
--color-outline-variant: #e8eaed;
|
||||
|
||||
### Example Full Config (Azure Blob + S3)
|
||||
/* MD3 Elevation (shadow) */
|
||||
--shadow-elevation-1: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-elevation-2: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-elevation-3: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
|
||||
```ini
|
||||
[my-azure]
|
||||
type = azureblob
|
||||
account = contosostorage
|
||||
key = dGhpcyBpcyBhIHBsYWNlaG9sZGVyIGtleQ==
|
||||
/* MD3 Shape (border-radius) */
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 28px;
|
||||
}
|
||||
|
||||
[my-s3]
|
||||
type = s3
|
||||
provider = AWS
|
||||
access_key_id = AKIAIOSFODNN7EXAMPLE
|
||||
secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
||||
region = us-east-1
|
||||
```
|
||||
/* --- Dark mode overrides --- */
|
||||
@layer base {
|
||||
.dark {
|
||||
--color-primary: #a8c7fa;
|
||||
--color-on-primary: #062e6f;
|
||||
--color-primary-container: #0842a0;
|
||||
--color-on-primary-container: #d3e3fd;
|
||||
|
||||
### Generation Pattern
|
||||
--color-secondary: #c4c7c5;
|
||||
--color-on-secondary: #303030;
|
||||
--color-secondary-container: #444746;
|
||||
--color-on-secondary-container: #e8eaed;
|
||||
|
||||
The rclone.conf builder is pure string templating:
|
||||
--color-tertiary: #a8c7fa;
|
||||
--color-on-tertiary: #062e6f;
|
||||
--color-tertiary-container: #0842a0;
|
||||
--color-on-tertiary-container: #d3e3fd;
|
||||
|
||||
```typescript
|
||||
function buildRcloneConf(remotes: RemoteConfig[]): string {
|
||||
return remotes.map(remote => {
|
||||
const lines = [`[${remote.name}]`, `type = ${remote.type}`];
|
||||
for (const [key, value] of Object.entries(remote.params)) {
|
||||
if (value !== undefined && value !== '') {
|
||||
lines.push(`${key} = ${value}`);
|
||||
--color-error: #f2b8b5;
|
||||
--color-on-error: #601410;
|
||||
--color-error-container: #8c1d18;
|
||||
--color-on-error-container: #f9dedc;
|
||||
|
||||
--color-surface: #1f1f1f;
|
||||
--color-surface-dim: #141414;
|
||||
--color-surface-container: #2d2d2d;
|
||||
--color-surface-container-low: #262626;
|
||||
--color-surface-container-high: #3c3c3c;
|
||||
--color-on-surface: #e8eaed;
|
||||
--color-on-surface-variant: #c4c7c5;
|
||||
|
||||
--color-outline: #5f6368;
|
||||
--color-outline-variant: #444746;
|
||||
|
||||
--shadow-elevation-1: 0 1px 3px 0 rgb(0 0 0 / 0.3);
|
||||
--shadow-elevation-2: 0 2px 6px 0 rgb(0 0 0 / 0.3);
|
||||
--shadow-elevation-3: 0 4px 8px 0 rgb(0 0 0 / 0.3);
|
||||
}
|
||||
}
|
||||
return lines.join('\n');
|
||||
}).join('\n\n');
|
||||
}
|
||||
```
|
||||
|
||||
No library needed — plain string concatenation is the correct approach.
|
||||
### Why This Approach (Not a Component Library)
|
||||
|
||||
---
|
||||
1. **No dependency on MD3 web components** -- `@material/web` is Angular/Lit-oriented and would fight React + Tailwind. The MD3 spec is a *design system*, not a library requirement.
|
||||
2. **Tailwind v4 @theme is the native mechanism** -- tokens defined in `@theme` become first-class Tailwind utilities (`bg-surface`, `text-on-primary`, `shadow-elevation-2`). No plugins needed.
|
||||
3. **Dark mode is a CSS variable swap** -- the `.dark` class override block is all that's needed. No JS re-rendering, no theme prop drilling to every component.
|
||||
4. **Accent color extensibility** -- to support user-selectable accent colors later, just override `--color-primary` and related tokens at runtime via JS on `document.documentElement.style`.
|
||||
|
||||
## Intune / RMM Deployment Patterns
|
||||
### MD3 Color Roles Used (Practical Subset)
|
||||
|
||||
**Confidence: MEDIUM** — Intune Win32 app deployment is well-established; exact script conventions vary by org.
|
||||
Full MD3 has 29+ color roles. For this wizard app, we use a practical subset:
|
||||
|
||||
### Intune Win32 App Approach
|
||||
| MD3 Role | Tailwind Class | Used For |
|
||||
|----------|---------------|----------|
|
||||
| `surface` | `bg-surface` | Page background, card backgrounds |
|
||||
| `on-surface` | `text-on-surface` | Primary text |
|
||||
| `on-surface-variant` | `text-on-surface-variant` | Secondary text, help text |
|
||||
| `surface-dim` | `bg-surface-dim` | Page background (current `bg-gray-50`) |
|
||||
| `surface-container` | `bg-surface-container` | Card fills, input backgrounds |
|
||||
| `surface-container-high` | `bg-surface-container-high` | Elevated cards, active states |
|
||||
| `primary` | `bg-primary`, `text-primary` | Primary buttons, active indicators |
|
||||
| `on-primary` | `text-on-primary` | Text on primary buttons |
|
||||
| `primary-container` | `bg-primary-container` | Selected BackendCard fill |
|
||||
| `on-primary-container` | `text-on-primary-container` | Text on selected BackendCard |
|
||||
| `error` | `text-error` | Validation error text |
|
||||
| `error-container` | `bg-error-container` | Error badge backgrounds |
|
||||
| `outline` | `border-outline` | Input borders, dividers |
|
||||
| `outline-variant` | `border-outline-variant` | Subtle borders |
|
||||
|
||||
Intune Win32 apps require a `.intunewin` package, but PowerShell-only deployments (Intune PowerShell scripts feature) are simpler and sufficient for this use case.
|
||||
### Current-to-Token Mapping
|
||||
|
||||
The generated script must:
|
||||
Explicit mapping of every hardcoded color in the existing codebase:
|
||||
|
||||
1. (Optionally) download and install rclone — copy `rclone.exe` to a stable path (e.g., `C:\ProgramData\rclone\`)
|
||||
2. Write `rclone.conf` to the user-appropriate path (`$env:APPDATA\rclone\rclone.conf` for per-user, or `C:\ProgramData\rclone\rclone.conf` for system-wide)
|
||||
3. (Optionally) create a scheduled task or a startup script to mount the remote on login
|
||||
4. Return exit code 0 on success; non-zero on failure (Intune reads exit codes)
|
||||
| Current Class | Semantic Token | Where Used |
|
||||
|--------------|---------------|------------|
|
||||
| `bg-gray-50` | `bg-surface-dim` | App.tsx page background |
|
||||
| `text-gray-900` | `text-on-surface` | App.tsx heading, BackendCard name |
|
||||
| `text-gray-500` | `text-on-surface-variant` | BackendCard description, help text |
|
||||
| `bg-white` | `bg-surface` | BackendCard default, card backgrounds |
|
||||
| `border-gray-200` | `border-outline-variant` | BackendCard default border |
|
||||
| `border-gray-300` | `border-outline` | Input borders, Back button border |
|
||||
| `hover:border-blue-400` | `hover:border-primary/60` | BackendCard hover |
|
||||
| `hover:bg-gray-50` | `hover:bg-surface-container` | BackendCard hover, Back button hover |
|
||||
| `border-blue-600` | `border-primary` | BackendCard selected border |
|
||||
| `bg-blue-50` | `bg-primary-container` | BackendCard selected fill, tooltip bg |
|
||||
| `bg-blue-600` | `bg-primary` | Next/primary buttons |
|
||||
| `text-white` (on blue bg) | `text-on-primary` | Primary button text |
|
||||
| `hover:bg-blue-700` | `hover:bg-primary/90` | Primary button hover |
|
||||
| `text-red-500` | `text-error` | Required field asterisk |
|
||||
| `text-red-600` | `text-error` | Error message text |
|
||||
| `border-red-500` | `border-error` | Error input border |
|
||||
| `focus:ring-blue-300` | `focus:ring-primary/40` | Input focus ring |
|
||||
| `focus:ring-red-300` | `focus:ring-error/40` | Error input focus ring |
|
||||
| `text-blue-500` | `text-primary` | Tooltip toggle button |
|
||||
| `text-blue-700` | `text-on-primary-container` | Tooltip text |
|
||||
| `border-blue-200` | `border-primary-container` | Tooltip border |
|
||||
| `text-gray-400` | `text-on-surface-variant` | Password show/hide button |
|
||||
| `hover:text-gray-700` | `hover:text-on-surface` | Password show/hide hover |
|
||||
| `#999` (inline) | `text-on-surface-variant` | StepIndicator future steps |
|
||||
| `fontWeight: bold` (inline) | `font-bold` | StepIndicator active step |
|
||||
| `fontWeight: normal` (inline) | `font-normal` | StepIndicator completed step |
|
||||
|
||||
### Intune Script Deployment Constraints
|
||||
## ThemeProvider Architecture
|
||||
|
||||
- Scripts run as SYSTEM by default, or as logged-on user (configurable)
|
||||
- Script must handle the case where rclone is already installed (idempotent)
|
||||
- 64-bit PowerShell is required for `rclone.exe` (32-bit PS cannot run 64-bit binaries reliably)
|
||||
- Scripts have a 30-minute execution timeout in Intune
|
||||
- Output directory for config must account for execution context (SYSTEM vs user)
|
||||
### Implementation
|
||||
|
||||
### RMM Script Approach (NinjaRMM, Datto, etc.)
|
||||
```typescript
|
||||
// src/store/theme-context.tsx
|
||||
import { createContext, useContext, useEffect, useState, useCallback } from 'react';
|
||||
|
||||
Simpler than Intune Win32: paste PS script, run as SYSTEM or user. Same functional requirements as above but no `.intunewin` packaging. Script should be self-contained.
|
||||
type Theme = 'light' | 'dark';
|
||||
|
||||
### PowerShell Script Structure
|
||||
interface ThemeContextValue {
|
||||
theme: Theme;
|
||||
toggleTheme: () => void;
|
||||
setTheme: (theme: Theme) => void;
|
||||
}
|
||||
|
||||
```powershell
|
||||
#Requires -RunAsAdministrator # or omit if running as user context
|
||||
const ThemeContext = createContext<ThemeContextValue | null>(null);
|
||||
|
||||
$rclonePath = "C:\ProgramData\rclone"
|
||||
$rcloneExe = "$rclonePath\rclone.exe"
|
||||
$rcloneConf = "$rclonePath\rclone.conf"
|
||||
function getInitialTheme(): Theme {
|
||||
if (typeof window === 'undefined') return 'light';
|
||||
const stored = localStorage.getItem('r2b-theme') as Theme | null;
|
||||
if (stored === 'light' || stored === 'dark') return stored;
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
# --- Optional: Install rclone ---
|
||||
# if ($InstallRclone) { ... download from $RcloneDownloadUrl ... }
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
const [theme, setThemeState] = useState<Theme>(getInitialTheme);
|
||||
|
||||
# --- Write config ---
|
||||
$confContent = @"
|
||||
[remote-name]
|
||||
type = azureblob
|
||||
account = REPLACE_ME
|
||||
key = REPLACE_ME
|
||||
"@
|
||||
const setTheme = useCallback((t: Theme) => {
|
||||
setThemeState(t);
|
||||
localStorage.setItem('r2b-theme', t);
|
||||
document.documentElement.classList.toggle('dark', t === 'dark');
|
||||
}, []);
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $rclonePath | Out-Null
|
||||
Set-Content -Path $rcloneConf -Value $confContent -Encoding UTF8
|
||||
const toggleTheme = useCallback(() => {
|
||||
setTheme(theme === 'dark' ? 'light' : 'dark');
|
||||
}, [theme, setTheme]);
|
||||
|
||||
# --- Optional: Register mount as scheduled task ---
|
||||
# ...
|
||||
// Sync on mount
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.toggle('dark', theme === 'dark');
|
||||
}, []);
|
||||
|
||||
exit 0
|
||||
return (
|
||||
<ThemeContext.Provider value={{ theme, toggleTheme, setTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useTheme(): ThemeContextValue {
|
||||
const ctx = useContext(ThemeContext);
|
||||
if (!ctx) throw new Error('useTheme must be used inside <ThemeProvider>');
|
||||
return ctx;
|
||||
}
|
||||
```
|
||||
|
||||
The PS script builder generates this structure by substituting values from wizard state into a template string.
|
||||
### Provider Nesting in App.tsx
|
||||
|
||||
---
|
||||
```typescript
|
||||
// ThemeProvider wraps WizardProvider -- theme is app-global, wizard state is feature-scoped
|
||||
export default function App() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<WizardProvider>
|
||||
<AppShell />
|
||||
</WizardProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Rationale:** ThemeProvider is outermost because theme affects the entire DOM tree. WizardProvider is inner because it only governs wizard state. They are independent -- no cross-dependencies.
|
||||
|
||||
## Component Refactoring Approach: Bottom-Up Primitives
|
||||
|
||||
### Why Bottom-Up (Not Top-Down)
|
||||
|
||||
1. **Primitives are the reuse boundary** -- Input, Button, Card are used by multiple wizard steps. Fix once, propagate everywhere.
|
||||
2. **Tests target behavior, not styles** -- existing 159 tests use Testing Library (query by role, text, label). Changing CSS classes does not break tests. Changing component structure (splitting FieldRenderer) could break tests if DOM hierarchy changes.
|
||||
3. **Incremental migration** -- each primitive can be built, tested, and swapped in isolation. No big-bang rewrite.
|
||||
|
||||
### Refactoring Layers
|
||||
|
||||
```
|
||||
Layer 1: CSS Foundation (index.css)
|
||||
- MD3 tokens in @theme
|
||||
- Dark mode @custom-variant + .dark overrides
|
||||
- Zero component changes needed
|
||||
- Zero test impact
|
||||
|
||||
Layer 2: Primitive Components (ui/)
|
||||
- NEW: Button, Input, Select, Card
|
||||
- Tests: New tests for new components only
|
||||
|
||||
Layer 3: Composed Components (ui/)
|
||||
- MODIFIED: FieldRenderer -- delegates to Input/Select/PasswordField
|
||||
- MODIFIED: BackendCard -- delegates to Card
|
||||
- MODIFIED: PasswordField -- uses Input primitive internally
|
||||
- Tests: Existing tests should pass (same DOM semantics, different styling)
|
||||
|
||||
Layer 4: Wizard Steps + StepIndicator (wizard/)
|
||||
- MODIFIED: Replace hardcoded Tailwind classes with semantic tokens
|
||||
- MODIFIED: StepIndicator -- inline styles to Tailwind classes
|
||||
- Tests: Existing tests should pass (no behavior change)
|
||||
|
||||
Layer 5: Layout Shell + Polish
|
||||
- NEW: AppShell (extracted from WizardShell in App.tsx)
|
||||
- NEW: ThemeToggle
|
||||
- MODIFIED: App.tsx -- adds ThemeProvider, uses AppShell
|
||||
- Tests: App.test.tsx needs update for new provider wrapping
|
||||
```
|
||||
|
||||
### Migration Pattern Per Component
|
||||
|
||||
For each existing component, the migration follows this pattern:
|
||||
|
||||
1. **Replace hardcoded colors with semantic tokens** using the Current-to-Token Mapping table above
|
||||
2. **Add `dark:` variants only where semantic tokens alone are insufficient** (should be rare -- the CSS variable swap handles most cases automatically)
|
||||
3. **Replace inline styles with Tailwind classes** (StepIndicator specific)
|
||||
4. **Run existing tests after each component** -- they should pass unchanged
|
||||
|
||||
## Patterns to Follow
|
||||
|
||||
### Pattern 1: Centralized Wizard State (Single Store)
|
||||
### Pattern 1: Semantic Color Tokens Only
|
||||
|
||||
**What:** All form data lives in one top-level state object, passed down or accessed via context/store. Steps read from and write to slices of this store.
|
||||
**What:** Never use raw Tailwind color classes (`blue-600`, `gray-50`) in components. Always use semantic MD3 token names.
|
||||
|
||||
**When:** Any multi-step form where later steps depend on earlier choices (e.g., backend type selection in step 1 drives which fields appear in step 2).
|
||||
**When:** Every component, every color reference.
|
||||
|
||||
**Why:** Avoids prop-drilling, makes "go back and edit" trivial, makes config builders pure functions with a single well-typed input.
|
||||
**Why:** Semantic tokens automatically adapt to dark mode via CSS variable override. Raw colors would need manual `dark:` overrides on every single usage.
|
||||
|
||||
**Shape:**
|
||||
```typescript
|
||||
interface WizardState {
|
||||
currentStep: number;
|
||||
remote: {
|
||||
name: string;
|
||||
backendType: BackendType;
|
||||
params: Record<string, string>; // backend-specific key/value pairs
|
||||
};
|
||||
deployment: {
|
||||
includeInstall: boolean;
|
||||
installSource: 'github' | 'custom';
|
||||
customInstallUrl?: string;
|
||||
mountPath?: string;
|
||||
scriptTargets: ('intune' | 'rmm')[];
|
||||
};
|
||||
outputOptions: {
|
||||
includeConf: boolean;
|
||||
includeIntune: boolean;
|
||||
includeRmm: boolean;
|
||||
bundleAsZip: boolean;
|
||||
};
|
||||
**Example:**
|
||||
```tsx
|
||||
// WRONG -- requires dark: override on every element
|
||||
<button className="bg-blue-600 text-white dark:bg-blue-400 dark:text-gray-900">
|
||||
|
||||
// RIGHT -- adapts automatically via CSS variable swap
|
||||
<button className="bg-primary text-on-primary">
|
||||
```
|
||||
|
||||
### Pattern 2: Component Composition Over Monoliths
|
||||
|
||||
**What:** Extract reusable UI primitives (Button, Input, Card) from inline markup.
|
||||
|
||||
**When:** Any UI element used in 2+ places, or any element with complex styling logic.
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
// BEFORE: Inline button styling in every step component
|
||||
<button className="px-4 py-2 text-sm bg-primary text-on-primary rounded-md hover:bg-primary/90">
|
||||
Next
|
||||
</button>
|
||||
|
||||
// AFTER: Reusable Button primitive
|
||||
<Button variant="filled">Next</Button>
|
||||
<Button variant="outlined">Back</Button>
|
||||
<Button variant="text">Cancel</Button>
|
||||
```
|
||||
|
||||
### Pattern 3: Test Helper for Theme Context
|
||||
|
||||
**What:** Create a test utility that wraps components in both ThemeProvider and WizardProvider.
|
||||
|
||||
**When:** Any component test that renders a component needing theme context.
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
// src/test-utils.tsx
|
||||
import { render } from '@testing-library/react';
|
||||
import { ThemeProvider } from './store/theme-context';
|
||||
import { WizardProvider } from './store/context';
|
||||
|
||||
export function renderWithProviders(ui: React.ReactElement) {
|
||||
return render(
|
||||
<ThemeProvider>
|
||||
<WizardProvider>
|
||||
{ui}
|
||||
</WizardProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Backend Schema Registry
|
||||
### Pattern 4: MD3 Elevation via Shadow Tokens
|
||||
|
||||
**What:** A static data structure (not code) that defines, per backend type, which fields are required, their labels, input types, placeholder text, and validation rules.
|
||||
**What:** Use the `shadow-elevation-*` tokens for card depth instead of arbitrary shadow utilities.
|
||||
|
||||
**When:** The wizard needs to render dynamic forms based on which rclone backend the user selected.
|
||||
**When:** Cards, modals, dropdowns -- any elevated surface.
|
||||
|
||||
**Why:** Adding support for a new backend means adding one entry to the registry, not writing new UI components. Keeps UI code backend-agnostic.
|
||||
**Example:**
|
||||
```tsx
|
||||
// Card at rest
|
||||
<div className="bg-surface-container rounded-lg shadow-elevation-1">
|
||||
|
||||
**Shape:**
|
||||
```typescript
|
||||
interface FieldDef {
|
||||
key: string; // matches rclone config key exactly
|
||||
label: string;
|
||||
inputType: 'text' | 'password' | 'select' | 'toggle';
|
||||
required: boolean;
|
||||
placeholder?: string;
|
||||
helpText?: string;
|
||||
options?: { value: string; label: string }[]; // for select
|
||||
}
|
||||
|
||||
type BackendSchema = Record<BackendType, FieldDef[]>;
|
||||
// Card hovered / elevated
|
||||
<div className="bg-surface-container rounded-lg shadow-elevation-2 hover:shadow-elevation-3">
|
||||
```
|
||||
|
||||
### Pattern 3: Pure Builder Functions
|
||||
|
||||
**What:** Config and script builders are pure functions — they receive wizard state and return a string. No side effects, no DOM access, no async.
|
||||
|
||||
**When:** Always — this is the core generation logic.
|
||||
|
||||
**Why:** Easily testable (unit test: given state X, output matches expected string). Deterministic. Separates concerns cleanly.
|
||||
|
||||
### Pattern 4: Blob URL Download
|
||||
|
||||
**What:** To trigger a file download in the browser without a server, create a `Blob` from the string content, generate an object URL, attach it to an `<a>` element, and programmatically click it.
|
||||
|
||||
**When:** Single file download.
|
||||
|
||||
```typescript
|
||||
function downloadTextFile(filename: string, content: string): void {
|
||||
const blob = new Blob([content], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
```
|
||||
|
||||
**Confidence: HIGH** — Blob URL download is a standard, well-supported browser API (all modern browsers, no library needed for single files).
|
||||
|
||||
### Pattern 5: ZIP Bundle via JSZip
|
||||
|
||||
**What:** When the user wants all generated files in one download, use JSZip to assemble a ZIP in-memory and trigger download.
|
||||
|
||||
**When:** Multi-file download (rclone.conf + one or more .ps1 files).
|
||||
|
||||
```typescript
|
||||
import JSZip from 'jszip';
|
||||
|
||||
async function downloadZip(files: { name: string; content: string }[]): Promise<void> {
|
||||
const zip = new JSZip();
|
||||
files.forEach(f => zip.file(f.name, f.content));
|
||||
const blob = await zip.generateAsync({ type: 'blob' });
|
||||
downloadTextFile('ready2blob-deployment.zip', URL.createObjectURL(blob));
|
||||
}
|
||||
```
|
||||
|
||||
**Confidence: HIGH** — JSZip is the established library for client-side ZIP creation. FileSaver.js is an optional companion for older browser compatibility but not required with the Blob URL pattern above.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
### Anti-Pattern 1: Per-Step Local State
|
||||
### Anti-Pattern 1: Dual-Track Color System
|
||||
|
||||
**What:** Each wizard step manages its own form state with no shared store.
|
||||
**What:** Keeping some components on raw Tailwind colors while migrating others to MD3 tokens.
|
||||
|
||||
**Why bad:** When the user navigates back to step 2 from step 4, their inputs are gone. The config builder cannot access step 2 data from step 4. Breaks the "review before download" pattern.
|
||||
**Why bad:** Dark mode will be broken on non-migrated components. Visual inconsistency. Harder to maintain.
|
||||
|
||||
**Instead:** Lift all state to the wizard root. Steps only control their own UI (focus, error display), not their data.
|
||||
**Instead:** Migrate ALL color references in one sweep per component. The token names are a 1:1 replacement -- no logic changes needed.
|
||||
|
||||
### Anti-Pattern 2: Generating Files Only at Download Time from DOM
|
||||
### Anti-Pattern 2: JS-Driven Theme Prop Drilling
|
||||
|
||||
**What:** Reading form field values directly from the DOM to build the config string at download time.
|
||||
**What:** Passing `isDark` or `theme` as a prop to every component and using ternaries to pick colors.
|
||||
|
||||
**Why bad:** Bypasses validation, couples the builder to the DOM structure, cannot unit-test without a browser. Fragile.
|
||||
**Why bad:** Massive prop threading, re-renders on theme change, duplicated color logic.
|
||||
|
||||
**Instead:** Always read from wizard state, which is the validated, typed representation of user input.
|
||||
**Instead:** CSS custom properties handle the switch. Components just use `bg-surface` -- the browser resolves the correct value based on `.dark` class presence. Only `ThemeToggle` needs to consume `useTheme`.
|
||||
|
||||
### Anti-Pattern 3: Hardcoding Backend Fields in Step Components
|
||||
### Anti-Pattern 3: Using @material/web Components
|
||||
|
||||
**What:** Writing a dedicated `<AzureBlobStep />`, `<S3Step />`, `<OneDriveStep />` component for every backend.
|
||||
**What:** Importing Google's Material Web Components (`<md-button>`, `<md-text-field>`).
|
||||
|
||||
**Why bad:** Adding a new backend requires a new component. Does not scale. Leads to duplication of validation logic.
|
||||
**Why bad:** These are web components designed for vanilla JS / Lit. They fight React's rendering model, do not integrate with react-hook-form's `register()`, and add significant bundle size. They use Shadow DOM which conflicts with Tailwind's utility approach.
|
||||
|
||||
**Instead:** Use the Backend Schema Registry to drive a single generic `<DynamicBackendStep />` that renders fields from schema definitions.
|
||||
**Instead:** Implement MD3's *visual language* (colors, elevation, shape, typography) via CSS tokens + Tailwind utilities on standard React elements.
|
||||
|
||||
### Anti-Pattern 4: Storing Secrets Beyond Session
|
||||
### Anti-Pattern 4: Big-Bang Step Rewrite
|
||||
|
||||
**What:** Persisting wizard state to `localStorage`, `sessionStorage`, or any cache.
|
||||
**What:** Rewriting entire wizard step components at once with new UI.
|
||||
|
||||
**Why bad:** Credentials (storage keys, SAS tokens) would persist on the machine after the browser tab is closed. Security risk explicitly called out in project constraints.
|
||||
**Why bad:** Breaks multiple tests simultaneously. Hard to isolate regressions. Merge conflicts if concurrent work.
|
||||
|
||||
**Instead:** Wizard state lives only in in-memory React/Vue/Svelte state. Closing the tab is the only "logout".
|
||||
**Instead:** Layer-by-layer approach. CSS tokens first (zero breakage), then primitives (additive), then swap in composed components, then layout.
|
||||
|
||||
### Anti-Pattern 5: Using a Backend for File Generation
|
||||
## Impact on Existing Test Suite (159 Tests)
|
||||
|
||||
**What:** Sending form data to a server to generate files, which returns them as downloads.
|
||||
### Test Categories and Impact Assessment
|
||||
|
||||
**Why bad:** Server receives credentials in plaintext. Violates the project's explicit no-backend constraint. Creates data retention risk.
|
||||
| Test File | Tests (approx) | Impact | Reason |
|
||||
|-----------|----------------|--------|--------|
|
||||
| `schemas/index.test.ts` | ~20 | NONE | Tests Zod schemas, no UI |
|
||||
| `schemas/registry.test.ts` | ~15 | NONE | Tests BACKEND_REGISTRY data, no UI |
|
||||
| `generators/*.test.ts` (3 files) | ~40 | NONE | Tests script generation, no UI |
|
||||
| `store/reducer.test.ts` | ~20 | NONE | Tests state reducer, no UI |
|
||||
| `App.test.tsx` | ~10 | LOW | May need ThemeProvider wrapper if test renders App internals directly |
|
||||
| `StepIndicator.test.tsx` | ~10 | LOW | Tests click behavior and text content, not styles |
|
||||
| `BackendSelectionStep.test.tsx` | ~15 | LOW | Tests card selection behavior via role queries |
|
||||
| `RemoteConfigStep.test.tsx` | ~15 | LOW | Tests form submission and validation |
|
||||
| `ReviewStep.test.tsx` | ~15 | LOW | Tests output generation, copy/download actions |
|
||||
|
||||
**Instead:** All generation is client-side (builders are pure TS/JS functions).
|
||||
**Key insight:** ~60% of tests (95+ tests across schemas, generators, reducer) are pure logic tests with ZERO UI coupling. They will not be affected at all.
|
||||
|
||||
---
|
||||
**For UI tests:** Testing Library queries by role, label text, and accessible names -- NOT by CSS class names. Swapping `bg-blue-600` to `bg-primary` does not change what `getByRole('button')` or `getByText('Next')` finds. These tests should remain green through the entire migration.
|
||||
|
||||
## Suggested Build Order (Component Dependencies)
|
||||
**One risk area:** If `FieldRenderer` refactoring changes DOM structure (e.g., wrapping inputs in a new primitive component that adds an extra `<div>`), tests that use `container.querySelector` or rely on specific nesting could break. Mitigation: ensure new primitives produce the same semantic DOM (same `<input>`, `<select>`, `<label>` elements with same attributes).
|
||||
|
||||
Dependencies flow from foundational to dependent. Build in this order:
|
||||
## Suggested Build Order (Minimizes Breakage)
|
||||
|
||||
```
|
||||
Phase 1 — Foundation
|
||||
└── Wizard State shape definition (TypeScript types + store setup)
|
||||
└── Backend Schema Registry (static data, no UI)
|
||||
### Phase 1: CSS Foundation (Zero Test Impact)
|
||||
|
||||
Phase 2 — Core Generators (no UI needed yet, fully testable)
|
||||
└── rclone.conf Builder (pure function, unit-testable immediately)
|
||||
└── PowerShell Script Builder — Intune variant
|
||||
└── PowerShell Script Builder — RMM variant
|
||||
└── Download Manager (Blob URL + JSZip wrapper)
|
||||
**What:** Set up MD3 tokens and dark mode infrastructure in `index.css`. No component changes.
|
||||
|
||||
Phase 3 — Wizard UI Shell
|
||||
└── Step navigation (stepper, next/back, step completion tracking)
|
||||
└── Wizard State wired to UI (reads/writes)
|
||||
1. Add `@custom-variant dark (&:where(.dark, .dark *))` to `index.css`
|
||||
2. Define all MD3 color tokens in `@theme` block
|
||||
3. Add `.dark` override block in `@layer base`
|
||||
4. Define elevation shadow tokens and border-radius shape tokens
|
||||
5. Create `ThemeProvider` + `useTheme` hook (new files, no existing code touched)
|
||||
6. Wire `ThemeProvider` into `App.tsx` (outermost wrapper)
|
||||
|
||||
Phase 4 — Dynamic Step Forms
|
||||
└── Backend type selector (step 1)
|
||||
└── Dynamic backend fields step (step 2, driven by Schema Registry)
|
||||
└── Deployment options step (step 3)
|
||||
└── Review + Download step (step 4, calls builders + download manager)
|
||||
**Test impact:** Zero -- existing tests pass unchanged. Add new unit tests for ThemeProvider.
|
||||
|
||||
Phase 5 — Polish
|
||||
└── Per-step validation with user-visible errors
|
||||
└── Security warning modal before download
|
||||
└── Preview pane (show generated file content before download)
|
||||
```
|
||||
**Dependency:** None. Can start immediately.
|
||||
|
||||
**Rationale for this order:**
|
||||
### Phase 2: Primitive Extraction (Additive Only)
|
||||
|
||||
- Builders and the schema registry have zero UI dependencies — build and test them first
|
||||
- The wizard UI shell (navigation only) can be built against mock/empty state
|
||||
- Dynamic forms are built last because they depend on both state wiring AND the schema registry being final
|
||||
- The download step can only be meaningfully built once all builders exist
|
||||
**What:** Create new ui/ primitives without modifying existing components yet.
|
||||
|
||||
---
|
||||
1. Create `ui/Button` with MD3 variants (filled, outlined, text)
|
||||
2. Create `ui/Input` with MD3 styling (outline, label, error state, focus ring)
|
||||
3. Create `ui/Select` with MD3 styling
|
||||
4. Create `ui/Card` with MD3 elevation and shape
|
||||
5. Create `ThemeToggle` component
|
||||
6. Create `AppShell` layout component
|
||||
|
||||
**Test impact:** Zero on existing tests. Write new tests for each new component.
|
||||
|
||||
**Dependency:** Phase 1 tokens must be in place for correct color references.
|
||||
|
||||
### Phase 3: StepIndicator Migration (Low Risk, Isolated)
|
||||
|
||||
**What:** Replace inline styles with Tailwind + MD3 tokens. This is flagged tech debt.
|
||||
|
||||
1. Replace `style={{ fontWeight: 'bold' }}` with `className="font-bold"`
|
||||
2. Replace `style={{ color: '#999' }}` with `className="text-on-surface-variant"`
|
||||
3. Replace `style={{ fontWeight: 'normal' }}` with `className="font-normal"`
|
||||
4. Apply MD3 shape and color tokens to step indicator layout
|
||||
|
||||
**Test impact:** `StepIndicator.test.tsx` tests click behavior and text content. Style changes are invisible to these tests. Should pass unchanged.
|
||||
|
||||
**Dependency:** Phase 1 tokens.
|
||||
|
||||
### Phase 4: Component Token Migration (Core Migration)
|
||||
|
||||
**What:** Replace hardcoded Tailwind colors with semantic MD3 tokens across all components. Integrate new primitives.
|
||||
|
||||
1. `BackendCard` -- swap color classes using Current-to-Token Mapping, optionally compose with Card
|
||||
2. `FieldRenderer` -- delegate rendering to Input/Select primitives (preserve DOM semantics)
|
||||
3. `PasswordField` -- swap color classes, optionally use Input internally
|
||||
4. `DeploymentStep` -- swap button and input color classes
|
||||
5. `ReviewStep` -- swap color classes
|
||||
6. `RemoteConfigStep` -- swap color classes
|
||||
7. `BackendSelectionStep` -- swap color classes
|
||||
|
||||
**Test impact:** LOW. Color class changes are invisible to Testing Library. If FieldRenderer's DOM structure changes, run tests after each sub-step to catch issues early.
|
||||
|
||||
**Dependency:** Phase 1 tokens + Phase 2 primitives.
|
||||
|
||||
### Phase 5: Layout Shell and Dark Mode UX
|
||||
|
||||
**What:** AppShell integration, ThemeToggle placement, responsive improvements.
|
||||
|
||||
1. Extract layout from WizardShell into AppShell component
|
||||
2. Add ThemeToggle to app header area
|
||||
3. Wire up dark mode persistence (already in ThemeProvider)
|
||||
4. Mobile responsiveness passes
|
||||
5. Update `App.test.tsx` for new structure
|
||||
|
||||
**Test impact:** `App.test.tsx` may need structural updates. Other tests unaffected.
|
||||
|
||||
**Dependency:** All previous phases.
|
||||
|
||||
## Scalability Considerations
|
||||
|
||||
| Concern | At MVP (5 backends) | At Growth (20+ backends) | Notes |
|
||||
|---------|---------------------|--------------------------|-------|
|
||||
| Backend support | Hardcode schema for top 5 | Schema registry makes adding trivial | Registry pattern is the key enabler |
|
||||
| Bundle size | Single JS bundle is fine | Consider lazy-loading backend schemas | Each schema is tiny; not a real concern until 50+ backends |
|
||||
| State complexity | Flat wizard state struct | No change needed | Wizard is inherently linear; state stays simple |
|
||||
| Testing | Unit tests on builders | Add snapshot tests for generated files | Builders are pure functions — easiest thing to test |
|
||||
| Localization | Not needed v1 | Field labels/help text in schema enables i18n | Plan label strings as separate keys in schema if i18n is future |
|
||||
|
||||
---
|
||||
|
||||
## Key Technical Decisions
|
||||
|
||||
| Decision | Recommended Choice | Rationale |
|
||||
|----------|-------------------|-----------|
|
||||
| State management | React Context + useReducer OR Zustand (lightweight store) | No server state; no need for React Query or Redux. Zustand reduces boilerplate vs Context for this use case. |
|
||||
| ZIP library | JSZip v3 | De facto standard for browser ZIP. No alternatives with meaningful adoption. |
|
||||
| File download | Native Blob URL API | No library needed. FileSaver.js unnecessary for modern browsers. |
|
||||
| Config generation | Plain string templates (template literals) | rclone.conf is simple enough that a template engine adds no value. |
|
||||
| Script generation | Template literal function per script type | Same reasoning. Mustache/Handlebars would be overkill. |
|
||||
| Backend schema | Static TypeScript object (no database, no fetch) | Schemas are known at build time. Static data = zero loading time. |
|
||||
|
||||
---
|
||||
| Concern | Current (v1.2) | Future (accent colors) | Future (multi-theme) |
|
||||
|---------|----------------|----------------------|---------------------|
|
||||
| Color tokens | ~20 tokens in @theme | Override `--color-primary` family via JS `document.documentElement.style` | Add named theme classes, swap `.theme-blue` / `.theme-green` |
|
||||
| Theme persistence | localStorage `r2b-theme` key | Add `r2b-accent` key | Add `r2b-theme-name` key |
|
||||
| Bundle size | +0 KB (CSS only) | +~3KB if using `@material/material-color-utilities` for seed-based palette generation | Same |
|
||||
| Performance | CSS variable swap (no React re-render for color changes) | One-time JS computation + CSS variable batch update | Same |
|
||||
| Token generation | Hand-picked values | Use `@material/material-color-utilities` `themeFromSourceColor()` to generate all 29 tokens from one seed hex | Same |
|
||||
|
||||
## Sources
|
||||
|
||||
- rclone.conf format: training data (stable since rclone v1.x; format has not changed); confidence HIGH
|
||||
- Azure Blob rclone backend fields: training data; confidence MEDIUM (specific field names should be cross-checked against https://rclone.org/azureblob/ before implementing the schema registry)
|
||||
- Intune PowerShell script deployment: training data; confidence MEDIUM (execution context and timeout limits are well-documented but should be verified for current Intune behavior)
|
||||
- JSZip client-side ZIP: training data; confidence HIGH (library API is stable)
|
||||
- Blob URL download pattern: training data (standard browser API, MDN-documented); confidence HIGH
|
||||
- Web search and WebFetch unavailable during this research session — claims marked MEDIUM should be verified against official docs during implementation phases
|
||||
- [Tailwind CSS v4 Dark Mode documentation](https://tailwindcss.com/docs/dark-mode) -- HIGH confidence, official docs
|
||||
- [Material Design 3 Color Roles](https://m3.material.io/styles/color/roles) -- HIGH confidence, official spec
|
||||
- [Material Design 3 Design Tokens](https://m3.material.io/foundations/design-tokens) -- HIGH confidence, official spec
|
||||
- [Material Design 3 Elevation Tokens](https://m3.material.io/styles/elevation/tokens) -- HIGH confidence, official spec
|
||||
- [Tailwind v4 dark mode @custom-variant discussion](https://github.com/tailwindlabs/tailwindcss/discussions/15083) -- MEDIUM confidence, community verified pattern
|
||||
- [@material/material-color-utilities npm](https://www.npmjs.com/package/@material/material-color-utilities) -- HIGH confidence, official Google package
|
||||
- [Generating MD3 Dynamic Color with JavaScript](https://dt.in.th/M3DynamicColorJS) -- MEDIUM confidence, verified implementation walkthrough
|
||||
- [React dark mode with Context + Tailwind pattern](https://medium.com/@sandeepshome.dev/react-theming-dark-mode-with-context-api-and-tailwindcss-b3ef50a9522b) -- MEDIUM confidence, community pattern
|
||||
- [Tailwind v4 @theme with dark mode pattern](https://medium.com/@kevstrosky/theme-colors-with-tailwind-css-v4-0-and-next-themes-dark-light-custom-mode-36dca1e20419) -- MEDIUM confidence, community implementation
|
||||
|
||||
+178
-102
@@ -1,144 +1,220 @@
|
||||
# Feature Landscape
|
||||
# Feature Research
|
||||
|
||||
**Domain:** rclone configuration wizard / enterprise deployment helper
|
||||
**Project:** Ready2Blob
|
||||
**Researched:** 2026-03-26
|
||||
**Confidence note:** External research tools (WebSearch, WebFetch, Bash) were unavailable in this session. All findings are from training data (knowledge cutoff August 2025). Confidence levels are assigned conservatively. Recommend validating against live rclone docs and community forums before finalizing.
|
||||
**Domain:** Material Design 3 UI polish for rclone configuration wizard
|
||||
**Project:** Ready2Blob v1.2
|
||||
**Researched:** 2026-03-31
|
||||
**Confidence:** MEDIUM (MD3 web patterns well-documented; custom Tailwind implementation patterns less established than MUI-based approaches)
|
||||
|
||||
---
|
||||
## Feature Landscape
|
||||
|
||||
## Table Stakes
|
||||
This research covers the UI polish milestone only. The functional wizard (4 steps, 7 backends, validation, ZIP download) is already shipped. The question is: what transforms a bare, functional wizard into a professional, self-explanatory tool?
|
||||
|
||||
Features IT admins expect. Missing any of these means the tool gets discarded immediately.
|
||||
### Table Stakes (Users Expect These)
|
||||
|
||||
Features that any polished web form/wizard must have in 2026. Without these, the app looks like a prototype.
|
||||
|
||||
| Feature | Why Expected | Complexity | Notes |
|
||||
|---------|--------------|------------|-------|
|
||||
| Multi-step backend selection wizard | rclone has 50+ backends; admins need guided selection, not raw docs | Medium | First screen should show backends sorted by popularity: Azure Blob, S3, OneDrive, SFTP, then others |
|
||||
| Per-backend field forms with labels | Each backend has different required fields (account name vs access key vs OAuth token); forms must match | Medium | Source of truth is rclone's own `rclone config` flow; replicate those fields exactly |
|
||||
| Valid rclone.conf output | The generated file must be parseable by rclone with no errors | Low | INI-like format: `[remote-name]`, `type = azureblob`, then key=value pairs. Pure string generation |
|
||||
| Remote name customization | Admins name remotes to match org conventions (e.g., `corp-backup`, `client-files`) | Low | Single text input, validated to allow only rclone-safe characters (alphanumeric, dash, underscore) |
|
||||
| Intune PowerShell deployment script | Intune Win32 app or PS script deployment is the dominant MDM workflow for Windows | High | Must handle: detection script, install script, optional rclone.exe download, config placement at correct path |
|
||||
| RMM deployment script | NinjaRMM, Datto RMM, ConnectWise Automate, Syncro — MSP-dominant tools | High | Single PS script that downloads rclone if needed and drops config; simpler than Intune (no detection logic needed) |
|
||||
| Optional rclone install inclusion | Some orgs already have rclone in their baseline image; others don't | Medium | Checkbox: "Include rclone installation". If checked, script downloads from rclone.org/downloads or GitHub releases |
|
||||
| Security warning before download | Credentials are in plain text in generated files — legal/compliance exposure if admin doesn't understand | Low | Modal or banner: "This file contains your storage credentials in plain text. Store and transmit securely." Must be impossible to miss |
|
||||
| Download individual output files | Admin may only need the .conf, or only the script, depending on their environment | Low | Separate download buttons for each generated artifact |
|
||||
| No data sent to server | IT security teams will ask "where do my credentials go?" — answer must be "nowhere, browser only" | Low | Static site + client-side generation. Prominently state this in UI |
|
||||
| **MD3 text field styling (outlined variant)** | Outlined text fields with floating labels are the modern standard for form inputs. The current bare `<input>` with static labels above looks dated. | MEDIUM | MD3 "outlined" style: 1px border that highlights on focus, label that floats into the border on focus/fill. Requires CSS-only animation or a small wrapper component. Tailwind v4 `@theme` tokens map well to MD3 color roles. |
|
||||
| **MD3 button hierarchy (filled, outlined, text)** | Users expect visual hierarchy between primary actions (Next, Download) and secondary (Back, Copy). Current buttons use ad-hoc blue/gray with no consistent system. | LOW | Three tiers: filled (primary actions), outlined (secondary/back), text (tertiary). Use MD3 shape tokens (rounded-xl for buttons). Map to `--md-sys-color-primary` and `--md-sys-color-on-primary`. |
|
||||
| **MD3 card components with elevation** | Backend selection cards and output blocks need consistent elevation, padding, and shape. Current cards have inconsistent border-only styling. | LOW | MD3 elevation uses tonal surface tint (not just shadows). Levels 0-5. Cards typically at level 1 (subtle tint + minimal shadow). Selected state bumps to level 2. |
|
||||
| **Dark mode** | 87% of users prefer apps that support automatic theme switching. A dev/IT tool without dark mode feels incomplete. | MEDIUM | Three-state toggle: System / Light / Dark. Respect `prefers-color-scheme` as default. Persist choice in `localStorage`. Implement via `data-theme` attribute on `<html>` + CSS custom properties. Tailwind v4 `dark:` variant works with `@media (prefers-color-scheme: dark)` or class strategy. |
|
||||
| **Consistent color token system** | Hardcoded `text-gray-700`, `bg-blue-600`, `border-red-500` scattered across components creates maintenance burden and makes theming impossible. | MEDIUM | Define MD3 color roles as CSS custom properties: `--color-primary`, `--color-on-primary`, `--color-surface`, `--color-on-surface`, `--color-error`, etc. All components reference tokens, not raw Tailwind colors. Enables dark mode and accent colors with a single layer of indirection. |
|
||||
| **Proper step indicator / progress bar** | Current StepIndicator uses inline styles, plain text with ">" separators, and checkbox emoji. Looks unprofessional. | MEDIUM | MD3 stepper pattern: numbered circles connected by lines. Completed steps show checkmark icon in filled circle. Active step is highlighted with primary color. Future steps are muted. Must show step labels and be responsive (collapse labels on mobile, show numbers only). |
|
||||
| **Responsive layout (mobile-friendly forms)** | IT admins use phones/tablets for quick reference or field work. Current `max-w-2xl` centered layout does not adapt form fields for small screens. | MEDIUM | Backend cards: grid `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3`. Form fields: full-width on mobile, optionally 2-column on wide screens for short fields. Step indicator: horizontal on desktop, compact (numbers-only or dots) on mobile. Buttons: full-width on mobile. |
|
||||
| **Form field error states (MD3 style)** | Current red border + small text is functional but not MD3-compliant. MD3 errors use the error color role, an error icon, and supporting text below the field. | LOW | Error state: border changes to `--color-error`, label text changes to error color, trailing error icon appears, supporting text shows error message in error color. Already have error messages; need to style them consistently. |
|
||||
| **Step-level descriptions** | Each wizard step needs a brief explanation of what the user is doing and why. Current steps jump straight to form fields with only "Step N: Title". | LOW | 1-2 sentence description below each step heading. Examples: "Choose your cloud storage provider. This determines which credentials you will need." Static text, no logic. |
|
||||
| **Accessible focus states** | EAA compliance (June 2025) requires visible focus indicators for keyboard navigation. Current `:focus` uses `ring-2` which is minimal. | LOW | MD3 focus: 3px outline using `--color-primary` with 2px offset. All interactive elements (buttons, inputs, cards, links) must have visible focus. Tailwind `focus-visible:` variant preferred over `focus:` to avoid showing focus on mouse click. |
|
||||
|
||||
## Differentiators
|
||||
### Differentiators (Competitive Advantage)
|
||||
|
||||
Features that set Ready2Blob apart from "just read the rclone docs" or copy-pasting PS scripts from Reddit.
|
||||
Features that elevate Ready2Blob from "functional tool" to "tool that inspires confidence."
|
||||
|
||||
| Feature | Value Proposition | Complexity | Notes |
|
||||
|---------|-------------------|------------|-------|
|
||||
| Intune-specific detection script generation | Intune Win32 apps require a separate detection script (exit 0 = installed); most admins copy-paste wrong ones | Medium | Detect by checking rclone.exe presence at install path AND config file presence. Both must exist |
|
||||
| Intune packaging hints / IntuneWinAppUtil guidance | After generating scripts, show admin the exact IntuneWinAppUtil command to wrap the installer | Low | Static text block, not dynamic generation — but reduces a common stumbling point |
|
||||
| RMM-specific script variants | NinjaRMM, Datto, and ConnectWise have slightly different execution contexts (SYSTEM vs user, working dir) | High | Start with a generic "SYSTEM context" PS script that works across RMMs; add named variants later |
|
||||
| Rclone version pinning | MSP environments require reproducible deployments; "latest" is not acceptable for production | Low | Text input: "Pin to rclone version" (e.g., `v1.68.2`). Defaults to latest stable. Affects download URL in script |
|
||||
| Config placement path options | Config can go to `%APPDATA%\rclone\rclone.conf` (user) or a machine-wide path. Intune SYSTEM context needs machine-wide | Medium | Dropdown: User profile path vs machine-wide path (`C:\ProgramData\rclone\`). Explain implications of each |
|
||||
| Multiple remotes in one config | A single rclone.conf can contain multiple named remotes; some orgs need 2-3 backends on same endpoint | High | Allow "Add another remote" in wizard. Generates a single .conf with multiple sections |
|
||||
| Live config preview | Admin sees the exact text of generated files before downloading — builds trust, catches errors | Low | Syntax-highlighted read-only textarea. Updates in real time as form fields change |
|
||||
| Copy-to-clipboard for each output | Some RMM tools have a "run script" field — paste directly without downloading a file | Low | Copy button beside each output block |
|
||||
| Field-level validation with rclone-specific rules | Azure Blob storage account names are 3-24 lowercase alphanumeric chars — catch this before the admin deploys a broken config | Medium | Per-field regex/rule validation. Reduces "why doesn't rclone connect?" support tickets |
|
||||
| Explanatory tooltips on sensitive fields | "What is an SAS token vs an Access Key?" — admins often don't know which credential type to use | Low | Tooltip or inline help text per field. Reduces abandonment from confusion |
|
||||
| Backend popularity ordering | Show Azure Blob, S3, OneDrive, SFTP, GCS at top — don't bury them alphabetically | Low | Simple UX decision with high impact on time-to-task-complete |
|
||||
| **User-selectable accent color** | Personalizes the tool. IT admins often match internal tools to company brand colors. Also demonstrates the token system works. | MEDIUM | Offer 5-8 preset accent colors (blue default, teal, purple, green, orange, pink). User picks one, CSS custom properties update for `--color-primary` and its derivatives. Persist in `localStorage`. Use MD3 tonal palette generation: from one source color, derive on-primary, primary-container, on-primary-container. |
|
||||
| **App intro / landing section** | First-time visitors need to understand what Ready2Blob does before diving into the wizard. Current app jumps straight to Step 1 with no context. | LOW | Hero section above the wizard: app name, one-sentence value prop ("Generate rclone configs and deployment scripts for Windows endpoints"), 3-4 feature bullets (client-side only, 7 backends, Intune + RMM scripts), and a "Get Started" button that scrolls to or reveals the wizard. Collapses/hides once wizard is started. |
|
||||
| **Animated step transitions** | Smooth transitions between wizard steps make the app feel responsive and polished rather than jarring page swaps. | LOW | CSS-only fade or slide transition on step change. Use `opacity` + `transform: translateX()` with 200ms ease. Keep it subtle -- this is a productivity tool, not a marketing site. Respect `prefers-reduced-motion`. |
|
||||
| **Contextual help popovers (upgraded)** | Current tooltip toggle (click to show/hide inline text) works but is basic. MD3-style popovers with arrow indicators and rich content feel more professional. | MEDIUM | Replace inline toggle with a proper popover component: positioned above/below the trigger, with arrow, dismissed on outside click or Escape. Still click-triggered (not hover -- touch devices need click). Consider using Floating UI (lightweight, ~3KB) for positioning math. |
|
||||
| **Remote name field clarity** | The "Remote name" field confuses users who do not know rclone conventions. Needs prominent explanation, examples, and format hint. | LOW | Add: placeholder "e.g., corp-backup", help text explaining "This name identifies the storage target in rclone commands and scripts", and an inline example showing how it appears in the generated config `[corp-backup]`. Already partially addressed in v1.1 tooltips; needs more prominent treatment. |
|
||||
| **MD3 select/dropdown styling** | Native `<select>` elements look different on every OS and cannot be themed. Breaks the visual consistency of MD3 forms. | HIGH | Custom dropdown component matching MD3 outlined field style. Must handle keyboard navigation, ARIA `listbox` role, focus management. Significantly more complex than native select. Consider whether the 2-3 selects in the app justify the effort. Alternative: style the surrounding label/container as MD3 and accept native select rendering. |
|
||||
| **Code block syntax highlighting (review step)** | Output blocks (rclone.conf, PowerShell scripts) currently render as plain `<pre>` text. Syntax highlighting adds professionalism and readability. | MEDIUM | Use a lightweight highlighter like Prism.js or highlight.js (INI + PowerShell grammars only -- tree-shake aggressively). Alternatively, minimal hand-rolled highlighting for INI format (`[section]` headers in primary color, `key = value` with key bold). Dark mode must invert colors. |
|
||||
| **Smooth scroll-to-error on validation failure** | When a user clicks Next with invalid fields, scrolling to the first error field reduces confusion on longer forms (RemoteConfigStep with many fields). | LOW | On form submit failure, call `element.scrollIntoView({ behavior: 'smooth', block: 'center' })` on the first errored field. Minor polish, large UX impact on mobile where fields extend below fold. |
|
||||
|
||||
## Anti-Features
|
||||
### Anti-Features (Commonly Requested, Often Problematic)
|
||||
|
||||
Features to explicitly NOT build in v1 — scope creep killers.
|
||||
Features that seem good for UI polish but create problems.
|
||||
|
||||
| Anti-Feature | Why Avoid | What to Do Instead |
|
||||
|--------------|-----------|-------------------|
|
||||
| Test connection / validate credentials | Requires a backend proxy (CORS blocks direct cloud API calls from browser); breaks the "no server" constraint entirely | Show a callout: "Run `rclone lsd remote-name:` on any Windows PC after deploying to verify connectivity" |
|
||||
| Save / load configurations | Requires either a backend (no-server constraint violated) or localStorage (credentials in browser storage = security incident) | Tell user to save the downloaded .conf file. That IS their save format |
|
||||
| User accounts / authentication | No backend = no accounts. Would require a complete architecture rethink | Out of scope permanently for v1. Re-evaluate only if architecture changes |
|
||||
| rclone mount / sync scheduling | UI for configuring rclone mount or scheduled sync jobs adds a second problem domain (task scheduler, Windows service) on top of the first | Separate product decision. Ready2Blob focuses solely on getting rclone configured and deployed |
|
||||
| Auto-push to Intune via Graph API | Would require Azure AD app registration, OAuth flow, Graph API integration — massive scope increase | Generate files the admin uploads manually. Graph API is a v2+ consideration |
|
||||
| Multi-OS support (macOS, Linux) | Scripts are PowerShell for Windows. macOS/Linux have different path conventions, shell scripts, MDM tools | Out of scope for v1. State clearly in UI: "Windows endpoints only" |
|
||||
| rclone version auto-update logic | Keeping rclone up to date on endpoints is a separate lifecycle management problem | Point admin to rclone's own update mechanism or their RMM's patch management |
|
||||
| Visual diff of old vs new config | Requires knowing what's already deployed — impossible without a backend | Not viable without persistence layer |
|
||||
| Encryption of config credentials | rclone supports `rclone config` password-encrypted configs but requires interactive unlock on each use, incompatible with unattended deployment | Document the limitation; recommend Azure Key Vault or Intune-native secrets for sensitive deployments |
|
||||
|
||||
---
|
||||
| Feature | Why Requested | Why Problematic | Alternative |
|
||||
|---------|---------------|-----------------|-------------|
|
||||
| **Full MUI or Material Tailwind component library** | "Just use MUI for MD3" -- seems like it would save time | Massive bundle size increase (MUI is ~300KB+ gzipped). Overrides existing Tailwind approach. Migration tax is high. Most MUI components are unnecessary for a 4-step wizard with ~15 form fields. Creates a dependency on MUI's theming system instead of owning your tokens. | Build a thin MD3 design token layer in CSS custom properties + Tailwind utilities. Create 4-5 custom components (TextField, Button, Card, StepIndicator, Toggle). Total code is smaller than MUI's tree-shaken output for the same components. |
|
||||
| **Animated wizard with page-per-step routing** | "Each step should be its own route for bookmarkability" | Adds React Router dependency. Wizard state is ephemeral (credentials in memory) -- refreshing a step would lose context. Back/forward browser buttons would conflict with wizard Back/Next. Bookmarking a credential-entry step is a security anti-pattern. | Keep single-page wizard with in-memory state. Step indicator serves as navigation. No routing needed. |
|
||||
| **Glassmorphism / neomorphism / heavy visual effects** | "Modern UI trends" | These effects are CPU-intensive (backdrop-blur), have accessibility issues (low contrast), and will look dated within a year. IT admins want clarity, not visual flair. | MD3 tonal elevation (surface tint) provides depth without heavy effects. Clean, readable, professional. |
|
||||
| **Custom-styled native checkboxes and radios** | "The DeploymentStep checkboxes look plain" | Custom checkbox/radio styling requires hiding the native element and rebuilding focus, checked, indeterminate, and disabled states. High effort for a few toggles. Touch target issues. | Use MD3-inspired wrapper: larger touch target (48px), visible label, proper spacing. Keep native input for accessibility. Style the surrounding container instead. |
|
||||
| **Toast notifications for copy-to-clipboard** | "Show a toast when text is copied" | Requires a toast/snackbar system with portal, z-index management, animation, and auto-dismiss timer -- infrastructure overhead for a single use case. | Inline feedback: change the copy button text/icon to "Copied!" for 2 seconds, then revert. Zero infrastructure needed. Already understood by users. |
|
||||
| **Theme with arbitrary user-picked hex color** | "Let users enter any hex color" | Arbitrary colors break accessibility (contrast ratios). A random bright yellow as primary makes error states invisible, text unreadable. Generating a full tonal palette from an arbitrary source requires the `@material/material-color-utilities` library (~15KB). | Offer 5-8 curated presets that have been verified for contrast compliance in both light and dark modes. Covers 95% of personalization desire with zero accessibility risk. |
|
||||
|
||||
## Feature Dependencies
|
||||
|
||||
```
|
||||
Backend selection
|
||||
→ Per-backend form fields (fields depend on selected backend type)
|
||||
→ Remote name input
|
||||
→ rclone.conf generation (depends on: backend type, all field values, remote name)
|
||||
→ Intune script generation (depends on: config content, install option, config path choice)
|
||||
→ RMM script generation (depends on: config content, install option, config path choice)
|
||||
→ Version pin input (affects download URL inside both scripts)
|
||||
→ Config path option (affects file placement command inside both scripts)
|
||||
Color Token System (CSS custom properties)
|
||||
|-- required by --> Dark Mode (tokens swap values per theme)
|
||||
|-- required by --> Accent Colors (tokens swap primary hue)
|
||||
|-- required by --> MD3 Text Fields (reference token colors)
|
||||
|-- required by --> MD3 Buttons (reference token colors)
|
||||
|-- required by --> MD3 Cards (reference token colors + elevation tint)
|
||||
|-- required by --> Step Indicator (reference token colors)
|
||||
|-- required by --> Error States (reference error token)
|
||||
|
||||
Live config preview → rclone.conf generation (real-time rendering of same output)
|
||||
Download buttons → all generation outputs (nothing to download until form is valid)
|
||||
Security warning → download buttons (warning must be acknowledged before download is enabled)
|
||||
Dark Mode
|
||||
|-- requires --> Color Token System
|
||||
|-- requires --> localStorage persistence (theme choice)
|
||||
|-- enhances --> Accent Colors (must work in both light and dark)
|
||||
|
||||
Accent Colors
|
||||
|-- requires --> Color Token System
|
||||
|-- requires --> Curated palette presets (per-accent token sets)
|
||||
|
||||
Step Indicator (MD3)
|
||||
|-- requires --> Color Token System
|
||||
|-- independent of --> other component styling
|
||||
|
||||
App Intro Section
|
||||
|-- independent of --> all styling work (content-only)
|
||||
|-- enhances --> first-time experience
|
||||
|
||||
Step Descriptions
|
||||
|-- independent of --> all styling work (content-only)
|
||||
|
||||
Remote Name Clarity
|
||||
|-- independent of --> MD3 styling (content improvement)
|
||||
|
||||
Responsive Layout
|
||||
|-- independent of --> color tokens (Tailwind breakpoints)
|
||||
|-- should follow --> MD3 component styling (size tokens align)
|
||||
|
||||
Animated Transitions
|
||||
|-- requires --> Step rendering structure (already exists)
|
||||
|-- should follow --> MD3 component styling (transition consistent)
|
||||
```
|
||||
|
||||
---
|
||||
### Dependency Notes
|
||||
|
||||
## MVP Recommendation
|
||||
- **Color Token System is the foundation**: Every visual component depends on it. Must be implemented first. Without tokens, each component hardcodes colors and dark mode becomes a per-component rewrite.
|
||||
- **Dark Mode requires tokens but tokens do not require dark mode**: Tokens can ship first with light-only, then dark mode adds a second set of token values.
|
||||
- **Accent Colors layer on top of both**: Accent colors multiply the token sets (N accents x 2 themes = 2N token sets). Implement after dark mode is stable.
|
||||
- **Content improvements (intro, descriptions, remote name) are independent**: Can be done in any order, in parallel with styling work.
|
||||
- **Step Indicator is visually complex but logically independent**: Can be rebuilt without affecting other components.
|
||||
|
||||
Prioritize in this order:
|
||||
## MVP Definition
|
||||
|
||||
1. Backend selection + per-backend forms (Azure Blob, S3, OneDrive, SFTP as initial set — covers 80% of use cases)
|
||||
2. rclone.conf generation with live preview
|
||||
3. Intune PowerShell script generation (primary target audience pain point)
|
||||
4. RMM PowerShell script generation
|
||||
5. Security warning gate before download
|
||||
6. Optional rclone install toggle
|
||||
7. Config path selector (user vs machine-wide)
|
||||
### Launch With (v1.2 Core)
|
||||
|
||||
Defer to post-MVP:
|
||||
Minimum set to achieve "polished, self-explanatory experience" goal.
|
||||
|
||||
- Multiple remotes in one config: adds wizard UX complexity; single remote covers the majority of deployments
|
||||
- RMM-named variants (NinjaRMM-specific, Datto-specific): start with generic SYSTEM-context PS script
|
||||
- Intune IntuneWinAppUtil packaging hints: valuable but can be a static docs page
|
||||
- Version pinning: default to latest stable with a text field; low-effort add
|
||||
- Field-level validation beyond basic required-field checks: adds significant per-backend maintenance burden
|
||||
- [ ] **Color token system** -- CSS custom properties for all MD3 color roles, referenced by all components. This is the enabler for everything else.
|
||||
- [ ] **MD3 text fields (outlined)** -- Floating labels, proper focus/error states. Applied to FieldRenderer and BackendSelectionStep remote name input.
|
||||
- [ ] **MD3 button hierarchy** -- Filled primary, outlined secondary, text tertiary. Applied to all wizard navigation and download buttons.
|
||||
- [ ] **MD3 card components** -- Elevation via surface tint. Applied to BackendCard and OutputBlock.
|
||||
- [ ] **MD3 step indicator** -- Numbered circles with connecting lines, completed/active/future states.
|
||||
- [ ] **Dark mode toggle** -- System/Light/Dark with localStorage persistence.
|
||||
- [ ] **App intro section** -- Hero explaining what Ready2Blob is, with Get Started CTA.
|
||||
- [ ] **Step-level descriptions** -- Brief explanation text on each wizard step.
|
||||
- [ ] **Remote name field clarity** -- Prominent explanation, examples, format hint.
|
||||
- [ ] **Responsive layout** -- Mobile-friendly grid, collapsible step indicator, full-width buttons on small screens.
|
||||
- [ ] **Accessible focus states** -- Visible focus-visible outlines on all interactive elements.
|
||||
- [ ] **FieldRenderer aria consistency fix** -- Existing tech debt from v1.1.
|
||||
- [ ] **StepIndicator inline style migration** -- Replace inline styles with Tailwind classes (existing tech debt).
|
||||
|
||||
---
|
||||
### Add After Validation (v1.2.x)
|
||||
|
||||
## Backend Coverage Priority
|
||||
Features to add once the core polish is working and tested.
|
||||
|
||||
Based on enterprise Windows deployment prevalence (HIGH confidence from domain knowledge):
|
||||
- [ ] **Accent color selector** -- 5-8 preset colors, persistent. Add after token system and dark mode are stable.
|
||||
- [ ] **Animated step transitions** -- CSS fade/slide on step change. Add after step rendering is finalized.
|
||||
- [ ] **Contextual help popovers (upgraded)** -- Replace inline tooltip toggles with positioned popovers. Add after MD3 component styling is settled.
|
||||
- [ ] **Scroll-to-error on validation failure** -- Minor polish, add when form field styling is complete.
|
||||
|
||||
| Tier | Backends | Rationale |
|
||||
|------|----------|-----------|
|
||||
| Tier 1 — Must ship in v1 | Azure Blob Storage, Amazon S3, Microsoft OneDrive | Dominant in enterprise; covers ~70% of use cases. "Ready2Blob" brand implies Azure first |
|
||||
| Tier 2 — Ship in v1 if feasible | SFTP, Google Cloud Storage, Backblaze B2 | Common in MSP environments and SMB |
|
||||
| Tier 3 — Post-v1 | Google Drive, Dropbox, SharePoint, S3-compatible (Wasabi, MinIO, etc.) | Consumer-origin or niche; lower enterprise priority |
|
||||
| Tier 4 — Document only | All remaining rclone backends (50+) | Too many to form-ify in v1; link to rclone docs |
|
||||
### Future Consideration (v2+)
|
||||
|
||||
**Note on S3-compatible backends:** Amazon S3 forms should include an "endpoint override" field so the same form handles Wasabi, MinIO, Cloudflare R2, etc. This is how rclone handles them natively — `provider` + optional `endpoint`. One form, many backends. (MEDIUM confidence — verify against rclone S3 docs)
|
||||
Features to defer until after this milestone.
|
||||
|
||||
---
|
||||
- [ ] **Custom MD3 select/dropdown** -- High complexity for 2-3 selects in the app. Native select with MD3-styled container is sufficient for v1.2.
|
||||
- [ ] **Code syntax highlighting** -- Nice but not essential. Plain monospace output blocks are standard for config/script tools.
|
||||
- [ ] **Arbitrary user hex color theming** -- Requires color utility library and contrast validation. Curated presets suffice.
|
||||
|
||||
## IT Admin Expectations (Contextual)
|
||||
## Feature Prioritization Matrix
|
||||
|
||||
These are workflow expectations rather than discrete features, but they inform every feature decision:
|
||||
| Feature | User Value | Implementation Cost | Priority |
|
||||
|---------|------------|---------------------|----------|
|
||||
| Color token system | HIGH | MEDIUM | P1 |
|
||||
| MD3 text fields | HIGH | MEDIUM | P1 |
|
||||
| MD3 buttons | HIGH | LOW | P1 |
|
||||
| MD3 cards | MEDIUM | LOW | P1 |
|
||||
| Dark mode | HIGH | MEDIUM | P1 |
|
||||
| Step indicator (MD3) | HIGH | MEDIUM | P1 |
|
||||
| App intro section | HIGH | LOW | P1 |
|
||||
| Step descriptions | MEDIUM | LOW | P1 |
|
||||
| Remote name clarity | MEDIUM | LOW | P1 |
|
||||
| Responsive layout | HIGH | MEDIUM | P1 |
|
||||
| Focus states (a11y) | MEDIUM | LOW | P1 |
|
||||
| FieldRenderer aria fix | LOW | LOW | P1 (tech debt) |
|
||||
| StepIndicator style migration | LOW | LOW | P1 (tech debt) |
|
||||
| Accent color selector | MEDIUM | MEDIUM | P2 |
|
||||
| Step transitions | LOW | LOW | P2 |
|
||||
| Upgraded popovers | LOW | MEDIUM | P2 |
|
||||
| Scroll-to-error | LOW | LOW | P2 |
|
||||
| Custom select/dropdown | LOW | HIGH | P3 |
|
||||
| Syntax highlighting | LOW | MEDIUM | P3 |
|
||||
|
||||
- **Scripts must run as SYSTEM** — Intune and most RMMs execute scripts as SYSTEM, not as the logged-in user. Config path must be machine-wide, not `%APPDATA%`. This is the single most common deployment failure mode.
|
||||
- **Scripts must be idempotent** — Running the install script twice must not break anything. Check-then-act pattern: if rclone.exe already exists and config already exists, exit 0.
|
||||
- **Scripts must have exit codes** — Intune uses exit codes to determine success/failure of a deployment. Script must exit 0 on success, non-zero on failure.
|
||||
- **Detection scripts must be separate from install scripts** — Intune Win32 app model requires them to be distinct. Many generated scripts online conflate them.
|
||||
- **No interactive prompts** — Scripts run silently. Any `Read-Host`, `Write-Host` expecting input, or UAC prompt breaks unattended deployment.
|
||||
- **64-bit PowerShell** — Intune on 64-bit Windows sometimes executes PS in 32-bit mode. rclone.exe path may differ. Scripts should force 64-bit context or be path-aware.
|
||||
**Priority key:**
|
||||
- P1: Must have for v1.2 launch -- achieves the "polished" goal
|
||||
- P2: Should have, add when core P1 features are stable
|
||||
- P3: Nice to have, defer unless time permits
|
||||
|
||||
(Confidence: HIGH for SYSTEM context and exit codes — verified by common Intune troubleshooting canon. MEDIUM for 32/64-bit PS caveat — common but less universally documented.)
|
||||
## What Makes a Config Wizard Feel Professional vs Bare
|
||||
|
||||
---
|
||||
Based on analysis of the current Ready2Blob UI against MD3 patterns and wizard UX best practices:
|
||||
|
||||
**Current state (bare):**
|
||||
- Raw HTML inputs with no visual framework
|
||||
- Inline styles on StepIndicator
|
||||
- Inconsistent color usage (hardcoded Tailwind values)
|
||||
- No dark mode
|
||||
- No intro explaining the tool
|
||||
- Step headings with no descriptions
|
||||
- No visual hierarchy in buttons
|
||||
- Backend cards have basic border styling only
|
||||
- Output blocks are unstyled pre/textarea elements
|
||||
|
||||
**Professional target:**
|
||||
1. **Visual consistency** -- Every element follows the same design language (spacing, color, shape, elevation)
|
||||
2. **Clear information hierarchy** -- Primary actions are obvious, secondary actions are subdued, labels guide the eye
|
||||
3. **Contextual guidance** -- The UI explains itself: what each step does, what each field means, what happens next
|
||||
4. **Responsive confidence** -- Works smoothly on any screen size without horizontal scrolling or cramped layouts
|
||||
5. **Polish details** -- Smooth transitions, proper loading states, visible focus, no flicker on theme change
|
||||
6. **Trust signals** -- Professional appearance, security notices styled prominently, client-side-only badge
|
||||
|
||||
The gap between bare and professional is not one feature -- it is the cumulative effect of the token system enabling consistent styling across every component, combined with content improvements (intro, descriptions, help text) that make the wizard self-explanatory.
|
||||
|
||||
## Sources
|
||||
|
||||
- rclone official documentation (rclone.org) — not fetched in this session due to tool restrictions; referenced from training data (knowledge cutoff August 2025)
|
||||
- Microsoft Intune Win32 app deployment model — training data (HIGH confidence on SYSTEM context, exit codes, detection script requirements)
|
||||
- RMM deployment patterns (NinjaRMM, Datto, ConnectWise) — training data (MEDIUM confidence)
|
||||
- rclone.conf INI format specification — training data (HIGH confidence; format is stable and well-documented)
|
||||
- [Material Design 3 Text Fields Guidelines](https://m3.material.io/components/text-fields/guidelines) -- MD3 text field patterns (filled vs outlined, supporting text, error states)
|
||||
- [Material Design 3 Components](https://m3.material.io/components) -- Full component catalog
|
||||
- [Material Design 3 Elevation](https://m3.material.io/styles/elevation/applying-elevation) -- Surface tint vs shadow, elevation levels 0-5
|
||||
- [Material Design 3 Color Roles](https://m3.material.io/styles/color/roles) -- Primary, secondary, tertiary, surface, error color roles
|
||||
- [Material Design 3 Dynamic Color](https://m3.material.io/styles/color/dynamic/user-generated-source) -- User-generated source color for theming
|
||||
- [MD3 box-shadow CSS values](https://studioncreations.com/blog/material-design-3-box-shadow-css-values/) -- CSS elevation implementation (MEDIUM confidence)
|
||||
- [Beyond the Progress Bar: Stepper UI Design](https://medium.com/@david.pham_1649/beyond-the-progress-bar-the-art-of-stepper-ui-design-cfa270a8e862) -- Stepper patterns and best practices
|
||||
- [Wizard Design Pattern (UX Planet)](https://uxplanet.org/wizard-design-pattern-8c86e14f2a38) -- Wizard UX fundamentals
|
||||
- [Wizards: Definition and Design Recommendations (NN/g)](https://www.nngroup.com/articles/wizards/) -- Nielsen Norman Group wizard guidelines
|
||||
- [Dark Mode Toggle and prefers-color-scheme](https://dev.to/abbeyperini/dark-mode-toggle-and-prefers-color-scheme-4f3m) -- Implementation pattern for system/manual toggle
|
||||
- [The Ultimate Guide to Coding Dark Mode 2025](https://devieffe.substack.com/p/the-ultimate-guide-to-coding-dark-mode-layouts-in-2025) -- CSS custom properties + data-theme approach
|
||||
- [Design Tokens and Theming: Scalable UI Systems 2025](https://materialui.co/blog/design-tokens-and-theming-scalable-ui-2025) -- Token architecture patterns
|
||||
- [Tailwind CSS Responsive Design](https://tailwindcss.com/docs/responsive-design) -- Mobile-first breakpoint system
|
||||
- [MD3 Theming Tokens (seenode)](https://seenode.com/blog/what-is-material-3-and-why-it-matters-in-2025) -- 141 system tokens, token hierarchy
|
||||
- [Input Field Design Best Practices 2025](https://fireart.studio/blog/input-field-design-best-practice/) -- Floating labels, error patterns, accessibility
|
||||
|
||||
**Validation recommended before roadmap finalization:**
|
||||
- Confirm current rclone backend list and required fields per backend at rclone.org/overview
|
||||
- Confirm Intune Win32 app detection script requirements in current Microsoft docs
|
||||
- Check if rclone has changed S3-compatible `provider`+`endpoint` pattern in recent releases
|
||||
---
|
||||
*Feature research for: Ready2Blob v1.2 UI Polish*
|
||||
*Researched: 2026-03-31*
|
||||
|
||||
+210
-266
@@ -1,362 +1,306 @@
|
||||
# Domain Pitfalls
|
||||
# Pitfalls Research
|
||||
|
||||
**Domain:** rclone config generator / PowerShell deployment wizard (Windows/Intune/RMM)
|
||||
**Researched:** 2026-03-26
|
||||
**Confidence:** HIGH (Intune/PowerShell — verified against official Microsoft docs), MEDIUM (rclone-specific — based on format spec knowledge plus training data; rclone docs were inaccessible during research)
|
||||
**Domain:** UI polish overhaul -- Material Design 3, dark mode, accent colors added to existing Tailwind v4 + React wizard app
|
||||
**Researched:** 2026-03-31
|
||||
**Confidence:** HIGH (based on codebase analysis + verified Tailwind v4 docs + community patterns)
|
||||
|
||||
---
|
||||
|
||||
## Critical Pitfalls
|
||||
|
||||
Mistakes that cause the generated script/config to silently fail or require a full rewrite.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 1: rclone config deployed to wrong path under SYSTEM context
|
||||
### Pitfall 1: Tailwind v4 Dark Mode Requires CSS-First Config, Not tailwind.config.js
|
||||
|
||||
**What goes wrong:**
|
||||
When Intune deploys a PowerShell script with "Run as SYSTEM" (the `No` logged-on-credentials option), the script runs as `NT AUTHORITY\SYSTEM`. The default rclone config location resolves from the SYSTEM user's `%APPDATA%`, which is `C:\Windows\system32\config\systemprofile\AppData\Roaming\rclone\rclone.conf`. This path is not readable by the end user who will later run rclone interactively. The config is deposited silently with no error, but rclone launched by the user finds no config.
|
||||
Developers reach for `tailwind.config.js` with `darkMode: 'class'` which does not exist in Tailwind v4. The app currently has only `@import "tailwindcss";` in `index.css` with no config file at all. Using v3 dark mode patterns produces zero effect and wastes debugging time.
|
||||
|
||||
**Why it happens:**
|
||||
rclone resolves config location from environment variables at runtime. Under SYSTEM, `%APPDATA%` and `%USERPROFILE%` expand to the SYSTEM profile paths, not any individual user's profile. Developers test locally as themselves and never hit this path.
|
||||
Most tutorials and Stack Overflow answers still reference Tailwind v3 syntax. Tailwind v4 moved to a fully CSS-first configuration model. The `darkMode` config key is gone.
|
||||
|
||||
**Consequences:**
|
||||
- rclone runs with no configuration; all sync commands fail with "no remote" error
|
||||
- Hard to debug because the config file exists on disk — just in the wrong place
|
||||
- If the wizard generates a hardcoded `%APPDATA%` path string in the script, that string is evaluated at deployment time (SYSTEM), not at user runtime
|
||||
**How to avoid:**
|
||||
Add the `@custom-variant` directive in `index.css` for class-based toggling:
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
```
|
||||
This enables manual toggle via a `.dark` class on `<html>`. The `:where()` wrapper keeps specificity at zero, preventing cascade conflicts. Verified against [Tailwind v4 dark mode docs](https://tailwindcss.com/docs/dark-mode).
|
||||
|
||||
**Prevention:**
|
||||
- The generated script must write the config to a machine-wide path such as `C:\ProgramData\rclone\rclone.conf` and then invoke rclone with `--config "C:\ProgramData\rclone\rclone.conf"` (or set `RCLONE_CONFIG` env var).
|
||||
- Alternatively: write to each user's profile by running in user context — but SYSTEM context is common for silently deploying software.
|
||||
- The wizard should make the config destination path explicit and let the IT admin choose: machine-wide vs. user-profile. Never default to a bare `%APPDATA%` expansion in a SYSTEM-context script.
|
||||
**Warning signs:**
|
||||
- `dark:` prefixed classes have no visible effect
|
||||
- Dark mode only responds to OS preference, not the toggle button
|
||||
|
||||
**Detection:**
|
||||
- Config exists at SYSTEM profile path but rclone launched by user says "no remote configured"
|
||||
- Check `rclone config file` — it will show the wrong path
|
||||
|
||||
**Phase relevance:** Phase generating the PowerShell script (any phase touching script output)
|
||||
**Phase to address:**
|
||||
Phase 1 (Theme Foundation) -- this must be the very first CSS change before any `dark:` classes are added to components.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 2: Intune PowerShell scripts are size-limited to 200 KB (ASCII)
|
||||
### Pitfall 2: Hardcoded Color Values Across 73 className Usages
|
||||
|
||||
**What goes wrong:**
|
||||
Microsoft Intune enforces a hard 200 KB (ASCII) size limit on uploaded PowerShell scripts. Scripts that embed a large rclone installer binary (base64-encoded), or that inline multiple large config payloads, will be rejected at upload time.
|
||||
The codebase has 73 `className=` usages with hardcoded Tailwind color classes (`text-gray-700`, `border-gray-300`, `focus:ring-blue-300`, `text-red-500`, `bg-gray-50`, etc.). Adding dark mode by appending a `dark:` counterpart to every single one creates unreadable className strings and guarantees missed spots -- invisible text, invisible borders, or unreadable error messages against dark backgrounds.
|
||||
|
||||
**Why it happens:**
|
||||
IT developers prototype a "self-contained" script that downloads rclone, unpacks it, writes the config, and sets up a scheduled task — all in one file. Base64-encoding a ~50 MB rclone binary produces a ~67 MB string. Even base64-encoding a 400 KB installer produces a 550 KB string, well over the limit.
|
||||
When building light-mode-only, hardcoded color classes are natural. The cost is deferred until dark mode arrives. Developers add `dark:` to the visible components and miss the less-obvious ones (help text, error messages, placeholders, disabled states).
|
||||
|
||||
**Consequences:**
|
||||
- Script upload fails; IT admin gets a non-obvious error in Intune
|
||||
- Workaround requires restructuring the entire script delivery approach
|
||||
**How to avoid:**
|
||||
Define semantic CSS custom properties (design tokens) mapped to Tailwind's `@theme` directive:
|
||||
```css
|
||||
@theme {
|
||||
--color-surface: #ffffff;
|
||||
--color-on-surface: #1a1a1a;
|
||||
--color-primary: #2563eb;
|
||||
--color-error: #dc2626;
|
||||
}
|
||||
```
|
||||
Then use `bg-surface`, `text-on-surface` throughout components. Dark mode changes the token values once (on `.dark`), not every component. This is the Material Design 3 approach (surface, on-surface, primary, on-primary, etc.).
|
||||
|
||||
**Prevention:**
|
||||
- The wizard must never embed rclone binary content into the generated script
|
||||
- The rclone installation step must use a network download (e.g., `Invoke-WebRequest` from the rclone GitHub releases API or a corporate file share URL) or reference a Win32 app deployment separately
|
||||
- Clearly surface this constraint in the wizard: "rclone binary will be downloaded from [URL] at deployment time" — and let the admin specify an internal mirror if internet access is restricted on endpoints
|
||||
**Warning signs:**
|
||||
- `dark:` classes appearing in JSX alongside light classes, creating 100+ character className strings
|
||||
- Text disappearing on dark backgrounds during manual testing
|
||||
- Error messages (`text-red-600`) becoming unreadable against dark backgrounds
|
||||
|
||||
**Detection:**
|
||||
- Intune admin center shows upload error "Script size exceeds limit"
|
||||
- Script file is visibly large before upload
|
||||
|
||||
**Phase relevance:** Phase implementing the rclone-install option in script generation
|
||||
**Phase to address:**
|
||||
Phase 1 (Theme Foundation) defines tokens. Phase 2 (Component Overhaul) replaces hardcoded colors with token references.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 3: PowerShell script encoding mismatch causes silent config corruption
|
||||
### Pitfall 3: Dark Mode Color Contrast Failures (WCAG AA)
|
||||
|
||||
**What goes wrong:**
|
||||
The generated PowerShell script writes the rclone config file to disk using `Set-Content` or `Out-File`. The default encoding in Windows PowerShell 5.1 is UTF-16 LE with BOM for `Out-File`, and varies for `Set-Content` (system codepage/ANSI on PS 5.1, UTF-8 no-BOM on PS 7+). rclone expects its config file in UTF-8. A config with a UTF-16 BOM or ANSI-encoded special characters (common in storage keys) will be misread, causing authentication failures.
|
||||
Text that passes 4.5:1 contrast in light mode fails in dark mode. The most common failures: gray help text on dark gray backgrounds, red error text on dark surfaces, blue links on dark blue-gray backgrounds. The current app uses `text-gray-500` for help text and `text-red-600` for errors -- both will fail against typical dark backgrounds.
|
||||
|
||||
**Why it happens:**
|
||||
Developers write `Out-File $configPath` and it works in their test because all values are ASCII. The bug surfaces when a customer has a storage account key or SAS token containing characters that differ between encodings, or when the file has a BOM that confuses rclone's parser.
|
||||
Developers assume inverting colors preserves contrast ratios. They do not. Concrete example from this codebase: `text-gray-500` (#6b7280) on `bg-white` (#ffffff) gives 4.6:1 contrast -- barely passing AA. The same `text-gray-500` on `bg-gray-900` (#111827) gives only 3.5:1 -- failing AA for normal text.
|
||||
|
||||
**Consequences:**
|
||||
- rclone silently reads a corrupt config; authentication fails with opaque errors
|
||||
- Hard to reproduce because it only manifests with certain key contents
|
||||
**How to avoid:**
|
||||
Define separate color values per theme within the token system. In dark mode, help text must use a lighter gray (equivalent of `text-gray-400`), errors must use a lighter red (equivalent of `text-red-400`). Semantic tokens centralize these mappings so each value is defined once. Verify every text/background pair with the browser DevTools accessibility panel or WebAIM contrast checker against WCAG AA 4.5:1 minimum for normal text, 3:1 for large text.
|
||||
|
||||
**Prevention:**
|
||||
- The generated script must always write the config with explicit UTF-8 no-BOM encoding:
|
||||
```powershell
|
||||
[System.IO.File]::WriteAllText($configPath, $configContent, [System.Text.Encoding]::UTF8)
|
||||
```
|
||||
or
|
||||
```powershell
|
||||
Set-Content -Path $configPath -Value $configContent -Encoding UTF8
|
||||
```
|
||||
Note: In PowerShell 5.1, `-Encoding UTF8` writes UTF-8 *with* BOM. Use `[System.IO.File]::WriteAllText` with `new System.Text.UTF8Encoding($false)` to guarantee no BOM.
|
||||
- The wizard's script template must hardcode the correct write method; never leave encoding to PS default
|
||||
**Warning signs:**
|
||||
- Help text feels "hard to read" in dark mode during visual review
|
||||
- Browser DevTools accessibility audit flagging contrast ratios below 4.5:1
|
||||
- Error states visually blending into background colors
|
||||
|
||||
**Detection:**
|
||||
- Open the written config in a hex editor: UTF-16 has `FF FE` as first bytes; UTF-8 BOM has `EF BB BF`
|
||||
- rclone error: "unexpected character at start of file" or authentication failures on otherwise valid credentials
|
||||
|
||||
**Phase relevance:** Any phase producing the PowerShell script template
|
||||
**Phase to address:**
|
||||
Phase 1 (Token Definition) for color values. Phase 2 (Component Overhaul) for application. Each component restyle must include a contrast verification before marking complete.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 4: rclone config section names collide with rclone reserved names or contain invalid characters
|
||||
### Pitfall 4: Breaking 131 Test Selectors During Component Restyling
|
||||
|
||||
**What goes wrong:**
|
||||
rclone remote names in the config are used on the command line as `remotename:path`. The name becomes part of shell arguments and rclone's internal addressing. Names with spaces, colons, forward slashes, or square brackets break the INI section header (`[remote name]` is valid INI only if the name contains no `]`). Names that match rclone built-in remote types (e.g., naming a remote "local", "union", "memory") cause confusing errors. Names starting with a dash conflict with CLI flag parsing.
|
||||
The test suite uses 131 occurrences of `getByText`, `getByRole`, `getByTestId`, `getByLabelText`, and `queryBy` selectors across 5 test files (App.test.tsx, StepIndicator.test.tsx, ReviewStep.test.tsx, RemoteConfigStep.test.tsx, BackendSelectionStep.test.tsx). Restyling components breaks tests by: changing visible text content, wrapping elements in new containers that alter DOM hierarchy, replacing native elements with styled equivalents (changing roles), or removing/renaming aria attributes.
|
||||
|
||||
**Why it happens:**
|
||||
The wizard lets IT admins freely type a remote name without validation. The name goes into `[user input]` verbatim.
|
||||
UI overhauls touch the same JSX that tests query. Specific examples from this codebase:
|
||||
- `getByText(/Backend/)` in StepIndicator.test.tsx breaks if the label text changes or gets wrapped in a `<span>` that splits the text node
|
||||
- `getAllByRole('button')` breaks if buttons become styled `<a>` tags or `<div>` elements
|
||||
- `screen.findByText('2', { selector: '[data-testid="step"]' })` breaks if data-testid attributes are renamed during refactoring
|
||||
|
||||
**Consequences:**
|
||||
- Config is syntactically broken (rclone fails to parse)
|
||||
- Or config parses but the remote cannot be referenced on the command line
|
||||
- Error messages are cryptic: "Failed to create file system for remotename: didn't find section in config file"
|
||||
**How to avoid:**
|
||||
1. Run the full 159-test suite after every single component change, not in a batch at the end.
|
||||
2. Restyle one component, verify tests, commit. Never batch-restyle all components then fix all tests.
|
||||
3. When restructuring JSX, preserve text content and element roles. A `<button>` must remain a `<button>`.
|
||||
4. If adding wrapper elements, ensure text nodes are not split (e.g., `getByText(/Backend/)` matches a single text node, not text across siblings).
|
||||
|
||||
**Prevention:**
|
||||
- Validate remote names in the wizard UI before generation: allow only `[a-zA-Z0-9_-]`, max ~40 chars, no leading dash
|
||||
- Show a live preview of the section header: `[my-remote]`
|
||||
- Reject reserved-looking names or warn on them
|
||||
**Warning signs:**
|
||||
- More than 3 test failures appearing simultaneously after a restyle
|
||||
- Tests failing with "Unable to find element" errors
|
||||
- `getByRole` queries returning unexpected counts
|
||||
|
||||
**Detection:**
|
||||
- rclone returns "didn't find section in config file" when the name contains special characters
|
||||
- rclone returns parse error when name contains `]`
|
||||
|
||||
**Phase relevance:** Wizard input validation phase; config generation phase
|
||||
**Phase to address:**
|
||||
Every phase -- each component change must include a "159 tests green" gate. This is the single most likely source of rework.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 5: Secrets embedded in generated scripts are exposed in Intune admin center logs
|
||||
### Pitfall 5: Flash of Unstyled Content (FOUC) on Dark Mode Load
|
||||
|
||||
**What goes wrong:**
|
||||
Intune logs PowerShell script output and stores it in the Azure portal (AgentExecutor.log on endpoint + reporting in Intune admin center). If the generated script echoes the config content or uses `Write-Host` with credential values for debugging, those secrets are persisted in logs accessible to any Intune admin.
|
||||
The app loads with light mode CSS, then JavaScript runs and toggles the `.dark` class, causing a visible white flash. For IT professionals who often use dark OS themes, this flash is jarring and signals low quality.
|
||||
|
||||
**Why it happens:**
|
||||
Developers add debug output during testing ("Writing config: [content]") and forget to remove it. Or error handlers dump the config on failure.
|
||||
React runs after the initial paint. If dark mode preference is stored in `localStorage` and applied via `useEffect` or React state, the first paint is always light mode. The class toggle happens milliseconds later, but the flash is visible.
|
||||
|
||||
**Consequences:**
|
||||
- Storage account keys, SAS tokens, or OAuth secrets appear in Intune reporting
|
||||
- Violates least-privilege and secrets hygiene; potential audit/compliance failure
|
||||
**How to avoid:**
|
||||
Add a synchronous inline `<script>` in the `<head>` of `index.html` (before any CSS or React bundle loads):
|
||||
```html
|
||||
<script>
|
||||
if (localStorage.theme === 'dark' ||
|
||||
(!('theme' in localStorage) &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
</script>
|
||||
```
|
||||
This executes before first paint, preventing any flash. The React toggle component then reads and syncs with the already-applied state.
|
||||
|
||||
**Prevention:**
|
||||
- The wizard's generated script template must never echo credential values
|
||||
- Use a sentinel like `Write-Host "Writing config to $configPath"` (path only, no content)
|
||||
- Add a comment in the generated script: `# Do not add Write-Host or logging for $configContent`
|
||||
- The wizard UI must display a security warning at download time (already planned per PROJECT.md)
|
||||
**Warning signs:**
|
||||
- White flash visible when loading the app with dark mode previously enabled
|
||||
- Users on dark OS themes seeing a brief light flash on every page load
|
||||
|
||||
**Detection:**
|
||||
- Audit the script template for any interpolation of credential variables into strings passed to output cmdlets
|
||||
|
||||
**Phase relevance:** Script template design (early phase); security review before any release
|
||||
**Phase to address:**
|
||||
Phase 1 (Theme Foundation) -- the FOUC prevention script must ship together with the dark mode toggle implementation, not as a later fix.
|
||||
|
||||
---
|
||||
|
||||
## Moderate Pitfalls
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 6: Group Policy overrides PowerShell execution policy set in the script
|
||||
### Pitfall 6: Theme Context Re-renders Causing Full Wizard Re-render
|
||||
|
||||
**What goes wrong:**
|
||||
The generated script attempts to set `Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned` or `Bypass`. In enterprise environments with Group Policy managing `MachinePolicy` or `UserPolicy` execution policy scopes, the script's `Set-ExecutionPolicy` call has no effect — Group Policy always wins. The script appears to succeed (no error from `Set-ExecutionPolicy`) but subsequent script logic may still fail if the endpoint GP enforces `AllSigned` or `Restricted`.
|
||||
Adding a `ThemeContext` that stores `{ theme: 'dark', accentColor: 'blue' }` causes every `useTheme()` consumer to re-render when any theme value changes. Since the app already has a `WizardContext` with `useReducer`, adding another context that triggers re-renders on toggle will cause all 4 wizard steps to re-render, potentially resetting form input focus or scroll position.
|
||||
|
||||
**Why it happens:**
|
||||
Official Microsoft docs confirm: "Set-ExecutionPolicy doesn't override a Group Policy, even if the user preference is more restrictive than the policy." Intune itself bypasses execution policy for its own scripts (IME uses `-ExecutionPolicy Bypass` internally), but any child processes spawned by the script inherit the GP-enforced policy.
|
||||
React Context re-renders every consumer when the provider value changes (referential equality). Tutorials show `<ThemeProvider value={{ theme, setTheme }}>` where a new object is created on every render. Even with `useMemo`, toggling theme changes the value and re-renders all consumers.
|
||||
|
||||
**Consequences:**
|
||||
- Scripts that call `& rclone.exe` or invoke helper `.ps1` files from within the script fail with execution policy errors
|
||||
- Developers test on unmanaged machines and never observe GP interference
|
||||
**How to avoid:**
|
||||
Do NOT store theme in React Context. Apply the theme via the `.dark` CSS class on `<html>` element and CSS custom properties. Theme toggling becomes a DOM class toggle (zero React re-renders). Store the toggle state in a small component-local state that only the toggle button uses:
|
||||
```tsx
|
||||
function ThemeToggle() {
|
||||
const [isDark, setIsDark] = useState(() =>
|
||||
document.documentElement.classList.contains('dark')
|
||||
);
|
||||
const toggle = () => {
|
||||
document.documentElement.classList.toggle('dark');
|
||||
setIsDark(d => !d);
|
||||
localStorage.theme = isDark ? 'light' : 'dark';
|
||||
};
|
||||
return <button onClick={toggle}>...</button>;
|
||||
}
|
||||
```
|
||||
Only the toggle button re-renders. No context, no provider, no cascade. Accent color works the same way -- set a CSS variable on `<html>`, no React re-render.
|
||||
|
||||
**Prevention:**
|
||||
- The generated script should not attempt to change execution policy
|
||||
- Any sub-scripts should be invoked with `-ExecutionPolicy Bypass` in the powershell.exe call, or avoided entirely (inline everything)
|
||||
- Document this in the wizard's "Intune deployment" output pane
|
||||
**Warning signs:**
|
||||
- Form inputs losing focus when toggling dark mode
|
||||
- Visible flicker across the entire wizard when toggling
|
||||
- React DevTools profiler showing all components re-rendering on theme change
|
||||
|
||||
**Detection:**
|
||||
- `Get-ExecutionPolicy -List` on target machine shows `MachinePolicy = AllSigned`
|
||||
- Script works in test but fails on managed fleet endpoints
|
||||
|
||||
**Phase relevance:** Script generation phase; testing guidance
|
||||
**Phase to address:**
|
||||
Phase 1 (Theme Foundation) -- architecture decision: CSS class approach, not React Context for theming.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 7: 32-bit vs 64-bit PowerShell host affects path resolution
|
||||
### Pitfall 7: Form Accessibility Regressions During Restyling
|
||||
|
||||
**What goes wrong:**
|
||||
Intune's default is to run scripts in the 32-bit PowerShell host (`Run script in 64-bit PowerShell host = No`). On 64-bit Windows, 32-bit processes use File System Redirector: `System32` resolves to `SysWOW64`, and `%ProgramFiles%` resolves to `%ProgramFiles(x86)%`. If the generated script installs rclone to `$env:ProgramFiles\rclone\` under 32-bit context, the binary lands in `C:\Program Files (x86)\rclone\`, not `C:\Program Files\rclone\`. When the user runs rclone from a 64-bit shell, they look in `Program Files` and find nothing.
|
||||
The current FieldRenderer has proper `<label htmlFor>` and `<input id>` associations, error message display, and tooltip buttons with `aria-label`. During restyling, these connections break: labels get separated from inputs by decorative wrapper divs, error messages lose their visual proximity, or card wrappers introduce unexpected tab stops.
|
||||
|
||||
**Why it happens:**
|
||||
The Intune "Run in 64-bit" option defaults to `No` per Microsoft docs. Developers test in a normal 64-bit PowerShell session.
|
||||
Visual-focused restyling treats JSX as a canvas for layout. Developers restructure DOM for card layouts, add icon containers, or wrap form groups in Material-style "outlined" containers. The label-input-error chain relies on specific DOM relationships. The FieldRenderer currently does NOT use `aria-describedby` for error messages -- this is already noted as v1.1 tech debt. Restyling is the right time to fix this, but also the highest risk time to break what works.
|
||||
|
||||
**Consequences:**
|
||||
- rclone binary installed to wrong Program Files variant
|
||||
- PATH entries or shortcuts point to non-existent location
|
||||
**How to avoid:**
|
||||
1. Fix the `aria-describedby` gap as part of the restyling, not separately. Add `aria-describedby={error ? \`${field.key}-error\` : undefined}` to inputs and `id={\`${field.key}-error\`}` to error paragraphs.
|
||||
2. After restyling each form component, verify: label click focuses the input, error messages are associated with inputs, tab order follows visual order.
|
||||
3. Keep the `<label htmlFor={field.key}>` + `<input id={field.key}>` pattern intact regardless of wrapper changes.
|
||||
|
||||
**Prevention:**
|
||||
- The generated script should use `$env:ProgramW6432` (always the native 64-bit Program Files on 64-bit Windows) or hardcode `C:\Program Files\rclone\`
|
||||
- The wizard UI for Intune output should recommend enabling "Run script in 64-bit PowerShell host" and document why
|
||||
- Alternatively, use `C:\ProgramData\rclone\` which is not subject to WOW64 redirection
|
||||
**Warning signs:**
|
||||
- Clicking a label no longer focuses its input
|
||||
- Tab key skips inputs or gets trapped in decorative elements
|
||||
- Browser form autofill stops working on restyled inputs
|
||||
|
||||
**Detection:**
|
||||
- rclone binary absent from expected path after deployment
|
||||
- `[System.Environment]::Is64BitProcess` returns `False` inside the running script
|
||||
|
||||
**Phase relevance:** Script generation phase; Intune deployment option
|
||||
**Phase to address:**
|
||||
Phase 2 (Component Overhaul) -- every form component restyle must include an accessibility verification step. Resolve the v1.1 aria tech debt item here rather than deferring again.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 8: OAuth-backed backends require interactive browser flow — incompatible with SYSTEM/headless deployment
|
||||
## Technical Debt Patterns
|
||||
|
||||
**What goes wrong:**
|
||||
rclone backends that use OAuth (OneDrive, Google Drive, Dropbox, Box, etc.) require an interactive browser authorization step to generate the token. The rclone config for these backends includes an `token = {...}` JSON blob. If the IT admin generates a config without pre-populating this token, the deployment script writes a config with no token. When rclone first runs on the endpoint, it attempts an interactive browser flow — which silently fails or hangs in a SYSTEM/headless context.
|
||||
Shortcuts that seem reasonable but create long-term problems.
|
||||
|
||||
**Why it happens:**
|
||||
The wizard generates the config from form inputs. For OAuth backends, the wizard cannot complete the OAuth flow on behalf of the user — there is no rclone running in the browser context to perform `rclone config`. The IT admin might not realize the token needs to be obtained separately on a reference machine.
|
||||
| Shortcut | Immediate Benefit | Long-term Cost | When Acceptable |
|
||||
|----------|-------------------|----------------|-----------------|
|
||||
| Adding `dark:` to every className instead of tokens | Fast, no refactor needed | 73+ locations to maintain, every new component needs dual classes | Never -- token approach costs the same upfront and scales |
|
||||
| Using `!important` to fix specificity issues | Immediate visual fix | Cascading specificity arms race, impossible to override later | Never |
|
||||
| Storing theme only in React state (not localStorage) | Simpler code | Preference lost on refresh, FOUC on every load | Never -- localStorage + inline script is trivial |
|
||||
| Skipping contrast verification "will check later" | Faster shipping | Accessibility failures discovered post-ship, painful to retroactively audit all 73 class locations | Never -- check during each component restyle |
|
||||
| Building a full design system with token categories for every MD3 role | "Complete" spec adherence | Over-engineered for a 4-step wizard with ~10 components; 80% of tokens go unused | Never for this app -- pick the 15-20 tokens that matter |
|
||||
| Copying MD3 token names verbatim (md-sys-color-surface-container-highest) | Matches Google spec exactly | Verbose, unfamiliar to Tailwind developers, poor DX for a small team | Never -- use simplified semantic names (surface, on-surface, primary) |
|
||||
| Adding MUI or another component library for "proper" MD3 | Instant MD3 components | +200KB bundle, specificity wars with Tailwind, two styling systems to maintain | Never for this app -- 10 components do not justify a library |
|
||||
|
||||
**Consequences:**
|
||||
- Deployed rclone silently does nothing or opens a browser on the endpoint
|
||||
- Most prominent with OneDrive; affects any backend requiring `rclone authorize`
|
||||
## Integration Gotchas
|
||||
|
||||
**Prevention:**
|
||||
- For OAuth backends, the wizard must show a prominent notice: "This backend requires an OAuth token. You must run `rclone config` or `rclone authorize` on a reference Windows machine as the target user, then copy the resulting token value into this wizard."
|
||||
- The wizard should provide a dedicated "OAuth token" input field for token-based backends, with instructions for how to extract the token from `rclone config show remotename`
|
||||
- Consider warning against deploying OAuth backends via SYSTEM-context Intune scripts entirely; recommend user-context deployment instead
|
||||
Common mistakes when connecting theme infrastructure to existing systems.
|
||||
|
||||
**Detection:**
|
||||
- Config section for OneDrive/GDrive has no `token =` line
|
||||
- rclone first-run opens a browser on the endpoint or exits with "no token found"
|
||||
| Integration | Common Mistake | Correct Approach |
|
||||
|-------------|----------------|------------------|
|
||||
| react-hook-form + restyled inputs | Wrapping `<input>` in a custom component that breaks `register()` ref forwarding | Use `React.forwardRef` on any custom input wrapper, or keep native `<input>` with Tailwind classes (preferred for this app) |
|
||||
| Zod validation + error display | Moving error `<p>` tags away from their input during restyle, breaking visual association | Keep error message immediately after its input in DOM order; add `aria-describedby` |
|
||||
| WizardContext + theme toggle | Creating a ThemeContext provider that causes WizardProvider consumers to re-render | Theme via CSS class on `<html>` (zero React re-renders), NOT via React context |
|
||||
| CSS hidden auth toggles (AzureAuthToggle / SftpAuthToggle) | Restyling visible state but forgetting the hidden state, breaking `className="hidden"` pattern | Verify both auth toggle states render correctly in both light and dark modes |
|
||||
| StepIndicator inline styles | Replacing `style={{ fontWeight: 'bold' }}` with Tailwind classes but altering text content structure | Replace inline styles with Tailwind classes (`font-bold`, `font-normal`, `text-muted`) while keeping text content strings identical for test compatibility |
|
||||
| BackendCard selection state | Changing selection indicator (e.g., border color) to use tokens but forgetting dark mode variant | Selected card must be visually distinct in both themes; test with all 7 backends |
|
||||
|
||||
**Phase relevance:** Backend-specific configuration phase; wizard backend selection step
|
||||
## Performance Traps
|
||||
|
||||
---
|
||||
| Trap | Symptoms | Prevention | When It Breaks |
|
||||
|------|----------|------------|----------------|
|
||||
| Theme stored in React Context causing re-renders | All 4 wizard steps re-render on every toggle; form focus lost | CSS class on `<html>`, no React context for theme | Immediate on every toggle |
|
||||
| Importing full component library for 10 components | Bundle doubles (+200KB gzipped for MUI) | Build MD3 styles with Tailwind tokens; zero additional dependencies | Immediate -- slower first load |
|
||||
| CSS transition on every property during theme switch | 200ms lag on every element when toggling dark mode | Transition only `background-color` and `color` on body; skip borders/shadows | Noticeable with 50+ DOM elements |
|
||||
| Over-using CSS custom properties on every element | Slow repaints when toggling theme on low-end devices | Define tokens on `:root` / `.dark`, let inheritance cascade naturally | On low-end devices or with 100+ custom properties |
|
||||
|
||||
### Pitfall 9: SAS tokens and storage keys contain characters that need escaping in INI values
|
||||
## UX Pitfalls
|
||||
|
||||
**What goes wrong:**
|
||||
Azure SAS tokens contain `%`, `=`, `&`, and `+` characters. Azure storage keys contain `+` and `/` and end in `==`. In rclone's INI config format, values are read until end-of-line — no quoting needed for most characters — but if the value accidentally contains a line-break (e.g., from copy-paste in a browser field that wraps), the config is truncated silently. If the generated value is also used inside a PowerShell string interpolation (e.g., `"sas_url = $sasToken"`), PowerShell variable substitution can corrupt values containing `$`.
|
||||
| Pitfall | User Impact | Better Approach |
|
||||
|---------|-------------|-----------------|
|
||||
| Dark mode toggle buried in settings | IT pros who want dark mode cannot find it | Visible toggle in app header, immediately accessible |
|
||||
| No system preference detection | User has OS dark mode, app loads light | Default to OS preference via `prefers-color-scheme`, with manual override stored in localStorage |
|
||||
| Accent color picker with unlimited options | Analysis paralysis, clashing colors | 3-5 curated accent colors that all pass contrast checks in both themes |
|
||||
| Theme transition animation on every element | Jarring, slow, distracting on toggle | Subtle 150ms transition on background-color and color on body only |
|
||||
| Dark mode applied but OutputBlock code still light | Inconsistent feel in the most important step (Review/download) | OutputBlock must respect dark mode for generated config and script previews |
|
||||
| Security warning banner lost in dark mode | Users miss the credential security warning before download | Warning must remain high-contrast and prominent (use error/warning token colors) in both modes |
|
||||
|
||||
**Why it happens:**
|
||||
The wizard builds the config as a JavaScript template literal. Storage keys and SAS tokens pasted by users may include trailing newlines or spaces. PowerShell double-quoted strings treat `$` as variable prefix.
|
||||
## "Looks Done But Isn't" Checklist
|
||||
|
||||
**Consequences:**
|
||||
- Truncated SAS token causes authentication failures with opaque Azure storage errors
|
||||
- Corrupted key causes "AuthenticationFailed" from Azure
|
||||
Things that appear complete but are missing critical pieces.
|
||||
|
||||
**Prevention:**
|
||||
- Trim all credential inputs in the wizard before inserting into the config (strip leading/trailing whitespace including `\n`, `\r`)
|
||||
- In the PowerShell script template, use single-quoted strings for the config content (PowerShell single-quoted strings do not interpolate `$`):
|
||||
```powershell
|
||||
$configContent = @'
|
||||
[myremote]
|
||||
type = azureblob
|
||||
account = mystorageaccount
|
||||
key = ABC+xyz==
|
||||
'@
|
||||
```
|
||||
(here-string with single-quote terminator)
|
||||
- Validate that credential inputs do not contain newlines before generating
|
||||
- [ ] **Dark mode select dropdowns:** Browser renders `<option>` elements with OS colors -- white dropdown menus appear on dark backgrounds on some browsers. Verify on Chrome, Firefox, Edge.
|
||||
- [ ] **Dark mode scrollbars:** Light scrollbars on dark backgrounds look broken. Apply `scrollbar-color` CSS property or use `dark` color-scheme.
|
||||
- [ ] **Error text contrast:** `text-red-600` on dark backgrounds has insufficient contrast. Must use lighter red (equivalent of `text-red-400`) in dark mode via tokens.
|
||||
- [ ] **Focus rings in dark mode:** `focus:ring-blue-300` is nearly invisible on dark backgrounds. Must use `focus:ring-blue-500` equivalent in dark mode.
|
||||
- [ ] **Placeholder text in dark mode:** Light gray placeholder text vanishes on dark input backgrounds. Verify placeholder is visible in both modes.
|
||||
- [ ] **Security warning banner:** The credential warning in ReviewStep must remain prominent and high-contrast in dark mode (not just "inverted").
|
||||
- [ ] **Accent color + dark background:** Verify every accent color option still passes WCAG AA 4.5:1 against the dark surface background.
|
||||
- [ ] **BackendCard hover and selected states:** Card states must be visually distinguishable in both modes with all 7 backends.
|
||||
- [ ] **Disabled button contrast:** Disabled buttons using lower opacity reduce contrast further on dark backgrounds. Use distinct disabled token colors instead of opacity.
|
||||
- [ ] **PasswordField show/hide toggle:** The eye icon/button must be visible in both themes.
|
||||
- [ ] **Tooltip info boxes:** The `bg-blue-50 border-blue-200 text-blue-700` tooltip in FieldRenderer needs a dark mode equivalent that maintains readability.
|
||||
|
||||
**Detection:**
|
||||
- Config file, when opened, shows a truncated key value
|
||||
- rclone error: "failed to parse config file" or Azure "AuthenticationFailed"
|
||||
## Recovery Strategies
|
||||
|
||||
**Phase relevance:** Config generation logic (core phase)
|
||||
When pitfalls occur despite prevention, how to recover.
|
||||
|
||||
---
|
||||
| Pitfall | Recovery Cost | Recovery Steps |
|
||||
|---------|---------------|----------------|
|
||||
| Hardcoded colors everywhere (no tokens) | MEDIUM | Extract to CSS variables in one pass, then find-replace all 73 className usages. ~2 hours for this codebase. |
|
||||
| Test suite broken by batch restyle | LOW-MEDIUM | `git stash` the batch change, restyle one component at a time verifying tests between each. |
|
||||
| FOUC on dark mode | LOW | Add 5-line inline script to `index.html <head>`. 10-minute fix. |
|
||||
| Specificity conflicts from component library | HIGH | Remove component library, rebuild styles with Tailwind. Prevention is far cheaper than recovery. |
|
||||
| Accessibility regressions in forms | MEDIUM | Audit with browser accessibility tools, fix label/input/aria associations. Harder to find than to fix. |
|
||||
| Dark mode contrast failures | LOW-MEDIUM | With centralized tokens: update token values once. Without tokens: hunt through all 73 className usages. |
|
||||
| Theme context re-renders | LOW | Remove ThemeContext, move to CSS class approach. ~30 minutes if caught early. |
|
||||
|
||||
### Pitfall 10: Windows path length limit (MAX_PATH = 260) breaks rclone operations on deep directory trees
|
||||
## Pitfall-to-Phase Mapping
|
||||
|
||||
**What goes wrong:**
|
||||
On Windows versions before Windows 10 1607, and on any Windows where the Long Path registry key is not set, paths exceeding 260 characters cause rclone operations to fail silently or with cryptic I/O errors. rclone syncing deep SharePoint or OneDrive folder trees commonly hits this. The deployment script may also fail if it writes files to paths that are too long (e.g., user profile paths with long usernames inside long corporate folder structures).
|
||||
|
||||
**Why it happens:**
|
||||
Windows enforces MAX_PATH = 260 by default per `kernel32.dll`. IT admins don't control the endpoint's registry setting. The wizard generates scripts without path-length guards.
|
||||
|
||||
**Consequences:**
|
||||
- rclone skips or errors on files with long paths
|
||||
- `New-Item` or `Set-Content` in the PowerShell script itself can fail if the config destination path is long
|
||||
|
||||
**Prevention:**
|
||||
- The wizard should recommend using `C:\ProgramData\rclone\` (short path) for config and binary placement, not user-profile paths
|
||||
- Generated scripts should include a check and optionally enable long paths:
|
||||
```powershell
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1
|
||||
```
|
||||
(requires admin rights; typically available in SYSTEM context)
|
||||
- Document the limitation in the wizard output pane for Intune deployments
|
||||
|
||||
**Detection:**
|
||||
- rclone logs show `ERROR: ... path too long`
|
||||
- PowerShell script itself fails with "The specified path, file name, or both are too long"
|
||||
|
||||
**Phase relevance:** Script generation; deployment documentation phase
|
||||
|
||||
---
|
||||
|
||||
## Minor Pitfalls
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 11: Intune script runs once per device; config changes don't re-deploy unless script is modified
|
||||
|
||||
**What goes wrong:**
|
||||
Intune only re-runs a PowerShell script if the script content changes or is reassigned. If the IT admin generates a new config (different credentials, different remote name) and wants to update the deployed config, they must upload a new version of the script to Intune. If they upload the exact same script bytes with only a comment changed, the re-run is triggered. But if they don't know this, they think re-assigning the unchanged script will update endpoints — it won't.
|
||||
|
||||
**Prevention:**
|
||||
- Document this in the wizard output: "To update config on endpoints, modify and re-upload the script (e.g., bump a version comment) to trigger Intune re-execution."
|
||||
- Consider auto-inserting a `# Generated: [timestamp]` comment in each script so re-generated scripts always differ
|
||||
|
||||
**Phase relevance:** Documentation/UX phase
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 12: rclone binary download URL in the script becomes stale
|
||||
|
||||
**What goes wrong:**
|
||||
The generated script contains a hardcoded rclone download URL (e.g., `https://downloads.rclone.org/rclone-current-windows-amd64.zip`). rclone uses the filename `rclone-current-*` as a redirect alias. This URL is stable, but if the wizard hardcodes a specific version URL (e.g., `v1.68.0`) to ensure repeatability, that version URL remains functional but the binary may have known issues. If the wizard uses `current`, the binary silently upgrades, potentially introducing breaking changes.
|
||||
|
||||
**Prevention:**
|
||||
- Use the `rclone-current-windows-amd64.zip` alias for the default path (always latest stable)
|
||||
- Allow an override field for IT admins who want to pin a version
|
||||
- Add a comment in the generated script stating the resolved version strategy
|
||||
|
||||
**Phase relevance:** Script generation (rclone install option)
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 13: Generated config has Windows-style line endings that cause issues on cross-platform rclone use
|
||||
|
||||
**What goes wrong:**
|
||||
JavaScript running in a browser on Windows may produce `\r\n` line endings when building the config string (less likely with modern JS but possible with string concatenation involving platform newlines). rclone's INI parser handles `\r\n` correctly on Windows, but if the config is later copied to a Linux/macOS system, the `\r` characters appear in values.
|
||||
|
||||
**Prevention:**
|
||||
- Explicitly normalize line endings to `\n` in the config generation logic before download
|
||||
- Use `content.replace(/\r\n/g, '\n').replace(/\r/g, '\n')` before creating the Blob for download
|
||||
|
||||
**Phase relevance:** Config generation (frontend logic)
|
||||
|
||||
---
|
||||
|
||||
## Phase-Specific Warnings
|
||||
|
||||
| Phase Topic | Likely Pitfall | Mitigation |
|
||||
|-------------|---------------|------------|
|
||||
| Script template design | SYSTEM context config path mismatch (Pitfall 1) | Use machine-wide path; document context options |
|
||||
| rclone install option | 200 KB Intune script size limit (Pitfall 2) | Download-only; never embed binary |
|
||||
| Script file write logic | PowerShell encoding writes UTF-16 BOM (Pitfall 3) | Use `[System.IO.File]::WriteAllText` with explicit UTF-8 no-BOM |
|
||||
| Remote name input field | Invalid characters in section name (Pitfall 4) | Validate `[a-zA-Z0-9_-]` in UI before generation |
|
||||
| Debug/error output in script | Secrets exposed in Intune logs (Pitfall 5) | No credential interpolation in output cmdlets |
|
||||
| Execution policy in script | GP overrides any Set-ExecutionPolicy call (Pitfall 6) | Do not set policy; use `-ExecutionPolicy Bypass` on sub-processes |
|
||||
| Intune script options | 32-bit host path redirection (Pitfall 7) | Use `$env:ProgramW6432` or `C:\ProgramData\rclone\` |
|
||||
| OAuth backend config | Headless OAuth flow impossible (Pitfall 8) | Require pre-obtained token; prominent wizard warning |
|
||||
| Credential input handling | SAS/key corruption via whitespace or `$` (Pitfall 9) | Trim inputs; single-quoted PowerShell here-strings |
|
||||
| Config/binary placement | MAX_PATH exceeded on deep trees (Pitfall 10) | Short machine-wide paths; optionally enable long paths |
|
||||
| Re-deployment UX | Intune won't re-run identical script (Pitfall 11) | Auto-insert timestamp comment; document update flow |
|
||||
| rclone download URL | Pinned URL goes stale (Pitfall 12) | Default to `rclone-current`; allow version override |
|
||||
| Config string generation | Windows CRLF in config file (Pitfall 13) | Normalize to LF before Blob creation |
|
||||
|
||||
---
|
||||
| Pitfall | Prevention Phase | Verification |
|
||||
|---------|------------------|--------------|
|
||||
| Tailwind v4 dark mode config (P1) | Phase 1: Theme Foundation | `dark:bg-gray-900` toggles correctly via `.dark` class on `<html>` |
|
||||
| Hardcoded colors (P2) | Phase 1 (Tokens) + Phase 2 (Overhaul) | Zero hardcoded Tailwind color classes remain in component JSX |
|
||||
| Dark mode contrast failures (P3) | Phase 2: Component Overhaul | Every text/background pair checked, all pass WCAG AA 4.5:1 |
|
||||
| Breaking test selectors (P4) | Every phase | All 159 tests pass after each individual component restyle |
|
||||
| FOUC (P5) | Phase 1: Theme Foundation | Load app with `localStorage.theme = 'dark'`, verify no white flash |
|
||||
| Theme context re-renders (P6) | Phase 1: Architecture Decision | Theme toggle causes zero React re-renders outside the toggle button itself |
|
||||
| Form accessibility regressions (P7) | Phase 2: Component Overhaul | Label-input associations verified, `aria-describedby` added for all error messages |
|
||||
| CSS specificity conflicts | Phase 1: Architecture Decision | Decision documented: no component library, Tailwind-only approach |
|
||||
| Over-engineering design system | Phase 1: Token Definition | Token count stays under 20 semantic colors; no unused token categories |
|
||||
|
||||
## Sources
|
||||
|
||||
- Microsoft Learn — PowerShell scripts in Intune (updated 2025-10-02): https://learn.microsoft.com/en-us/intune/intune-service/apps/powershell-scripts
|
||||
- Microsoft Learn — Intune Management Extension (updated 2026-03-17): https://learn.microsoft.com/en-us/intune/intune-service/apps/intune-management-extension
|
||||
- Microsoft Learn — Set-ExecutionPolicy reference (updated 2025-04-15): https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy
|
||||
- Microsoft Learn — Naming Files, Paths, and Namespaces (Win32): https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
||||
- Microsoft Learn — Code Page Identifiers: https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
|
||||
- rclone config format and Windows behavior: training data (MEDIUM confidence; rclone official docs were inaccessible during research session — verify against https://rclone.org/docs/ before finalizing)
|
||||
- [Tailwind CSS v4 Dark Mode docs](https://tailwindcss.com/docs/dark-mode) -- official, verified (HIGH confidence)
|
||||
- [Tailwind v4 upgrade discussion #16517](https://github.com/tailwindlabs/tailwindcss/discussions/16517) -- community reports of broken dark mode after upgrade
|
||||
- [Tailwind specificity discussion #12714](https://github.com/tailwindlabs/tailwindcss/discussions/12714) -- class collisions with component libraries
|
||||
- [BOIA: Dark Mode and WCAG Contrast](https://www.boia.org/blog/offering-a-dark-mode-doesnt-satisfy-wcag-color-contrast-requirements) -- dark mode does not auto-satisfy WCAG
|
||||
- [Complete Dark Mode Accessibility Guide (2026)](https://blog.greeden.me/en/2026/02/23/complete-accessibility-guide-for-dark-mode-and-high-contrast-color-design-contrast-validation-respecting-os-settings-icons-images-and-focus-visibility-wcag-2-1-aa/) -- WCAG 2.1 AA guidance for dark mode
|
||||
- [MUI MD3 adoption discussion #29345](https://github.com/mui/material-ui/issues/29345) -- MD3 implementation complexity
|
||||
- [React Context performance optimization](https://medium.com/zestgeek/performance-optimization-techniques-with-reacts-usecontext-5dc7e4ef6b25) -- re-render prevention patterns
|
||||
- Codebase analysis: 73 className usages, 131 test selectors across 5 files, 3 inline styles in StepIndicator, FieldRenderer aria-describedby gap confirmed (HIGH confidence -- direct code inspection)
|
||||
|
||||
---
|
||||
*Pitfalls research for: UI polish overhaul (MD3, dark mode, accent colors) on Ready2Blob v1.2*
|
||||
*Researched: 2026-03-31*
|
||||
|
||||
+216
-89
@@ -1,52 +1,189 @@
|
||||
# Technology Stack
|
||||
|
||||
**Project:** Ready2Blob
|
||||
**Researched:** 2026-03-26
|
||||
**Confidence note:** External verification tools were unavailable in this session. Version numbers reflect training data (knowledge cutoff August 2025). Verify all versions against npmjs.com before scaffolding.
|
||||
**Project:** Ready2Blob v1.2 — UI Polish & MD3 Overhaul
|
||||
**Researched:** 2026-03-31
|
||||
**Scope:** Stack ADDITIONS for Material Design 3, dark mode, accent colors, responsive improvements
|
||||
**Existing stack (validated, not re-researched):** Vite 6, React 18, TypeScript 5, Tailwind v4 (@tailwindcss/vite), react-hook-form 7, Zod 4, Vitest 4, JSZip
|
||||
|
||||
---
|
||||
|
||||
## Recommended Stack
|
||||
## Recommendation: Zero New Runtime Dependencies
|
||||
|
||||
### Core Framework
|
||||
The v1.2 UI overhaul should be achieved with **Tailwind v4's native theming system + hand-authored MD3 design tokens in CSS**. No component library. No runtime theming library. The existing approach (semantic HTML + Tailwind utilities) is the right foundation -- it just needs a proper token system layered on top.
|
||||
|
||||
**Rationale:** The app currently has zero component library dependencies and 159 passing tests. Introducing a component library (Material Tailwind, MUI, shadcn/ui) at this stage would:
|
||||
1. Require rewriting every existing component to match the library's API
|
||||
2. Break existing tests that assert on current DOM structure
|
||||
3. Add bundle weight for a wizard that needs at most 6-8 component types
|
||||
4. Create upgrade debt for a library the team doesn't control
|
||||
|
||||
Instead: define MD3 tokens as CSS custom properties, wire them into Tailwind v4's `@theme` directive, and build the small set of reusable patterns (card, input, button, elevation) as project-owned Tailwind utility compositions.
|
||||
|
||||
---
|
||||
|
||||
## New Stack Additions
|
||||
|
||||
### Design Token Generation (Dev Dependency Only)
|
||||
|
||||
| Technology | Version | Purpose | Why |
|
||||
|------------|---------|---------|-----|
|
||||
| React | 18.x | Component model, state management, rendering | Largest ecosystem, best-in-class multi-step form libraries all target React first. Hooks (useState, useReducer, useContext) provide exactly the right mental model for a wizard: local step state + shared config accumulator state. No SSR needed — this is 100% client-side rendering. |
|
||||
| Vite | 5.x | Build tooling, dev server, static asset bundling | Near-zero config for a React SPA. `vite build` produces a static `dist/` folder deployable to GitHub Pages, Netlify, or any CDN with no server required. HMR makes iteration fast. Replaces CRA, which is abandoned. Replaces Webpack, which requires painful configuration for something this simple. |
|
||||
| TypeScript | 5.x | Type safety | rclone config generation involves composing structured data (backend type, required fields per backend, optional flags) into string templates. TypeScript catches the inevitable "wrong field name" bugs at compile time rather than at user download time. The marginal overhead is worth it for a tool where correctness of generated output is the entire product. |
|
||||
| `@material/material-color-utilities` | 0.4.0 | Generate MD3 color palettes from seed color | Official Google library. Used at build/dev time via a small script to generate light + dark token sets from a single seed color. NOT bundled into the app -- it produces static CSS custom properties. This is the same algorithm the Material Theme Builder uses. HIGH confidence (official Google package, actively maintained). |
|
||||
|
||||
### Styling
|
||||
**How it works:** Write a one-time Node script (`scripts/generate-theme.ts`) that:
|
||||
1. Takes a seed color (hex)
|
||||
2. Uses `themeFromSourceColor()` to generate full MD3 palette (primary, secondary, tertiary, error, surface, outline, etc.)
|
||||
3. Outputs CSS custom properties in the `--md-sys-color-*` naming convention
|
||||
4. Writes to `src/theme-tokens.css` which is imported into `src/index.css`
|
||||
|
||||
This means the generated tokens are **static CSS** -- zero runtime cost, zero bundle impact from the color library.
|
||||
|
||||
### Tailwind v4 Theme Integration (No New Dependency)
|
||||
|
||||
| Technology | Version | Purpose | Why |
|
||||
|------------|---------|---------|-----|
|
||||
| Tailwind CSS | 3.x | Utility-first styling | No design system to maintain — each step of the wizard is a one-off layout. Tailwind's inline classes mean styling stays co-located with markup, avoiding CSS file sprawl. For a tool likely built by one or two developers, it eliminates the "where does this class live?" question. Avoid CSS Modules (too much file switching) and styled-components (runtime overhead, no benefit here). |
|
||||
| Tailwind v4 `@theme` directive | Already installed | Map MD3 tokens to Tailwind utility classes | Tailwind v4's `@theme` directive creates utility classes from CSS custom properties. Defining `--color-primary`, `--color-surface`, etc. in `@theme` blocks automatically generates `bg-primary`, `text-on-primary`, `bg-surface` utilities. No config file needed -- pure CSS. HIGH confidence (verified in official Tailwind v4 docs). |
|
||||
| Tailwind v4 `@custom-variant` | Already installed | Class-based dark mode toggle | Tailwind v4 replaces the old `darkMode: 'class'` config with `@custom-variant dark (&:where(.dark, .dark *));` in CSS. This enables the `dark:` prefix to respond to a `.dark` class on the HTML element. HIGH confidence (verified in official Tailwind v4 dark mode docs). |
|
||||
|
||||
### Form & Wizard State
|
||||
### No Other New Dependencies
|
||||
|
||||
| Technology | Version | Purpose | Why |
|
||||
|------------|---------|---------|-----|
|
||||
| react-hook-form | 7.x | Per-step form validation and field registration | The standard for React forms. Uncontrolled inputs with ref-based validation means no re-render on every keystroke — important when some wizard steps may have 10+ fields (e.g., S3 config). Native Zod integration via `@hookform/resolvers` allows schema-driven validation that mirrors the rclone backend field spec. |
|
||||
| Zod | 3.x | Schema definition and runtime validation | Per-backend field schemas (required vs optional, string format, enum values) map directly to Zod schemas. A `backends/azure.ts`, `backends/s3.ts`, etc. pattern lets each backend declare its own schema — react-hook-form validates against it per step. This is the correct abstraction: the schema IS the backend spec. |
|
||||
| Category | Decision | Rationale |
|
||||
|----------|----------|-----------|
|
||||
| Component library | **Do NOT add** | Current semantic HTML + Tailwind is correct. MD3 styling is achieved through tokens + utility classes, not through library components. |
|
||||
| CSS-in-JS | **Do NOT add** | Tailwind v4 handles everything via CSS. Adding styled-components or Emotion would conflict with the existing Tailwind approach. |
|
||||
| Theme toggle library (next-themes) | **Do NOT add** | next-themes is Next.js-focused. For a pure Vite SPA, a 15-line React hook (`useTheme`) with `localStorage` + `classList.toggle` is all that's needed. |
|
||||
| Animation library | **Do NOT add** | MD3 motion tokens (duration, easing) are CSS custom properties. Tailwind v4's `@theme` can define transition tokens. No framer-motion or similar needed for the subtle transitions in a wizard UI. |
|
||||
| Icon library | **Evaluate later** | If MD3 icons are desired, `@material-design-icons/svg` provides tree-shakeable SVGs. But this is a nice-to-have, not a v1.2 blocker. |
|
||||
|
||||
### File Generation & Download
|
||||
---
|
||||
|
||||
| Technology | Version | Purpose | Why |
|
||||
|------------|---------|---------|-----|
|
||||
| Native Blob API | — (browser built-in) | Text file download (rclone.conf, .ps1 scripts) | No library needed. `new Blob([content], { type: 'text/plain' })` + `URL.createObjectURL()` + programmatic anchor click is the standard pattern for single-file downloads. Zero dependency, works in all modern browsers. Using a library for this adds complexity without benefit. |
|
||||
| JSZip | 3.x | ZIP bundling of all generated files | When the user wants to download all files at once (rclone.conf + Intune script + RMM script), a ZIP is far better UX than three separate downloads. JSZip is the de-facto standard for client-side ZIP in browsers, actively maintained, no server required. `file-saver` is often paired with it for the `saveAs()` convenience but the Blob/anchor pattern works fine without it. |
|
||||
## Detailed Integration Plan
|
||||
|
||||
### State Management
|
||||
### 1. MD3 Color Token System
|
||||
|
||||
| Technology | Version | Purpose | Why |
|
||||
|------------|---------|---------|-----|
|
||||
| React built-ins (useState / useReducer / useContext) | 18.x | Wizard state, accumulated config object | No external state library needed. The wizard has one primary data structure: the accumulating rclone config object (backend type + per-backend fields + script options). A `useReducer` at the app root with a context provider gives all steps read/write access without prop drilling. This is a solved problem at this scale — Zustand/Redux are overkill. |
|
||||
The Material Design 3 color system uses ~29 semantic color roles (not raw palette values). These map to CSS custom properties:
|
||||
|
||||
### Hosting / Deployment
|
||||
```css
|
||||
/* Light theme tokens (generated from seed color) */
|
||||
:root {
|
||||
--md-sys-color-primary: #006A6A;
|
||||
--md-sys-color-on-primary: #FFFFFF;
|
||||
--md-sys-color-primary-container: #6FF7F6;
|
||||
--md-sys-color-on-primary-container: #002020;
|
||||
--md-sys-color-secondary: #4A6363;
|
||||
--md-sys-color-on-secondary: #FFFFFF;
|
||||
--md-sys-color-surface: #FAFDFC;
|
||||
--md-sys-color-on-surface: #191C1C;
|
||||
--md-sys-color-surface-container: #EFF2F1;
|
||||
--md-sys-color-surface-container-low: #F4F7F6;
|
||||
--md-sys-color-surface-container-high: #E9ECEB;
|
||||
--md-sys-color-outline: #6F7979;
|
||||
--md-sys-color-outline-variant: #BEC9C8;
|
||||
--md-sys-color-error: #BA1A1A;
|
||||
--md-sys-color-on-error: #FFFFFF;
|
||||
/* ... ~29 roles total */
|
||||
}
|
||||
|
||||
| Technology | Version | Purpose | Why |
|
||||
|------------|---------|---------|-----|
|
||||
| GitHub Pages or Netlify (free tier) | — | Static hosting | The output of `vite build` is a folder of HTML/CSS/JS. Any static host works. GitHub Pages is zero-cost and integrates directly with the repository. Netlify adds deploy previews for PRs, which is useful for validating config generation changes. No server needed at either. |
|
||||
/* Dark theme tokens (same seed, dark scheme) */
|
||||
.dark {
|
||||
--md-sys-color-primary: #4EDADA;
|
||||
--md-sys-color-on-primary: #003737;
|
||||
--md-sys-color-surface: #101414;
|
||||
--md-sys-color-on-surface: #E0E3E2;
|
||||
/* ... all roles overridden */
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Tailwind v4 Theme Wiring
|
||||
|
||||
```css
|
||||
/* src/index.css */
|
||||
@import "tailwindcss";
|
||||
@import "./theme-tokens.css"; /* Generated MD3 tokens */
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
/* Map MD3 tokens to Tailwind color utilities */
|
||||
--color-primary: var(--md-sys-color-primary);
|
||||
--color-on-primary: var(--md-sys-color-on-primary);
|
||||
--color-primary-container: var(--md-sys-color-primary-container);
|
||||
--color-on-primary-container: var(--md-sys-color-on-primary-container);
|
||||
--color-secondary: var(--md-sys-color-secondary);
|
||||
--color-on-secondary: var(--md-sys-color-on-secondary);
|
||||
--color-surface: var(--md-sys-color-surface);
|
||||
--color-on-surface: var(--md-sys-color-on-surface);
|
||||
--color-surface-container: var(--md-sys-color-surface-container);
|
||||
--color-surface-container-low: var(--md-sys-color-surface-container-low);
|
||||
--color-surface-container-high: var(--md-sys-color-surface-container-high);
|
||||
--color-outline: var(--md-sys-color-outline);
|
||||
--color-outline-variant: var(--md-sys-color-outline-variant);
|
||||
--color-error: var(--md-sys-color-error);
|
||||
--color-on-error: var(--md-sys-color-on-error);
|
||||
|
||||
/* MD3 Shape scale */
|
||||
--radius-xs: 4px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 28px;
|
||||
--radius-full: 9999px;
|
||||
|
||||
/* MD3 Elevation (box-shadows) */
|
||||
--shadow-elevation-0: none;
|
||||
--shadow-elevation-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
|
||||
--shadow-elevation-2: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 2px 6px 2px rgb(0 0 0 / 0.15);
|
||||
--shadow-elevation-3: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 4px 8px 3px rgb(0 0 0 / 0.15);
|
||||
--shadow-elevation-4: 0 2px 3px 0 rgb(0 0 0 / 0.3), 0 6px 10px 4px rgb(0 0 0 / 0.15);
|
||||
--shadow-elevation-5: 0 4px 4px 0 rgb(0 0 0 / 0.3), 0 8px 12px 6px rgb(0 0 0 / 0.15);
|
||||
|
||||
/* MD3 Motion tokens */
|
||||
--animate-md3-enter: md3-enter 0.2s cubic-bezier(0, 0, 0, 1);
|
||||
--animate-md3-exit: md3-exit 0.15s cubic-bezier(0.3, 0, 1, 1);
|
||||
|
||||
@keyframes md3-enter {
|
||||
from { opacity: 0; transform: scale(0.92); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
@keyframes md3-exit {
|
||||
from { opacity: 1; transform: scale(1); }
|
||||
to { opacity: 0; transform: scale(0.92); }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in components** then becomes natural Tailwind:
|
||||
```tsx
|
||||
<div className="bg-surface-container rounded-md shadow-elevation-1 dark:shadow-elevation-2">
|
||||
<h2 className="text-on-surface">Step Title</h2>
|
||||
<button className="bg-primary text-on-primary rounded-full px-6 py-2">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 3. Dark Mode Toggle Hook
|
||||
|
||||
No library needed. A simple React hook:
|
||||
|
||||
```typescript
|
||||
// src/hooks/useTheme.ts
|
||||
function useTheme() {
|
||||
const [theme, setTheme] = useState<'light' | 'dark' | 'system'>(() => {
|
||||
return (localStorage.getItem('theme') as 'light' | 'dark') ?? 'system';
|
||||
});
|
||||
// Toggle .dark class on <html>, persist to localStorage
|
||||
// Listen to prefers-color-scheme for 'system' mode
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Accent Color System
|
||||
|
||||
For user-selectable accent colors, the approach is:
|
||||
1. Offer 3-5 preset seed colors (not arbitrary color picker)
|
||||
2. Pre-generate token sets for each seed color at build time
|
||||
3. Switch accent by swapping a CSS class on `<html>` that loads a different set of `--md-sys-color-*` variables
|
||||
|
||||
This avoids runtime color generation (which would require bundling `@material/material-color-utilities`).
|
||||
|
||||
---
|
||||
|
||||
@@ -54,65 +191,53 @@
|
||||
|
||||
| Category | Recommended | Alternative | Why Not |
|
||||
|----------|-------------|-------------|---------|
|
||||
| Framework | React 18 | Vue 3 | Vue is a reasonable choice but the wizard library ecosystem (react-hook-form, Formik) is React-first. No strong reason to diverge. |
|
||||
| Framework | React 18 | Svelte / SvelteKit | Svelte has no widely-adopted multi-step form library. Would require hand-rolling wizard state. The compile-time model is elegant but not worth the ecosystem tradeoff here. |
|
||||
| Build tool | Vite | Create React App | CRA is officially deprecated by the React team. Not a valid choice for new projects in 2025. |
|
||||
| Build tool | Vite | Next.js | Next.js is a server-framework. Using it for a pure static SPA adds file-based routing conventions, SSR plumbing, and deployment assumptions that are all irrelevant here. `vite + react` is simpler and more appropriate. |
|
||||
| Styling | Tailwind CSS | Material UI / shadcn/ui | shadcn/ui is worth considering as a component library for accessible form elements (inputs, selects, checkboxes). It is built on Radix UI primitives and works with Tailwind. If the team wants pre-built accessible components rather than raw HTML + Tailwind, shadcn/ui is the right addition — not a replacement for Tailwind, but a layer on top. |
|
||||
| Forms | react-hook-form | Formik | Formik is older and uses controlled inputs (re-render on every keystroke). react-hook-form is the current standard and has better performance and Zod integration. |
|
||||
| ZIP | JSZip | fflate | fflate is faster and smaller than JSZip. Both are valid. JSZip has more documentation and community examples for the browser download pattern, making it easier to implement correctly without prior experience. If bundle size becomes a concern, swap to fflate. |
|
||||
| State | useReducer + Context | Zustand | Zustand is excellent but unnecessary at this scale. No async state, no complex selectors needed. Adding a dependency for something React itself handles cleanly is not justified. |
|
||||
|
||||
---
|
||||
|
||||
## Recommended shadcn/ui Addition
|
||||
|
||||
**Use shadcn/ui for form components.** shadcn/ui is not a dependency — it is a code generator. Running `npx shadcn-ui@latest add button input select checkbox` copies accessible, Tailwind-styled components into your project. These components are owned by the project (not a node_module) and fully customizable. For a wizard with many form inputs, this provides:
|
||||
|
||||
- Accessible labels, focus states, error message patterns out of the box
|
||||
- Consistent visual design without a custom design system
|
||||
- Radix UI primitives under the hood (keyboard navigation, ARIA) at no extra runtime cost
|
||||
|
||||
This is the current 2025 best practice for React + Tailwind projects.
|
||||
| Component library | None (keep semantic HTML + Tailwind) | Material Tailwind v3 | Material Tailwind is React + Tailwind but: (a) requires rewriting all existing components, (b) v3 is still in pre-order/beta, (c) adds ~50KB+ bundle weight for components the wizard doesn't need. Not worth the rewrite cost for 6-8 component types. |
|
||||
| Component library | None | MUI (Material UI) | MUI uses Emotion CSS-in-JS, fundamentally conflicts with Tailwind. Would require ripping out Tailwind entirely. Wrong direction. |
|
||||
| Component library | None | shadcn/ui | Good library but opinionated toward Radix primitives. Adding it now means learning a new component API while also implementing MD3 tokens. For v1.2 scope (cards, inputs, buttons, toggles), hand-authored Tailwind components are faster and simpler. |
|
||||
| Color generation | `@material/material-color-utilities` (dev-only) | `m3-tailwind-colors` npm package | Only 3 GitHub stars, single maintainer, uncertain maintenance. The underlying `@material/material-color-utilities` is the official Google package -- better to use it directly with a small script than depend on a wrapper. |
|
||||
| Color generation | Build-time script | Runtime `@material/material-color-utilities` in bundle | Adds ~30KB to the client bundle for something that only needs to run once per accent color change. Pre-generate at build time instead. |
|
||||
| Dark mode toggle | Custom 15-line hook | next-themes | next-themes is designed for Next.js SSR hydration edge cases. For a Vite SPA, it's unnecessary complexity. The core logic is `classList.toggle('dark')` + `localStorage`. |
|
||||
| Dark mode approach | Class-based (`@custom-variant`) | Media query (prefers-color-scheme only) | Media query approach doesn't allow manual toggle. Users expect a toggle button. Class-based supports both: system preference as default, manual override via toggle. |
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Scaffold
|
||||
npm create vite@latest ready2blob -- --template react-ts
|
||||
cd ready2blob
|
||||
# Dev dependency only -- NOT bundled into the app
|
||||
npm install -D @material/material-color-utilities
|
||||
|
||||
# Tailwind CSS
|
||||
npm install -D tailwindcss postcss autoprefixer
|
||||
npx tailwindcss init -p
|
||||
|
||||
# Forms and validation
|
||||
npm install react-hook-form zod @hookform/resolvers
|
||||
|
||||
# ZIP generation
|
||||
npm install jszip
|
||||
|
||||
# shadcn/ui setup (optional but recommended)
|
||||
npx shadcn-ui@latest init
|
||||
# Then add components as needed:
|
||||
npx shadcn-ui@latest add button input select checkbox label
|
||||
# That's it. No other new dependencies.
|
||||
```
|
||||
|
||||
### Files to Create
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `scripts/generate-theme.ts` | Node script: seed color -> MD3 CSS tokens |
|
||||
| `src/theme-tokens.css` | Generated output: CSS custom properties for light + dark |
|
||||
| `src/hooks/useTheme.ts` | Theme toggle hook (light/dark/system) |
|
||||
|
||||
### Files to Modify
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `src/index.css` | Add `@import "./theme-tokens.css"`, `@custom-variant dark`, `@theme` block |
|
||||
| `package.json` | Add script: `"generate-theme": "tsx scripts/generate-theme.ts"` |
|
||||
|
||||
---
|
||||
|
||||
## What NOT to Use
|
||||
## What NOT to Add
|
||||
|
||||
| Technology | Why Not |
|
||||
|------------|---------|
|
||||
| Next.js | Server framework. Adds SSR/SSG complexity with zero benefit for a pure client-side tool. |
|
||||
| Create React App | Officially deprecated. Abandoned by React team. |
|
||||
| Redux / Redux Toolkit | Overkill for wizard state. useReducer + Context is sufficient. |
|
||||
| Formik | Superseded by react-hook-form. Controlled inputs cause unnecessary re-renders. |
|
||||
| Angular | Enterprise framework, large bundle, steep learning curve, wrong tool for a simple wizard. |
|
||||
| Backend of any kind | Explicitly out of scope. All generation is string manipulation in the browser. |
|
||||
| LocalStorage / IndexedDB | Out of scope per PROJECT.md — no persistence. |
|
||||
| Material Tailwind / MUI / any component library | Rewrite cost exceeds benefit. Keep semantic HTML + Tailwind utilities with MD3 tokens. |
|
||||
| CSS-in-JS (Emotion, styled-components) | Conflicts with Tailwind. Wrong direction. |
|
||||
| next-themes | Next.js-specific. Vite SPA needs 15 lines of code, not a library. |
|
||||
| framer-motion | MD3 motion is subtle transitions (opacity, scale). CSS transitions + Tailwind's `@theme` animation tokens handle it. |
|
||||
| PostCSS plugins | Tailwind v4 uses the `@tailwindcss/vite` plugin, not PostCSS. Don't add PostCSS config. |
|
||||
| tailwind.config.js | Tailwind v4 is CSS-first. All config goes in `src/index.css` via `@theme`. No JS config file. |
|
||||
| Runtime color generation in browser | Pre-generate tokens at build time. Don't ship the color algorithm to users. |
|
||||
|
||||
---
|
||||
|
||||
@@ -120,24 +245,26 @@ npx shadcn-ui@latest add button input select checkbox label
|
||||
|
||||
| Decision | Confidence | Basis |
|
||||
|----------|------------|-------|
|
||||
| React 18 + Vite as core | HIGH | Industry-standard since 2023, no credible challenger for this use case |
|
||||
| TypeScript | HIGH | Unambiguously correct for generated-output correctness |
|
||||
| react-hook-form + Zod | HIGH | De-facto standard pairing for React forms as of 2024-2025 |
|
||||
| Tailwind CSS | HIGH | Dominant utility-CSS framework; strong fit for wizard UI |
|
||||
| shadcn/ui | MEDIUM | Strong community adoption but version numbers evolve quickly; verify CLI syntax |
|
||||
| JSZip for ZIP | MEDIUM | Stable and widely used, but fflate is a valid modern alternative — verify latest version on npm |
|
||||
| Blob API for single-file download | HIGH | Native browser API, no version concern |
|
||||
| Version numbers (all) | LOW | Training data cutoff August 2025; must verify on npmjs.com before scaffolding |
|
||||
| Zero new runtime dependencies | HIGH | Existing Tailwind v4 handles everything; verified in official docs |
|
||||
| `@custom-variant dark` for dark mode | HIGH | Verified in official Tailwind v4 dark mode documentation |
|
||||
| `@theme` directive for MD3 tokens | HIGH | Verified in official Tailwind v4 theme documentation |
|
||||
| `@material/material-color-utilities` 0.4.0 for token generation | HIGH | Official Google package, actively maintained, used by Material Theme Builder |
|
||||
| MD3 shape scale values (4/8/12/16/28/9999 px) | HIGH | Confirmed in official Material Design 3 shape documentation |
|
||||
| MD3 elevation box-shadow values | MEDIUM | Values sourced from community reference (Studio N Creations) cross-referenced with Material Web component source. Official docs don't publish exact CSS box-shadow -- they use `--md-elevation-level` in their web components. The shadow values are a reasonable approximation. |
|
||||
| No component library needed | HIGH | Project has 159 tests against current DOM structure; rewriting components is unjustified for a styling overhaul |
|
||||
| Pre-generated accent colors (not runtime) | MEDIUM | Architectural choice -- runtime generation is valid but adds bundle weight for a rarely-used feature |
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
- PROJECT.md: project requirements and constraints (pure frontend, no backend, static hosting)
|
||||
- React documentation (react.dev) — training data, verify current version
|
||||
- Vite documentation (vitejs.dev) — training data, verify current version
|
||||
- react-hook-form documentation (react-hook-form.com) — training data, verify current version
|
||||
- Zod documentation (zod.dev) — training data, verify current version
|
||||
- JSZip (stuk.github.io/jszip) — training data, verify current version
|
||||
- shadcn/ui (ui.shadcn.com) — training data, verify CLI commands
|
||||
- MDN Web Docs: Blob API, URL.createObjectURL — browser built-in, no version concern
|
||||
- [Tailwind v4 Dark Mode Documentation](https://tailwindcss.com/docs/dark-mode) -- `@custom-variant` syntax, class-based toggle, localStorage pattern
|
||||
- [Tailwind v4 Theme Documentation](https://tailwindcss.com/docs/theme) -- `@theme` directive, CSS variable generation, namespace conventions
|
||||
- [Material Design 3 Design Tokens](https://m3.material.io/foundations/design-tokens) -- token naming, semantic color roles
|
||||
- [Material Design 3 Shape Scale](https://m3.material.io/styles/shape/corner-radius-scale) -- corner radius values (4/8/12/16/28dp)
|
||||
- [Material Design 3 Elevation](https://m3.material.io/styles/elevation/applying-elevation) -- elevation levels 0-5
|
||||
- [@material/material-color-utilities on npm](https://www.npmjs.com/package/@material/material-color-utilities) -- v0.4.0, official Google color algorithm
|
||||
- [Material Theme Builder](https://material-foundation.github.io/material-theme-builder/) -- CSS export format, `--md-sys-color-*` naming convention
|
||||
- [MD3 Box-Shadow CSS Values](https://studioncreations.com/blog/material-design-3-box-shadow-css-values/) -- elevation shadow approximations (MEDIUM confidence)
|
||||
- [m3-tailwind-colors GitHub](https://github.com/somteacodes/m3-tailwind-colors) -- evaluated and rejected (3 stars, single maintainer)
|
||||
- [Tailwind v4 Multi-Theme Strategy](https://simonswiss.com/posts/tailwind-v4-multi-theme) -- community pattern for theme switching with CSS variables
|
||||
|
||||
+112
-155
@@ -1,219 +1,176 @@
|
||||
# Project Research Summary
|
||||
|
||||
**Project:** Ready2Blob
|
||||
**Domain:** Pure-frontend rclone configuration wizard / enterprise Windows deployment helper
|
||||
**Researched:** 2026-03-26
|
||||
**Confidence:** MEDIUM-HIGH (stack HIGH, pitfalls HIGH, features MEDIUM, architecture MEDIUM)
|
||||
**Project:** Ready2Blob v1.2 -- UI Polish & MD3 Overhaul
|
||||
**Domain:** Material Design 3 theming layer on existing React + Tailwind v4 wizard app
|
||||
**Researched:** 2026-03-31
|
||||
**Confidence:** HIGH
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Ready2Blob is a client-side-only static web application that guides IT administrators through configuring rclone for cloud storage backends (Azure Blob, S3, OneDrive, SFTP) and generating PowerShell deployment scripts for Intune and RMM platforms. The product has no backend, no persistence, and no server: all credential handling, config generation, and file download happen entirely in the browser. The right technology choices are well-established — React + Vite + TypeScript with react-hook-form/Zod for per-step validation, and native Blob/JSZip APIs for file generation. This is a greenfield SPA with a small, stable dependency set and no novel architecture challenges.
|
||||
Ready2Blob v1.2 is a pure styling and UX content overhaul of an already-functional 4-step rclone configuration wizard. The existing stack (Vite 6, React 18, TypeScript 5, Tailwind v4, react-hook-form 7, Zod 4, 159 passing tests) is solid and does not need architectural changes. The research unanimously recommends a **zero new runtime dependencies** approach: define Material Design 3 color tokens as CSS custom properties, wire them into Tailwind v4's native `@theme` directive, and build a small set of reusable UI primitives (Button, Input, Card, Select) that replace the current scattered inline markup. The only new dependency is `@material/material-color-utilities` as a dev-only tool for generating MD3 color palettes from a seed color -- it never ships to the browser.
|
||||
|
||||
The recommended approach is to build from the inside out: define types and schemas first, build pure generator functions (config builder, PS script builder) second, then add the wizard UI shell on top. This order ensures the most critical output — the generated files — is correct and testable before any UI work begins. The Backend Schema Registry pattern (one static TS object describing all backend field definitions) is the architectural keystone: it decouples form rendering from backend-specific knowledge and makes adding new backends trivial without modifying UI components.
|
||||
The recommended approach is a bottom-up, layer-by-layer migration. First establish the CSS token foundation and dark mode infrastructure (zero component changes, zero test impact). Then extract UI primitives as new additive components. Then swap existing hardcoded colors for semantic tokens one component at a time, running all 159 tests after each change. This ordering is critical because the token system is the foundation for everything else -- dark mode, accent colors, MD3 components, and responsive improvements all depend on it. Content improvements (app intro, step descriptions, remote name clarity) are independent and can be parallelized.
|
||||
|
||||
The single largest risk category is PowerShell deployment correctness, not frontend development. Generated scripts must handle SYSTEM-context path resolution, UTF-8 no-BOM encoding, single-quoted here-strings to prevent `$` interpolation, 32-bit vs 64-bit host differences, and Intune's 200 KB script size limit. These are operational correctness requirements that will not surface during local development — they only manifest in real enterprise Intune or RMM environments. Every script template decision must be made with these constraints in mind from the first line of code.
|
||||
|
||||
---
|
||||
The primary risks are: breaking the 131 test selectors across 5 UI test files during component restyling, dark mode contrast failures (WCAG AA), and flash of unstyled content on dark mode load. All three are preventable with the disciplined layer-by-layer approach. The most dangerous anti-pattern is a big-bang restyle where all components are changed at once -- this makes test failures impossible to isolate and virtually guarantees regressions.
|
||||
|
||||
## Key Findings
|
||||
|
||||
### Recommended Stack
|
||||
|
||||
The stack is lean by design. React 18 + Vite 5 + TypeScript 5 provides the scaffold; Tailwind CSS 3 + shadcn/ui handles styling and accessible form primitives without a maintained design system; react-hook-form 7 + Zod 3 handles per-step validation with schema-driven field definitions that map directly to rclone backend specs. File generation uses only browser-native APIs (Blob, URL.createObjectURL) for single-file downloads and JSZip 3 for ZIP bundles. No backend, no external state library, no database.
|
||||
Zero new runtime dependencies. The existing Tailwind v4 handles all styling needs through its CSS-first `@theme` directive and `@custom-variant` for dark mode. One dev dependency added: `@material/material-color-utilities` (v0.4.0) to generate MD3 palettes at build time via a Node script. The output is static CSS custom properties -- zero bundle impact.
|
||||
|
||||
**Core technologies:**
|
||||
- React 18 + Vite 5: SPA scaffold — fast HMR, zero-config static build, no SSR overhead
|
||||
- TypeScript 5: type safety for generated-output correctness — wrong field name = broken config
|
||||
- react-hook-form 7 + Zod 3: per-step validation + backend schema definitions — uncontrolled inputs, schema-driven, performant
|
||||
- Tailwind CSS 3 + shadcn/ui: utility styling + accessible form components — no design system maintenance
|
||||
- Native Blob API: single-file download — zero dependency, browser built-in
|
||||
- JSZip 3: ZIP bundle of all artifacts — de-facto browser ZIP standard
|
||||
- React useReducer + Context: wizard state — sufficient at this scale, no Zustand/Redux needed
|
||||
**Core technologies (all existing, no changes):**
|
||||
- **Tailwind v4 `@theme`**: Maps MD3 tokens to utility classes (`bg-surface`, `text-on-primary`) -- native, no plugins
|
||||
- **Tailwind v4 `@custom-variant dark`**: Class-based dark mode toggle replacing the removed `darkMode: 'class'` config
|
||||
- **CSS custom properties**: ~20 semantic MD3 color roles, elevation shadows, shape radii, motion tokens
|
||||
|
||||
**Critical exclusions:** No Next.js (SSR overhead irrelevant), no Create React App (deprecated), no localStorage/sessionStorage (credentials must not persist), no backend of any kind.
|
||||
**New dev dependency only:**
|
||||
- **`@material/material-color-utilities` 0.4.0**: Official Google library, generates full MD3 palette from a single seed hex color. Used by a build script (`scripts/generate-theme.ts`) that outputs `src/theme-tokens.css`. NOT bundled.
|
||||
|
||||
**Explicitly rejected:** MUI, Material Tailwind, shadcn/ui, CSS-in-JS, next-themes, framer-motion, PostCSS plugins, tailwind.config.js, runtime color generation. See STACK.md for detailed rationale on each.
|
||||
|
||||
### Expected Features
|
||||
|
||||
IT admins evaluating this tool will immediately abandon it if any table-stakes feature is missing. The MVP must cover the full generation pipeline — backend selection through file download — for at least Azure Blob, S3, and OneDrive before any polish work begins.
|
||||
**Must have (table stakes -- P1):**
|
||||
- MD3 color token system (CSS custom properties for all color roles)
|
||||
- MD3 text fields (outlined variant with floating labels)
|
||||
- MD3 button hierarchy (filled, outlined, text)
|
||||
- MD3 card components with elevation
|
||||
- Dark mode toggle (system/light/dark, localStorage persistence)
|
||||
- MD3 step indicator (numbered circles, connecting lines, state indicators)
|
||||
- Responsive layout (mobile-friendly grids, collapsible step indicator)
|
||||
- App intro/landing section explaining what Ready2Blob does
|
||||
- Step-level descriptions on each wizard step
|
||||
- Remote name field clarity (prominent explanation, examples)
|
||||
- Accessible focus states (focus-visible, 3px outline)
|
||||
- Tech debt: FieldRenderer aria fix, StepIndicator inline style migration
|
||||
|
||||
**Must have (table stakes):**
|
||||
- Multi-step backend selection wizard with popular backends (Azure Blob, S3, OneDrive, SFTP) shown first
|
||||
- Per-backend field forms with labels, help text, and required-field validation matching rclone's own config flow
|
||||
- Valid rclone.conf output (INI format, correct key/value pairs per backend)
|
||||
- Remote name input with character validation (alphanumeric, dash, underscore only)
|
||||
- Intune PowerShell deployment script (handles SYSTEM context, idempotent, correct exit codes)
|
||||
- RMM PowerShell deployment script (self-contained, runs as SYSTEM)
|
||||
- Optional rclone install toggle (script downloads rclone binary from URL — never embeds it)
|
||||
- Security warning gate before download (credentials are plaintext — must be acknowledged)
|
||||
- Individual download buttons per artifact (conf, Intune script, RMM script)
|
||||
- Prominent "no data sent to server" assurance
|
||||
**Should have (differentiators -- P2, add after core is stable):**
|
||||
- User-selectable accent color (5-8 curated presets)
|
||||
- Animated step transitions (CSS fade/slide, 200ms)
|
||||
- Upgraded contextual help popovers
|
||||
- Scroll-to-error on validation failure
|
||||
|
||||
**Should have (differentiators):**
|
||||
- Live config preview (real-time generated file content visible before downloading)
|
||||
- Intune detection script generation (separate from install script — Intune Win32 requirement)
|
||||
- Config path selector: machine-wide `C:\ProgramData\rclone\` vs user profile (with explanation of SYSTEM context implications)
|
||||
- Rclone version pinning input (defaults to `rclone-current`; override for reproducible deployments)
|
||||
- Copy-to-clipboard for all output blocks (RMM tools often have a "run script" field)
|
||||
- Field-level validation for backend-specific formats (Azure account name: 3-24 lowercase alphanumeric)
|
||||
- Explanatory tooltips on credential fields (SAS token vs access key confusion is common)
|
||||
- ZIP "download all" bundle
|
||||
|
||||
**Defer to v2+:**
|
||||
- Multiple remotes in one config (adds significant wizard UX complexity)
|
||||
- RMM-named script variants (NinjaRMM-specific, Datto-specific execution contexts)
|
||||
- IntuneWinAppUtil packaging hints (valuable but can be a static docs page)
|
||||
- Test connection / credential validation (requires a proxy backend — violates no-server constraint)
|
||||
- Save/load configurations (requires localStorage = credentials in browser storage = security incident)
|
||||
- Graph API auto-push to Intune (massive scope; manual upload is acceptable for v1)
|
||||
- Multi-OS support (macOS/Linux scripts out of scope; state clearly "Windows endpoints only")
|
||||
**Defer (v2+):**
|
||||
- Custom MD3 select/dropdown (HIGH complexity for 2-3 selects)
|
||||
- Code syntax highlighting in review step
|
||||
- Arbitrary user hex color theming
|
||||
|
||||
### Architecture Approach
|
||||
|
||||
The architecture follows four clean layers: Wizard UI (React components, navigation), Wizard State (single useReducer store, shared via Context), Config Builders (pure functions: state in, file string out), and Download Manager (Blob URL or JSZip). The Backend Schema Registry is a static TypeScript object that defines all field definitions per backend type — it drives dynamic form rendering, Zod schema construction, and config key/value generation from one source of truth. All builders are pure functions with no side effects, making them immediately unit-testable without a browser.
|
||||
A CSS custom properties layer bridges MD3 tokens with Tailwind v4. A thin `ThemeToggle` component manages the `.dark` class on `<html>` via direct DOM manipulation -- NOT via React Context, to avoid re-rendering the entire wizard tree on toggle. Components are refactored bottom-up: primitives first (Button, Input, Card, Select), then composed components (FieldRenderer, BackendCard), then layout (AppShell, StepIndicator). The critical architectural insight is that theme state belongs in CSS (class on `<html>` + custom properties), not in React state.
|
||||
|
||||
**Major components:**
|
||||
1. Wizard UI + Navigation — step rendering, next/back/jump, step completion tracking
|
||||
2. Wizard State (useReducer + Context) — single source of truth for all form data; typed WizardState interface
|
||||
3. Backend Schema Registry — static TS object: BackendType → FieldDef[]; drives dynamic forms
|
||||
4. rclone.conf Builder — pure function: WizardState → INI string; plain template literals
|
||||
5. PowerShell Script Builder (Intune + RMM variants) — pure functions: WizardState → .ps1 string
|
||||
6. Download Manager — Blob URL (single file) + JSZip (bundle); no library for single files
|
||||
|
||||
**Key patterns:**
|
||||
- Centralized state — all form data in one typed store; never per-step local state
|
||||
- Schema Registry — one entry per backend = new backend support with zero UI changes
|
||||
- Pure builders — same input always produces same output; no DOM reads; fully unit-testable
|
||||
- PowerShell single-quoted here-strings — prevents `$` interpolation corrupting credentials
|
||||
1. **CSS Token Layer** (`index.css` + `theme-tokens.css`) -- MD3 color roles, elevation, shape, motion as `@theme` values
|
||||
2. **UI Primitives** (`ui/Button`, `ui/Input`, `ui/Select`, `ui/Card`) -- MD3-styled presentational components using `forwardRef` for react-hook-form compatibility
|
||||
3. **ThemeToggle** -- standalone component, local state only, toggles `.dark` class on `<html>`
|
||||
4. **AppShell** -- layout wrapper extracted from current WizardShell, houses ThemeToggle
|
||||
5. **Modified existing components** -- FieldRenderer, BackendCard, PasswordField, all wizard steps -- swap hardcoded colors for semantic tokens
|
||||
|
||||
### Critical Pitfalls
|
||||
|
||||
These are the failure modes that cause silent deployment breakage in production enterprise environments. All five must be addressed in the initial script template — retrofitting them later risks shipping broken scripts.
|
||||
|
||||
1. **SYSTEM context config path mismatch** — rclone config written to SYSTEM's `%APPDATA%` is invisible to the logged-in user. Always write to `C:\ProgramData\rclone\rclone.conf` (machine-wide path, no WOW64 redirection). Make the destination path explicit in the wizard and let the admin choose; never default to bare `%APPDATA%` expansion.
|
||||
|
||||
2. **PowerShell encoding writes UTF-16 BOM** — `Out-File` in PS 5.1 defaults to UTF-16 LE with BOM; rclone cannot parse it. Always use `[System.IO.File]::WriteAllText($path, $content, [System.Text.UTF8Encoding]::new($false))` to guarantee UTF-8 no-BOM. Hardcode this in every script template from day one.
|
||||
|
||||
3. **Secrets exposed in Intune script logs** — Intune logs all script output to the Azure portal. Generated scripts must never echo credential values. Use `Write-Host "Writing config to $configPath"` (path only), never `Write-Host $configContent`. No debug output containing credential variables, ever.
|
||||
|
||||
4. **Intune 200 KB script size limit** — Scripts that embed or base64-encode rclone binary content are rejected at upload. The generated script must always download rclone from a URL (`Invoke-WebRequest`) at deployment time — never embed the binary. Surface the download URL in the wizard and let admins specify a corporate mirror.
|
||||
|
||||
5. **SAS token / storage key corruption via `$` interpolation** — Azure credentials contain `$` characters. In double-quoted PowerShell strings, `$` triggers variable substitution, silently corrupting the credential. Always wrap the config content block in a single-quoted here-string (`@' ... '@`). Trim all credential inputs before inserting into the config to prevent newline truncation.
|
||||
|
||||
**Additional moderate pitfalls to address in script templates:**
|
||||
- OAuth backends (OneDrive, Google Drive) require a pre-obtained token from `rclone authorize` — the wizard cannot complete OAuth flow in the browser; show a prominent warning and a dedicated token input field
|
||||
- 32-bit PowerShell host (Intune default) causes WOW64 path redirection; use `C:\ProgramData\rclone\` or `$env:ProgramW6432` — recommend enabling 64-bit PS in Intune settings
|
||||
- Do not call `Set-ExecutionPolicy` in the generated script — Group Policy always overrides it; use `-ExecutionPolicy Bypass` on any sub-process calls instead
|
||||
- Auto-insert a `# Generated: [timestamp]` comment in each script so re-generated scripts always have different bytes, enabling Intune re-execution on config updates
|
||||
|
||||
---
|
||||
1. **Tailwind v4 dark mode misconfiguration** -- v3 `darkMode: 'class'` does not exist. Must use `@custom-variant dark (&:where(.dark, .dark *))` in CSS. Address in Phase 1 before any `dark:` classes are added.
|
||||
2. **73 hardcoded color classes** -- Adding `dark:` counterparts to each creates unmaintainable 100+ char classNames. Instead, replace all with semantic tokens (`bg-surface`, `text-on-surface`) that swap values automatically via CSS variable override.
|
||||
3. **Breaking 131 test selectors** -- Restyling touches the same JSX that tests query. Must restyle one component at a time, running all 159 tests after each. Never batch-restyle.
|
||||
4. **FOUC on dark mode load** -- React applies `.dark` class after first paint. Add a synchronous inline `<script>` in `index.html <head>` to set the class before paint.
|
||||
5. **Dark mode contrast failures** -- `text-gray-500` on dark backgrounds fails WCAG AA. Semantic tokens must define separate light/dark values verified for 4.5:1 contrast on every pair.
|
||||
6. **Theme context re-renders** -- Do NOT use React Context for theme. CSS class toggle on `<html>` causes zero React re-renders. Only the toggle button needs local state.
|
||||
|
||||
## Implications for Roadmap
|
||||
|
||||
Based on combined research, the architecture's inside-out build order maps directly to phases. The generator functions and schema registry have zero UI dependencies — build them first, test them in isolation, then layer UI on top. This order also front-loads the hardest correctness requirements (script encoding, path choices, credential handling) before any deployment to users.
|
||||
Based on research, suggested phase structure:
|
||||
|
||||
### Phase 1: Foundation — Types, Schema Registry, State Shape
|
||||
### Phase 1: Theme Foundation
|
||||
**Rationale:** The token system is the dependency root -- every visual component, dark mode, and accent colors depend on it. Must come first. Zero test impact makes it safe.
|
||||
**Delivers:** MD3 color tokens in CSS, dark mode infrastructure (`@custom-variant`, `.dark` overrides), FOUC prevention script in `index.html`, ThemeToggle component, theme generation script (`scripts/generate-theme.ts`)
|
||||
**Addresses:** Color token system, dark mode toggle (infrastructure)
|
||||
**Avoids:** Tailwind v4 dark mode misconfiguration (P1), FOUC (P5), theme context re-renders (P6)
|
||||
|
||||
**Rationale:** All other components depend on these definitions. Building them first prevents architectural drift where UI components hardcode backend knowledge. The schema registry is the keystone — it must exist before dynamic forms, before builders, before anything.
|
||||
**Delivers:** TypeScript type definitions (WizardState, BackendType, FieldDef, RemoteConfig), Backend Schema Registry (Azure Blob, S3, OneDrive, SFTP as Tier 1; GCS, Backblaze B2 as Tier 2), Zod schemas per backend derived from registry, Vite + React + TypeScript + Tailwind scaffold
|
||||
**Addresses:** Foundational architecture (ARCHITECTURE.md Pattern 2 — Backend Schema Registry)
|
||||
**Avoids:** Anti-Pattern 3 (hardcoding backend fields in step components)
|
||||
### Phase 2: UI Primitives
|
||||
**Rationale:** Primitives are the reuse boundary -- Input, Button, Card are used by multiple steps. Building them before modifying existing components means additive-only changes with zero existing test impact.
|
||||
**Delivers:** `ui/Button` (filled/outlined/text), `ui/Input` (outlined, floating label, error), `ui/Select`, `ui/Card` (elevation), new component tests
|
||||
**Addresses:** MD3 text fields, MD3 buttons, MD3 cards
|
||||
**Avoids:** Big-bang rewrite anti-pattern (P4)
|
||||
|
||||
### Phase 2: Core Generators — rclone.conf + PowerShell Script Builders
|
||||
### Phase 3: Component Token Migration
|
||||
**Rationale:** With tokens and primitives in place, swap hardcoded colors across all existing components. This is the highest-risk phase for test breakage -- one component at a time, tests after each.
|
||||
**Delivers:** All components using semantic tokens, FieldRenderer delegating to primitives, BackendCard using Card, PasswordField using Input, StepIndicator inline style migration, FieldRenderer aria-describedby fix
|
||||
**Addresses:** Hardcoded color migration (73 usages), tech debt items, form accessibility, MD3 step indicator
|
||||
**Avoids:** Hardcoded color dual-track (P2), test breakage (P4), accessibility regressions (P7)
|
||||
|
||||
**Rationale:** Pure functions with no UI dependencies. Build and unit-test these before any React work. This is where all the correctness requirements from PITFALLS.md live — encoding, path choices, credential handling, no-log rules must be baked in from the first line of the template.
|
||||
**Delivers:** `buildRcloneConf(remotes)` — pure function producing INI string with LF normalization; `buildIntuneScript(state)` — PS script with UTF-8 no-BOM write, single-quoted here-string, machine-wide path, download-only rclone install, timestamp comment, no credential logging; `buildRmmScript(state)` — same constraints; Download Manager (Blob URL + JSZip wrapper)
|
||||
**Implements:** ARCHITECTURE.md Patterns 3, 4, 5 (pure builders, Blob URL, JSZip)
|
||||
**Avoids:** Pitfalls 1, 2, 3, 5, 8 (SYSTEM path, 200 KB limit, encoding, credential corruption, OAuth warning)
|
||||
### Phase 4: Layout, Content, and Responsive
|
||||
**Rationale:** With all components styled, add the layout shell, content improvements, and responsive behavior. These are mostly independent of each other and can be parallelized.
|
||||
**Delivers:** AppShell layout, app intro section, step descriptions, remote name clarity, responsive grid/breakpoints, dark mode UX polish (scrollbars, select dropdowns, focus rings)
|
||||
**Addresses:** App intro, step descriptions, remote name clarity, responsive layout, accessible focus states
|
||||
**Avoids:** Dark mode contrast failures (P3) -- final contrast audit here
|
||||
|
||||
### Phase 3: Wizard Shell + State Wiring
|
||||
|
||||
**Rationale:** Navigation shell and state store can be built against mock/empty step content. Getting the state shape right before forms are built prevents having to refactor form registration later.
|
||||
**Delivers:** useReducer + Context store wired to WizardState shape; step navigation (stepper, next/back, URL-free step tracking); step completion state; mobile-responsive layout shell using Tailwind + shadcn/ui
|
||||
**Uses:** React 18, useReducer/Context, Tailwind, shadcn/ui
|
||||
**Avoids:** Anti-Pattern 1 (per-step local state losing data on back-navigation)
|
||||
|
||||
### Phase 4: Wizard Step Forms — Dynamic Backend Forms + Deployment Options
|
||||
|
||||
**Rationale:** Now that state, schema registry, and builders all exist, forms can be built as thin wrappers that write to the state store. The dynamic form component reads from the schema registry — one component handles all backends.
|
||||
**Delivers:** Step 1 — Backend type selector (popularity-ordered: Azure Blob, S3, OneDrive, SFTP first); Step 2 — `<DynamicBackendStep />` driven by Schema Registry (react-hook-form + Zod per backend); Step 3 — Deployment options (rclone install toggle, config path selector: machine-wide vs user profile, version pin field, script target selection); Remote name input with `[a-zA-Z0-9_-]` validation and live section header preview
|
||||
**Addresses:** Table-stakes features from FEATURES.md; Pitfall 4 (remote name validation); Pitfall 8 (OAuth token field + warning for OAuth backends)
|
||||
|
||||
### Phase 5: Review, Download + Security Gate
|
||||
|
||||
**Rationale:** Final wizard step assembles everything. Live preview builds trust and catches errors before the admin deploys a broken config. Security warning is a hard blocker before download.
|
||||
**Delivers:** Review step with live syntax-highlighted config preview (updates in real time); security warning modal (credentials in plaintext — cannot be dismissed without acknowledgment); individual download buttons (conf, Intune .ps1, RMM .ps1); "Download All as ZIP" via JSZip; copy-to-clipboard for each output block
|
||||
**Addresses:** Table-stakes and differentiator features from FEATURES.md (live preview, security gate, copy-to-clipboard, ZIP bundle)
|
||||
**Avoids:** Pitfall 5 (security warning gate)
|
||||
|
||||
### Phase 6: Polish + Correctness Hardening
|
||||
|
||||
**Rationale:** After end-to-end flow works, add the depth features that reduce support tickets and build admin trust. This phase also adds per-field validation beyond basic required-field checks and the contextual help text that reduces abandonment.
|
||||
**Delivers:** Per-field backend-specific validation (Azure account name format, SAS token trimming); explanatory tooltips on credential fields; Intune detection script generation (separate from install script); config path implications documentation in the wizard output pane; 64-bit PS host recommendation in Intune output; auto-inserted `# Generated: [timestamp]` comment in scripts; LF normalization on all generated output
|
||||
**Addresses:** Differentiator features from FEATURES.md; Pitfalls 6, 7, 9, 10, 11, 13
|
||||
### Phase 5: Polish and Differentiators
|
||||
**Rationale:** Accent colors and transitions layer on top of the stable token system. These are P2 features that should only land after core is validated.
|
||||
**Delivers:** Accent color selector (5-8 presets), step transitions (CSS), upgraded popovers, scroll-to-error
|
||||
**Addresses:** All P2 differentiator features
|
||||
**Avoids:** Premature accent color work before token system is proven
|
||||
|
||||
### Phase Ordering Rationale
|
||||
|
||||
- Phases 1 and 2 have no UI dependencies and contain the highest-risk correctness requirements — building them first allows unit testing before any user-facing code exists
|
||||
- The wizard shell (Phase 3) can be built with empty/mock steps while the generators are being built, if team size allows parallelism
|
||||
- Dynamic forms (Phase 4) depend on both the schema registry (Phase 1) and state wiring (Phase 3) being finalized
|
||||
- The download step (Phase 5) can only be meaningfully built once all builders (Phase 2) exist
|
||||
- Polish (Phase 6) is deliberately last — it adds depth but does not change the architecture
|
||||
- **Token system first** because 100% of visual components depend on it (see FEATURES.md dependency graph)
|
||||
- **Primitives before migration** because building new components is additive (zero test risk), while modifying existing components carries test risk
|
||||
- **One-component-at-a-time migration** because the 131 test selectors across 5 files make batch changes dangerous
|
||||
- **Content and layout after components** because layout depends on component dimensions and spacing, and content is independent
|
||||
- **Differentiators last** because accent colors multiply token sets (N accents x 2 themes) and must not be attempted until the base system is stable
|
||||
|
||||
### Research Flags
|
||||
|
||||
Phases likely needing deeper research during planning:
|
||||
|
||||
- **Phase 2 (Script Builders):** Verify current Intune PowerShell script execution model against live Microsoft docs before writing templates. Intune behavior around 32-bit/64-bit host defaults and script re-execution triggers can change between Intune releases. High-stakes: wrong behavior is invisible until tested on a real managed device.
|
||||
- **Phase 4 (Backend Forms):** Verify exact required field names for each backend against live rclone docs (rclone.org/azureblob, rclone.org/s3, etc.) before implementing the Schema Registry. Field names are the source of truth for config generation — a wrong key name produces a silently broken config.
|
||||
- **Phase 4 (OAuth backends):** Confirm current `rclone authorize` token extraction flow for OneDrive before designing the OAuth token input UX. The token JSON structure may have changed.
|
||||
- **Phase 2 (UI Primitives):** MD3 outlined text field with floating label is the most complex primitive. CSS-only floating label animation needs prototyping. `forwardRef` integration with react-hook-form `register()` needs verification.
|
||||
- **Phase 3 (Component Migration):** The FieldRenderer refactor to delegate to primitives is the highest-risk change. DOM structure changes could break tests. Needs careful planning of the migration path per field type.
|
||||
|
||||
Phases with standard patterns (skip research-phase):
|
||||
|
||||
- **Phase 1 (Foundation):** React + Vite + TypeScript scaffold is fully documented; schema registry is a static TS object; no novel decisions required
|
||||
- **Phase 3 (Wizard Shell):** Multi-step form navigation with useReducer + Context is a well-documented React pattern; react-hook-form per-step validation is standard
|
||||
- **Phase 5 (Download):** Blob URL download and JSZip are stable browser APIs with no version concerns; security modal is standard UI
|
||||
|
||||
---
|
||||
- **Phase 1 (Theme Foundation):** Fully documented in Tailwind v4 official docs. Token structure is defined. FOUC script is a known 5-line pattern.
|
||||
- **Phase 4 (Layout/Content):** Content writing and responsive Tailwind grids are standard work. No novel patterns.
|
||||
- **Phase 5 (Polish):** Accent colors are a token swap. Step transitions are CSS-only. Well-documented patterns.
|
||||
|
||||
## Confidence Assessment
|
||||
|
||||
| Area | Confidence | Notes |
|
||||
|------|------------|-------|
|
||||
| Stack | HIGH | React + Vite + TypeScript + react-hook-form + Zod is the industry-standard 2025 pairing; no credible alternatives for this use case. shadcn/ui CLI syntax should be verified before scaffolding. |
|
||||
| Features | MEDIUM-HIGH | Table-stakes and IT admin deployment expectations are HIGH confidence from domain knowledge. Specific rclone backend field names are MEDIUM — must be cross-checked against live rclone docs before implementing schema registry. |
|
||||
| Architecture | MEDIUM-HIGH | rclone.conf format is HIGH (stable since v1.x). Intune script patterns are MEDIUM (well-documented but behavior can change between releases). Blob/JSZip download patterns are HIGH (stable browser APIs). |
|
||||
| Pitfalls | HIGH | SYSTEM context, encoding, execution policy, and size-limit pitfalls are verified against official Microsoft docs. rclone-specific pitfalls (section name validation, config format edge cases) are MEDIUM — verify against live rclone docs. |
|
||||
| Stack | HIGH | Zero new runtime deps. Tailwind v4 `@theme` and `@custom-variant` verified in official docs. `@material/material-color-utilities` is official Google package. |
|
||||
| Features | MEDIUM | Table stakes and differentiators well-identified. MD3 web implementation patterns less established than MUI-based approaches since we are hand-rolling with Tailwind. |
|
||||
| Architecture | HIGH | CSS custom properties + Tailwind v4 `@theme` is the documented approach. Bottom-up migration is low-risk. ThemeToggle via DOM class (not Context) avoids re-render cascade. |
|
||||
| Pitfalls | HIGH | Based on direct codebase analysis (73 classNames, 131 selectors, 3 inline styles). Tailwind v4 dark mode gotchas verified against official docs and community reports. |
|
||||
|
||||
**Overall confidence:** MEDIUM-HIGH
|
||||
**Overall confidence:** HIGH
|
||||
|
||||
### Gaps to Address
|
||||
|
||||
- **rclone backend field names:** Every field key in the Schema Registry must match rclone's exact config key names. Verify each Tier 1 backend against rclone.org before implementing Phase 1. Wrong keys produce silently broken configs.
|
||||
- **rclone S3-compatible provider pattern:** The `provider = AWS` + optional `endpoint` field pattern for S3-compatible backends (Wasabi, MinIO, Cloudflare R2) should be confirmed against current rclone S3 docs before implementing the S3 schema entry.
|
||||
- **Intune re-execution trigger:** Confirm whether Intune re-runs a modified script based on byte-level content change or requires a script version increment. This affects the timestamp-comment strategy in Phase 6.
|
||||
- **OneDrive OAuth token format:** Confirm current `token = {...}` JSON structure for OneDrive to design the token input field correctly in Phase 4.
|
||||
- **shadcn/ui CLI commands:** Verify current `npx shadcn-ui@latest` command syntax before Phase 3 scaffolding — the CLI interface has historically changed between major versions.
|
||||
|
||||
---
|
||||
- **MD3 elevation box-shadow values**: Sourced from community reference, not official Google CSS. The exact shadow values are approximations. Validate visually during Phase 1 and adjust if needed.
|
||||
- **MD3 outlined text field floating label**: CSS-only implementation needs prototyping. May require a small JS hook for detecting input fill state (`:placeholder-shown` selector or `onFocus`/`onBlur` handlers). Validate during Phase 2 planning.
|
||||
- **Native `<select>` in dark mode**: Browser renders `<option>` with OS colors. May look broken on dark backgrounds in some browsers. Evaluate during Phase 4 whether to accept native rendering or build custom dropdown (deferred to v2+ per feature research).
|
||||
- **Pre-generated accent color token sets**: The approach of pre-generating N accent x 2 theme token sets at build time has not been prototyped. If the CSS file grows too large, runtime generation (bundling the color utility) may be reconsidered. Validate during Phase 5.
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- Microsoft Learn — PowerShell scripts in Intune (2025-10-02) — SYSTEM context, size limits, exit codes, script re-execution
|
||||
- Microsoft Learn — Intune Management Extension (2026-03-17) — execution context, 32-bit/64-bit host defaults
|
||||
- Microsoft Learn — Set-ExecutionPolicy reference (2025-04-15) — Group Policy override behavior
|
||||
- Microsoft Learn — Naming Files, Paths, and Namespaces (Win32) — MAX_PATH constraints
|
||||
- MDN Web Docs — Blob API, URL.createObjectURL — browser file download pattern
|
||||
- PROJECT.md — project requirements and constraints (no backend, no persistence, static hosting)
|
||||
- [Tailwind v4 Dark Mode Documentation](https://tailwindcss.com/docs/dark-mode) -- `@custom-variant` syntax, class-based toggle
|
||||
- [Tailwind v4 Theme Documentation](https://tailwindcss.com/docs/theme) -- `@theme` directive, CSS variable generation
|
||||
- [Material Design 3 Color Roles](https://m3.material.io/styles/color/roles) -- semantic color role definitions
|
||||
- [Material Design 3 Design Tokens](https://m3.material.io/foundations/design-tokens) -- token naming conventions
|
||||
- [Material Design 3 Shape Scale](https://m3.material.io/styles/shape/corner-radius-scale) -- radius values
|
||||
- [Material Design 3 Elevation](https://m3.material.io/styles/elevation/applying-elevation) -- elevation levels 0-5
|
||||
- [@material/material-color-utilities](https://www.npmjs.com/package/@material/material-color-utilities) -- v0.4.0, official Google package
|
||||
- Codebase analysis: 73 className usages, 131 test selectors, 159 tests, 3 inline styles in StepIndicator
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- rclone.org documentation — backend field names, config format, S3 provider pattern (training data, knowledge cutoff August 2025; verify before implementation)
|
||||
- rclone.conf INI format specification — training data (format is stable since v1.x; HIGH confidence on structure, MEDIUM on per-backend field names)
|
||||
- RMM deployment patterns (NinjaRMM, Datto, ConnectWise) — training data; verify execution context differences before adding RMM-named variants
|
||||
- [MD3 Box-Shadow CSS Values](https://studioncreations.com/blog/material-design-3-box-shadow-css-values/) -- elevation shadow approximations
|
||||
- [Tailwind v4 dark mode @custom-variant discussion](https://github.com/tailwindlabs/tailwindcss/discussions/15083) -- community verified pattern
|
||||
- [Tailwind v4 Multi-Theme Strategy](https://simonswiss.com/posts/tailwind-v4-multi-theme) -- community pattern for theme switching
|
||||
- [Material Theme Builder](https://material-foundation.github.io/material-theme-builder/) -- CSS export format reference
|
||||
- [Wizard Design Pattern (UX Planet)](https://uxplanet.org/wizard-design-pattern-8c86e14f2a38) -- wizard UX fundamentals
|
||||
- [Wizards (NN/g)](https://www.nngroup.com/articles/wizards/) -- Nielsen Norman Group guidelines
|
||||
- [Input Field Design Best Practices 2025](https://fireart.studio/blog/input-field-design-best-practice/) -- floating labels, error patterns
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- Version numbers for all npm packages — training data cutoff August 2025; verify all versions against npmjs.com before scaffolding
|
||||
- [m3-tailwind-colors](https://github.com/somteacodes/m3-tailwind-colors) -- evaluated and rejected (3 stars, single maintainer)
|
||||
|
||||
---
|
||||
*Research completed: 2026-03-26*
|
||||
*Research completed: 2026-03-31*
|
||||
*Ready for roadmap: yes*
|
||||
|
||||
Reference in New Issue
Block a user