Ui Component
Overview
Build UI components as four files (.html, .ts, .stories.ts, .md). Use <template> cloning and data attributes for wiring, keep UI copy in Hebrew (RTL), and keep the docs concise and technical.
Workflow
- •Confirm or create the component set at src/components/<Component>.html, src/components/<Component>.ts, src/components/<Component>.stories.ts, and src/components/<Component>.md.
- •Put all markup and Tailwind classes in the HTML file inside a <template>.
- •Keep logic, state, and event wiring in the TS file; do not embed scripts in HTML.
- •In the .html file, put all text in Hebrew; except for PlanIt and other english-only phrases.
- •Use data attributes in the HTML to target elements from TS.
- •Export a component factory (e.g.,
AppHeader()) that returns a root element. - •Add a concise Markdown doc in
src/components/<Component>.mdfollowing the structure below. Component.mddocs are always written in English. - •Create
src/components/<Component>.stories.tsalongside the component. - •Define
DefaultandDarkstories, settingglobals: { theme: 'dark' }for dark. - •Rely on
.storybook/preview.tsfor the wrapper; do not create custom preview shells. - •Mount by
replaceWith()orappendChild()in the caller (avoidouterHTML). - •For async or data-driven components, add skeleton placeholders in the
template using the shared
skeleton-shimmerutility and adata-skeletonattribute that the TS layer toggles off once data is populated.
Component Contract
- •Files:
- •src/components/<Component>.html
- •src/components/<Component>.ts
- •src/components/<Component>.stories.ts
- •src/components/<Component>.md
- •HTML:
- •Wrap the component in a single <template> element.
- •Keep UI-only concerns here: structure, Tailwind classes, semantic tags.
- •Use data attributes like data-role, data-action, data-slot for hooks.
- •TypeScript:
- •Import the HTML as text (
?rawwith Vite). - •Clone the template into a root element.
- •Bind events and return a single root element.
- •Import the HTML as text (
Documentation File
- •Use
src/components/<Component>.mdto explain the component behavior. - •Keep it short and technical; follow the CourseTable example.
- •Write the documentation in English.
- •Recommended sections: Overview, Template Structure, Data Flow, Dependencies, Notes.
Verification Script
Run the repository check script to ensure every component has the required files:
python3 .agents/skills/ui-component/scripts/verify_components.py
Implementation Notes
- •Prefer small, explicit factories:
Component()returns an element. - •Keep state in TS; avoid inline styles or JS in HTML.
- •Use class toggles or data attributes for stateful styling.
- •Keep DOM queries scoped to the cloned root element.
- •Use
replaceWith(Component())when swapping placeholders. - •When data is not yet available, keep
data-skeleton="true"on the root and rely onskeleton-shimmerplaceholders in the HTML. Remove the attribute when real data is rendered.
Storybook Integration
- •Story files live next to components: src/components/<Component>.stories.ts.
- •Use the global Storybook theme toolbar and backgrounds.
- •Define two stories:
Default(light) andDark(setglobals: { theme: 'dark' }). - •Do not build custom preview wrappers; rely on
.storybook/preview.tsfor theme wrapping.
Dark Mode Behavior
- •Components should use CSS variable-based utilities (e.g.,
text-text,bg-surface-1). - •Dark mode is applied at the app shell or Storybook wrapper by swapping CSS variables.
- •No
dark:prefixes are required inside components when variables are used.
Minimal Template Pattern
Use this shape unless the codebase already provides a different pattern:
Component.html
html
<template>
<section class="..." data-component="Component">
<h2 class="..." data-role="title"></h2>
<button class="..." data-action="primary"></button>
</section>
</template>
Component.ts
ts
import templateHtml from './Component.html?raw';
export function Component(): HTMLElement {
const template = document.createElement('template');
template.innerHTML = templateHtml;
const templateElement = template.content.firstElementChild;
if (!(templateElement instanceof HTMLTemplateElement)) {
throw new Error('Component template element not found');
}
const root = templateElement.content.firstElementChild?.cloneNode(true);
if (!(root instanceof HTMLElement)) {
throw new Error('Component template root not found');
}
const title = root.querySelector<HTMLElement>("[data-role='title']");
if (title !== null) {
title.textContent = 'Title';
}
return root;
}
Component.stories.ts
ts
import type { Meta, StoryObj } from '@storybook/html';
import { Component } from './Component';
const meta: Meta = {
title: 'Components/Component',
};
export default meta;
export type Story = StoryObj;
export const Default: Story = {
render: () => Component(),
globals: {
theme: 'light',
},
};
export const Dark: Story = {
render: () => Component(),
globals: { theme: 'dark' },
parameters: {
backgrounds: {
default: 'dark',
},
},
};
Component.md
markdown
# Component ## Overview Describe what the component renders and where it is used. ## Template Structure - Note key layout regions and slots. ## Data Flow 1. Outline how the TS wires the template and any events. ## Dependencies - List related modules or assets. ## Notes - Mention constraints or gotchas.