Skip to content
Back to skills

Apply Theme

ASecurity

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.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
developmentpythongospringtestinggitbackend

Security analysis

A100/100

Scanned September 27, 2026

npx -y skills add David-Li0406/meta-skill-evloving --skill apply-theme --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Apply Theme?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Apply Theme
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/david-li0406-apply-theme/badge)](https://www.skillsdirectory.com/skills/david-li0406-apply-theme)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: apply-theme
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.
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…