Installable skillApache-2.0

Apply Theme Skill

Use when applying a brand theme configuration to the app. Reads theme_config.json and updates Dart theme files with new colors, gradients, logos, and animation effects.

BY David-Li0406UPDATED Oct 28, 2026
INTENDED FOR◈OpenClawInstall in a listed agent.

instructions.md

---
name: apply-theme-3d7fe97afac1
description: Use when applying a brand theme configuration to the app. Reads theme_config.json and updates Dart theme files with new colors, gradients, logos, and animation effects.
---

# Apply Theme Skill

Reads `assets/config/theme_config.json` generated by `/brand-theme` and applies it to the Flutter app's theme system.

## When to Use

- After running `/brand-theme` to generate a config
- User asks to "apply the theme", "update theme from config", or "implement the brand colors"
- User invokes `/apply-theme` directly

## Prerequisites

- `assets/config/theme_config.json` must exist (generated by `/brand-theme`)
- If it doesn't exist, instruct user to run `/brand-theme` first

## Process Overview

1. Read and validate `theme_config.json`
2. Update color definitions in `lib/core/theme/app_colors.dart`
3. Update gradients in `lib/core/theme/gradients.dart`
4. Update logo paths
5. Update app name, caption, and welcome message in `lib/core/constants/app_constants.dart`
6. Implement animation effects based on style preset
7. Verify the app builds successfully

## Step 1: Read Configuration

Read `assets/config/theme_config.json` and validate structure:

```json
{
  "brand": {
    "name": "...",
    "appName": { "light": "...", "dark": "..." },
    "caption": { "light": "...", "dark": "..." },
    "welcomeMessage": "...",
    "logo": { "light": "...", "dark": "..." }
  },
  "colors": { "primary": "#...", "secondary": "#...", "accent": "#..." },
  "animation": { "style": "neon|cosmic|minimal|professional|playful" },
  "modes": { "default": "dark|light|system" }
}
```

**Validation checks:**
- Primary color is valid hex
- Logo paths are provided
- Animation style is one of the valid presets
- App name and caption are provided (or default to brand.name)

## Step 2: Update app_colors.dart

File: `lib/core/theme/app_colors.dart`

### Color Mapping

Map config colors to the app's color system. Generate both light and dark variants.

**From config primary color, derive:**

For **dark mode** (if primary is the accent color):
- `_darkPrimary` = config primary
- `_darkPrimaryForeground` = contrasting color (white or dark based on luminance)
- `_darkSecondary` = config secondary (or derive complementary)
- `_darkAccent` = config accent (or derive triadic)
- `_darkBackground` = deep dark version of primary (10% lightness)
- `_darkCard` = slightly lighter than background (15% lightness)
- `_darkMuted` = desaturated primary at 20% lightness
- `_darkMessageBubbleOwn` = primary at 30% lightness

For **light mode**:
- `_lightPrimary` = config primary
- `_lightPrimaryForeground` = white (or dark if primary is light)
- `_lightBackground` = white or very light tint of primary
- `_lightCard` = off-white or very light gray
- `_lightMessageBubbleOwn` = config primary

### Logo Paths

Update lines 97-98 in app_colors.dart:
```dart
static const String lightLogo = '[CONFIG_LIGHT_LOGO]';
static const String darkLogo = '[CONFIG_DARK_LOGO]';
```

**Logo Sizing Note:**
- The welcome screen (`lib/features/chat/presentation/widgets/welcome_hero_screen.dart`) displays the logo at `height: 120`
- **Prefer emblem/icon-only logos** - full logos with text appear oversized at this height
- If logo appears too large, verify an emblem-only asset is being used

### Color Generation Algorithm

```
Given primary hex color:

1. Convert to HSL
2. For dark mode derived colors:
   - Background: H same, S * 0.3, L = 5%
   - Card: H same, S * 0.3, L = 8%
   - Muted: H same, S * 0.2, L = 15%
   - Border: H same, S * 0.2, L = 12%

3. For light mode derived colors:
   - Background: white (#FFFFFF)
   - Card: H same, S * 0.05, L = 98%
   - Muted: H same, S * 0.1, L = 96%

4. For foreground/text colors:
   - If primary luminance > 0.5: use dark text
   - If primary luminance <= 0.5: use light text
```

## Step 3: Update gradients.dart

File: `lib/core/theme/gradients.dart`

Generate gradient variations from the config colors:

```dart
// Primary gradient - 3 stops from lighter to darker
static const darkPrimaryGradient = LinearGradient(
  colors: [
    Color(0xFF[LIGHTER_PRIMARY]),  // +10% lightness
    Color(0xFF[PRIMARY]),           // config primary
    Color(0xFF[DARKER_PRIMARY]),    // -10% lightness
  ],
  stops: [0.0, 0.5, 1.0],
);
```

Apply same pattern to:
- `darkPrimaryGradient` / `lightPrimaryGradient`
- `darkSecondaryGradient` / `lightSecondaryGradient`
- `darkAccentGradient`
- `primaryButtonGradient`
- `secondaryButtonGradient`

## Step 4: Update App Constants

File: `lib/core/constants/app_constants.dart`

Update the app name, caption, and related strings from the config:

```dart
// App Information
static const String appName = '[CONFIG_APPNAME_LIGHT]';           // Light theme name
static const String appNameDark = "[CONFIG_APPNAME_DARK]";        // Dark theme name
static const String appCaption = '[CONFIG_CAPTION_LIGHT]';        // Light theme caption
static const String appCaptionDark = '[CONFIG_CAPTION_DARK]';     // Dark theme caption
```

**Mapping from config:**
- `appName` = `brand.appName.light` (or `brand.name` if not specified)
- `appNameDark` = `brand.appName.dark` (or `brand.name` if not specified)
- `appCaption` = `brand.caption.light` (or "Powered by AI" if not specified)
- `appCaptionDark` = `brand.caption.dark` (or same as light if not specified)

**Note:** The welcome message (`brand.welcomeMessage`) is used in `chat_home_page.dart` via the app name constants. Updating `appName` and `appNameDark` will automatically update the welcome message format: "Welcome to {appName}!"

## Step 5: Implement Animation Effects

Based on `animation.style` from config, create or update animation widgets.

### CRITICAL: Follow Existing Codebase Patterns

All effects MUST follow these patterns from the existing codebase:

#### Pattern 1: Theme Brightness Detection
```dart
final isDark = Theme.of(context).brightness == Brightness.dark;
```

#### Pattern 2: AppColors Extension Access
```dart
final appColors = Theme.of(context).extension<AppColorsExtension>()!;
final colorScheme = Theme.of(context).colorScheme;
```

#### Pattern 3: Dynamic Color Selection (from speech_to_text_widget.dart)
```dart
List<Color> _buildGlowColors() {
  final isDark = Theme.of(context).brightness == Brightness.dark;
  final appColors = context.appColors;

  if (isDark) {
    return isActive
      ? [
          colorScheme.primary.withOpacity(0.8),
          colorScheme.secondary.withOpacity(0.6),
          appColors.accent.withOpacity(0.4),
        ]
      : [
          appColors.mutedForeground.withOpacity(0.3),
          appColors.mutedForeground.withOpacity(0.2),
        ];
  }
  // Light theme with reduced intensity...
}
```

#### Pattern 4: Use AppConstants for Animation Values
Reference: `lib/core/constants/app_constants.dart`
```dart
static const Duration glowAnimationDuration = Duration(milliseconds: 2000);
static const double glowBorderSize = 2.0;
static const double speechGlowSizeListening = 12.0;
static const double speechGlowSizeIdle = 6.0;
```

#### Pattern 5: Existing Packages to Use
- **glowy_borders** - `AnimatedGradientBorder` for neon glow effects
- **flutter_animate** - `.animate().fadeIn().scale().shimmer()` chains
- **CustomPainter** - For particle/starfield effects (see `particles_widget.dart`)
- **AnimationController + AnimatedBuilder** - For wave/pulse effects

#### Pattern 6: Particles Widget Reference
See `lib/shared/widgets/particles_widget.dart` for starfield implementation:
```dart
ParticlesWidget(
  quantity: 50,
  color: appColors.primary,  // Theme-aware
  staticity: 50,
  size: 0.4,
)
```

### Effect Implementation Patterns

Reference: https://github.com/flutterfx/flutterfx_widgets

#### cosmic style
Create `lib/shared/widgets/effects/cosmic_background.dart`:
- Animated starfield using CustomPainter
- Subtle parallax on mouse/touch movement
- Use `flutter_animate` for particle fade effects

#### neon style
Create `lib/shared/widgets/effects/border_beams.dart`:
- Animated gradient border using `glowy_borders` package
- Pulsing glow effect synced with primary color
- Use config primary color for the beam color

#### minimal style
- No new widgets needed
- Ensure existing `BlurFade` transitions are subtle (duration: 200ms, blur: 2.0)

#### professional style
Create `lib/shared/widgets/effects/fancy_card.dart`:
- Elevated card with subtle shadow animation
- Hover state with slight scale and shadow increase
- Clean, understated motion

#### playful style
Create `lib/shared/widgets/effects/particle_burst.dart`:
- Confetti/particle effect on interactions
- Use chart colors from config for particle variety
- Bouncy spring animations

### Effect File Structure

```
lib/shared/widgets/effects/
├── cosmic_background.dart    # For cosmic style
├── border_beams.dart         # For neon style
├── fancy_card.dart           # For professional style
├── particle_burst.dart       # For playful style
└── effects_provider.dart     # Exports based on active style
```

### effects_provider.dart Template

```dart
import 'package:flutter/material.dart';

enum AnimationStyle { cosmic, neon, minimal, professional, playful }

// Current active style from config
const AnimationStyle activeStyle = AnimationStyle.[CONFIG_STYLE];

class EffectsProvider {
  static Widget wrapWithBackground(Widget child) {
    switch (activeStyle) {
      case AnimationStyle.cosmic:
        return CosmicBackground(child: child);
      case AnimationStyle.neon:
        return BorderBeamsBackground(child: child);
      case AnimationStyle.professional:
        return FancyCardBackground(child: child);
      case AnimationStyle.playful:
        return FlickeringGridBackground(child: child);
      case AnimationStyle.minimal:
      default:
        return child;
    }
  }

  static Widget wrapWithLoadingEffect(Widget child) {
    // Similar switch for loading indicators
  }
}
```

## Step 6: Verify Build

After making all changes:

1. Run `flutter analyze` to check for errors
2. Run `flutter build web --wasm` to verify build succeeds
3. Report any issues found

**IMPORTANT:** If testing via the backend server (http://localhost:8000):
- The server serves static files from `build/web`
- After rebuilding, **restart the server** to serve the new build
- Kill existing server: `lsof -ti :8000 | xargs kill -9`
- Start fresh: `cd backend && uv run python app.py`

## Output Summary

After applying theme, display:

```
Theme applied successfully!

Updated files:
- lib/core/theme/app_colors.dart (colors + logos)
- lib/core/theme/gradients.dart (gradient variations)
- lib/core/constants/app_constants.dart (app name + caption)
- lib/shared/widgets/effects/ (animation widgets)

Brand applied:
- App Name: [NAME]
- Caption: [CAPTION]
- Welcome: [WELCOME_MESSAGE]

Colors applied:
- Primary: [HEX]
- Secondary: [HEX]
- Accent: [HEX]

Animation style: [STYLE]

Next steps:
- Run `flutter build web --wasm` to rebuild
- Restart the backend server to serve the new build
- Test the app at http://localhost:8000
```

## Rollback

If issues occur, the skill should:
1. Keep original files backed up (git handles this)
2. Provide instructions to revert: `git checkout -- lib/core/theme/`

## File Modification Checklist

Before completing, verify these files were updated:

- [ ] `lib/core/theme/app_colors.dart` - Color constants and logo paths
- [ ] `lib/core/theme/gradients.dart` - Gradient definitions
- [ ] `lib/core/constants/app_constants.dart` - App name, caption, welcome message
- [ ] `lib/shared/widgets/effects/` - Animation effect widgets (if style != minimal)
- [ ] Verify `flutter analyze` passes
- [ ] Commit changes with descriptive message

## Error Handling

**Config file not found:**
```
theme_config.json not found at assets/config/theme_config.json

Run /brand-theme first to generate the configuration.
```

**Invalid color format:**
```
Invalid color "[value]" in config. Expected hex format like #FF5F46.
Please fix the config file or run /brand-theme again.
```

**Build fails after changes:**
```
Build failed after applying theme. Errors:
[error details]

You can revert changes with: git checkout -- lib/core/theme/
Then review the config and try again.
```
COMPATIBILITY & REFERENCES

Platform guidance

Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.