How the Theme System Works in the Web-Video-Presentation Skill
The web-video-presentation skill uses a declarative, file-based architecture that separates theme metadata (theme.json) from design tokens (tokens.css), enabling automatic discovery and runtime CSS injection without requiring code changes.
The web-video-presentation skill in the ConardLi/garden-skills repository implements a lightweight theming system that allows designers to create fully-styled video presentations by supplying just two declarative files. This architecture leverages CSS custom properties and TypeScript interfaces to ensure type-safe, runtime theme switching driven by mood-based motion configurations.
Theme System Architecture
The theme system adopts a strict separation of concerns between identity metadata and visual styling. Each theme resides in its own directory under skills/web-video-presentation/themes/<theme-id>/ and must provide exactly two files to be recognized by the loader.
Metadata Layer: theme.json
The theme.json file defines the theme's identity and semantic characteristics. It declares a unique id, human-readable name and nameZh fields for internationalization, a descriptive text block, and strategic metadata that drives UI behavior.
Key fields include:
mood: An array of tags (e.g.,"cinematic","light","professional") that downstream animation utilities consume to adjust motion timingbestFor: Usage hints describing optimal scenarios for the themepreview: A color palette object (shell,surface,text,accent) used by the theme picker UI to render swatches
Located at skills/web-video-presentation/themes/warm-keynote/theme.json, this file serves as the contract between the designer's intent and the runtime system.
Design Tokens Layer: tokens.css
The tokens.css file declares CSS custom properties that the presentation UI consumes uniformly. All components reference these variables, ensuring that swapping a theme instantly updates colors, typography, motion timing, shadows, and grid patterns without modifying component code.
Variables defined in tokens.css include:
--accent,--surface,--text: Core color tokens--font-display-en,--font-body: Typography stacks--dur-base,--dur-slow,--dur-cinematic: Motion timing variables--shadow-elevation-*: Depth and shadow values
Type Safety and the Theme Interface
To ensure runtime reliability, the system defines a strict TypeScript interface in skills/web-video-presentation/templates/src/registry/types.ts:
export interface Theme {
id: string;
name: string;
nameZh: string;
description: string;
descriptionZh: string;
mood: string[];
bestFor: string[];
preview: {
shell: string;
surface: string;
text: string;
accent: string
};
}
This Theme type enforces the shape of theme.json files, enabling compile-time checking when the loader fetches metadata. The interface supports bilingual content through the nameZh and descriptionZh fields, facilitating internationalized presentation UIs.
Theme Discovery and Build Process
The system automatically discovers available themes during the build phase through skills/web-video-presentation/scripts/scaffold.sh. This shell script performs the following operations:
- Loops over every folder under
skills/web-video-presentation/themes/* - Validates the presence of
theme.jsonandtokens.css - Reads each
theme.jsonto extract metadata - Compiles a master
themes.jsoncatalog that the UI queries at runtime
This build-time generation ensures the presentation UI always has an accurate, up-to-date list of available themes without manual registration.
Runtime Loading and Injection
At application startup, the runtime loads the selected theme's assets. The entry point at skills/web-video-presentation/templates/src/main.tsx coordinates this injection process:
// src/main.tsx
import { useEffect } from 'react';
import type { Theme } from './registry/types';
export function applyTheme(themeId: string) {
// Resolve the CSS file using Vite's import.meta.glob
const cssModules = import.meta.glob('../themes/*/tokens.css', { eager: true });
const cssPath = Object.keys(cssModules).find(p => p.includes(`/${themeId}/`));
if (cssPath) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = cssPath;
document.head.appendChild(link);
}
}
// Usage within a React component
function ThemePicker({ themes }: { themes: Theme[] }) {
const [selected, setSelected] = useState<string>(themes[0].id);
useEffect(() => applyTheme(selected), [selected]);
// ...
}
The JSON metadata is fetched separately to populate the theme-picker UI:
export async function loadThemeMeta(id: string): Promise<Theme> {
const resp = await fetch(`/themes/${id}/theme.json`);
return resp.json();
}
Mood-Driven Motion and Animation
Components consume the mood array from theme.json to adjust animation characteristics dynamically. The mood tags directly map to CSS timing variables defined in tokens.css, creating a semantic link between visual style and motion behavior.
For example, a theme with "cinematic" in its mood array will utilize longer duration values (--dur-cinematic), while a "light" or "playful" mood triggers faster spring settings (--dur-base). The Stage component and animation helpers reference these variables to maintain consistent visual feel:
// src/components/Stage.tsx
export const Stage = () => (
<section
style={{
backgroundColor: 'var(--surface)',
color: 'var(--text)',
padding: 'var(--stage-pad-y) var(--stage-pad-x)',
}}
>
{/* Content renders with theme-derived values */}
</section>
);
Creating a Custom Theme
To add a new theme to the web-video-presentation skill:
- Create a new directory under
skills/web-video-presentation/themes/<your-theme-id>/ - Add a
theme.jsonfile conforming to theThemeinterface, specifyingid,moodtags, andpreviewcolors - Add a
tokens.cssfile defining all required CSS custom properties (--accent,--font-*,--dur-*) - Run the build process to execute
scaffold.sh, which automatically registers the new theme in the catalog
No modifications to the TypeScript source code are required, as the loader dynamically resolves themes based on directory structure and file presence.
Summary
- The web-video-presentation skill uses a two-file system:
theme.jsonfor metadata andtokens.cssfor design tokens scaffold.shautomatically discovers themes and generates a runtime catalog during the build process- The
Themeinterface intypes.tsprovides type safety for JSON metadata, including internationalization support - CSS custom properties enable instant visual updates without component recompilation
moodtags in theme metadata drive animation timing through semantic variable mapping- New themes require only directory creation and file placement, with zero code changes needed
Frequently Asked Questions
What files are required to create a new theme in web-video-presentation?
You must provide exactly two files in a new directory under skills/web-video-presentation/themes/<theme-id>/: a theme.json containing metadata (id, name, mood, preview colors) and a tokens.css declaring CSS custom properties. The scaffold.sh script automatically detects these files during the build process.
How does the theme system handle animation timing and motion?
The system reads the mood array from theme.json (e.g., "cinematic", "light") and maps these tags to CSS timing variables like --dur-base and --dur-cinematic defined in tokens.css. Components reference these variables to adjust animation durations and easing curves dynamically.
Where is the theme catalog generated during the build process?
The scaffold.sh script located at skills/web-video-presentation/scripts/scaffold.sh iterates through the themes/ directory, validates each theme's files, and compiles a themes.json catalog. This catalog is consumed by the UI to populate the theme picker without hardcoding theme lists.
Can themes support internationalization?
Yes. The Theme interface in types.ts explicitly includes nameZh and descriptionZh fields. When creating a theme, you can provide bilingual content in theme.json, and the presentation UI can render the appropriate language based on user preferences.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →