docs/agi/skills/theme-system-integration/SKILL.md
# Skill: theme-system-integration ## Scope Add theme support to existing controls or integrate theming into new features. **Does:** - Define params schemas for controls - Register variants in the variant registry - Integrate `resolve_params` for merge priority - Add CSS variable hooks and data attributes - Test theme inheritance and override behavior **Does Not:** - Create controls from scratch (see jsgui3-control-creation skill) - Handle runtime theme switching animations ## Inputs - Contro
npx skillsauth add metabench/jsgui3-html docs/agi/skills/theme-system-integrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Add theme support to existing controls or integrate theming into new features.
Does:
resolve_params for merge priorityDoes Not:
Button, Panel, Input)primary, ghost, compact)size, variant, position)Identify themeable properties:
Create schema in themes/variants.js:
const <control>_schema = {
// Enum params (validated)
size: ['small', 'medium', 'large'],
variant: ['primary', 'secondary', 'ghost'],
position: ['left', 'right'],
// Boolean params
show_icon: [true, false],
// String params (no validation)
// icon_name: ['string']
};
// Add to param_schemas:
const param_schemas = {
// ... existing
<control>: <control>_schema
};
const <control>_variants = {
'default': {
size: 'medium',
variant: 'secondary',
// ... all defaults
},
'<variant_name>': {
// Override specific values
size: 'medium',
variant: 'primary'
}
};
const variants = {
window: window_variants,
button: button_variants,
panel: panel_variants,
// ADD:
<control>: <control>_variants
};
module.exports = {
variants,
get_variant_params,
register_variant,
// ADD:
<control>_variants
};
theme_params.js:const derive_hooks = (control_type, params) => {
const attrs = {};
const classes = [];
if (control_type === '<control>') {
// Data attributes for CSS targeting
if (params.variant) {
attrs['data-variant'] = params.variant;
}
if (params.size && params.size !== 'medium') {
attrs['data-size'] = params.size;
}
// Classes for structural variants
if (params.position) {
classes.push(params.position); // e.g., 'left', 'right'
}
}
return { attrs, classes };
};
const { resolve_params, apply_hooks } = require('<path>/control_mixins/theme_params');
constructor(spec) {
super(spec);
this.__type_name = '<control>';
// Resolve theme params
const { params, hooks } = resolve_params('<control>', spec, this.context);
this._theme_params = params;
apply_hooks(this, hooks);
// Use params in composition
this._compose(params);
}
_compose(params) {
// Instead of hardcoded values:
const size = params.size || 'medium';
const show_icon = params.show_icon !== false;
// Instead of hardcoded order:
const order = params.order || ['a', 'b', 'c'];
for (const item of order) {
if (params[`show_${item}`] === false) continue;
// Create item
}
}
themes/token_maps.js:const SIZE_TOKENS = {
<control>: {
small: {
'--<control>-height': '28px',
'--<control>-padding': '8px',
'--<control>-font-size': '12px'
},
medium: {
'--<control>-height': '36px',
'--<control>-padding': '12px',
'--<control>-font-size': '14px'
},
large: {
'--<control>-height': '44px',
'--<control>-padding': '16px',
'--<control>-font-size': '16px'
}
}
};
const { apply_token_map } = require('<path>/themes/token_maps');
// After resolve_params:
apply_token_map(this, '<control>', params);
.<control> {
height: var(--<control>-height, 36px);
padding: 0 var(--<control>-padding, 12px);
font-size: var(--<control>-font-size, 14px);
}
.<control>[data-variant="primary"] {
background: var(--color-primary);
color: white;
}
.<control>[data-variant="ghost"] {
background: transparent;
border: none;
}
.<control>[data-size="small"] {
/* If needed beyond CSS vars */
}
types/theme.d.ts:export interface <Control>Params {
size?: 'small' | 'medium' | 'large';
variant?: 'primary' | 'secondary' | 'ghost';
position?: 'left' | 'right';
show_icon?: boolean;
}
export interface ThemeParams {
window?: WindowParams;
button?: ButtonParams;
// ADD:
<control>?: <Control>Params;
}
export interface <Control>Spec extends ControlSpec<<Control>Params> {
variant?: '<variant1>' | '<variant2>';
}
// test/controls/<control>_theme.test.js
require('../setup');
const { expect } = require('chai');
describe('<Control> Theme Integration', () => {
let <Control>, context;
before(() => {
<Control> = require('../../controls/.../<control>');
});
beforeEach(() => {
context = createTestContext();
});
describe('default variant', () => {
it('uses default params', () => {
const ctrl = new <Control>({ context });
expect(ctrl._theme_params.size).to.equal('medium');
});
});
describe('spec.variant', () => {
it('applies variant preset', () => {
const ctrl = new <Control>({ context, variant: 'primary' });
expect(ctrl._theme_params.variant).to.equal('primary');
});
});
describe('spec.params override', () => {
it('overrides variant defaults', () => {
const ctrl = new <Control>({
context,
variant: 'primary',
params: { size: 'large' }
});
expect(ctrl._theme_params.size).to.equal('large');
});
});
describe('context.theme.params', () => {
it('applies theme-level defaults', () => {
context.theme = {
params: { <control>: { size: 'small' } }
};
const ctrl = new <Control>({ context });
expect(ctrl._theme_params.size).to.equal('small');
});
});
describe('backward compatibility', () => {
it('works without any params', () => {
const ctrl = new <Control>({ context });
// Should produce same output as before theming
});
});
});
themes/variants.jsdefault variant matches pre-theme behavior (backward compat)derive_hooksresolve_params in constructor┌─────────────────────────┐
│ spec.params │ ← Highest (per-instance)
├─────────────────────────┤
│ context.theme.params │ ← Theme-level
│ [control_type] │
├─────────────────────────┤
│ Variant defaults │ ← From variant registry
│ (spec.variant or │
│ context.theme.extends) │
└─────────────────────────┘
tools
# Skill: ui-pick-prompting ## Scope Use the ui-pick tool to present structured choices to the user. **Does:** - Show GUI picker with options. - Wait for user selection. - Return structured result (selection, cancelled, etc.). **Does Not:** - Handle complex multi-step wizards. - Manage state between prompts. ## Inputs - Options array (strings or objects with label/value/description). - Theme (optional): `wlilo` (dark) or `bright` (light). ## Procedure ### Via HTTP (Current Session) ```power
development
# Skill: typescript-types ## Scope Create and maintain TypeScript declaration files (.d.ts) for jsgui3-html components. **Does:** - Generate .d.ts files for controls and modules - Define interfaces for specs, params, and return types - Update package.json exports for TypeScript consumers - Maintain backward compatibility with JavaScript users **Does Not:** - Convert source files to TypeScript - Add runtime type checking - Handle complex generic patterns ## Inputs - File or module to type (e.
testing
# Skill: session-discipline ## Scope Maintain structured notes across agent sessions to ensure continuity and knowledge transfer. **Does:** - Initialize session folders with standard structure. - Track active work, findings, and follow-ups. - Enable future agents to resume work seamlessly. **Does Not:** - Persist state between AI context windows (that's the purpose of the docs). - Replace version control (still commit changes). ## Session Structure Each session lives in `docs/sessions/<date
testing
# Skill: lab-experimentation ## Scope Use lab experiments to answer "how should we do this?" questions about jsgui3 behavior. **Does:** - Run existing lab experiments to confirm behavior. - Create minimal new experiments when behavior is unknown. - Promote stable findings into Skills/Patterns. **Does Not:** - Replace unit tests (labs are for exploration). - Cover production deployment scenarios. ## Lab Structure ``` lab/ ├── experiments/ # Individual experiments │ └── 001-topic-