Ui Component
Overview
Build UI components as four files (.html, .ts, .stories.ts, .context.md). Use 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/.html, src/components/.ts, src/components/.stories.ts, and src/components/.context.md.
- Put all markup and Tailwind classes in the HTML file inside a .
- 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.
- Ensure all interactive controls prevent mobile double-tap zoom by applying
touch-manipulation (or equivalent touch-action: manipulation) to tap targets (button, a, [role="button"], and other clickable wrappers).
- Export a component factory (e.g.,
AppHeader()) that returns a root element.
- Add a concise Markdown doc in
src/components/<Component>.context.md following the structure below. Component .context.md docs are always written in English.
- Create
src/components/<Component>.stories.ts alongside the component.
- Define
Default and Dark stories, setting globals: { theme: 'dark' } for dark.
- Rely on
.storybook/preview.ts for the wrapper; do not create custom preview shells.
- Mount by
replaceWith() or appendChild() in the caller (avoid outerHTML).
- For async or data-driven components, add skeleton placeholders in the
template using the shared
skeleton-shimmer utility and a data-skeleton
attribute that the TS layer toggles off once data is populated.
Component Contract
- Files:
- src/components/.html
- src/components/.ts
- src/components/.stories.ts
- src/components/.context.md
- HTML:
- Wrap the component in a single 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 (
?raw with Vite).
- Clone the template into a root element.
- Bind events and return a single root element.
Documentation File
- Use
src/components/<Component>.context.md to 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 on skeleton-shimmer placeholders in the HTML. Remove the attribute when
real data is rendered.
Storybook Integration
- Story files live next to components: src/components/.stories.ts.
- Use the global Storybook theme toolbar and backgrounds.
- Define two stories:
Default (light) and Dark (set globals: { theme: 'dark' }).
- Do not build custom preview wrappers; rely on
.storybook/preview.ts for 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
<template>
<section class="..." data-component="Component">
<h2 class="..." data-role="title"></h2>
<button class="..." data-action="primary"></button>
</section>
</template>
Component.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
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.context.md
# 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.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: ui-component3description: Build or update UI components using vanilla HTML and TypeScript (using <template>), with Storybook support, and documentation in a Markdown file (in total, 4 files per component). Use when asked to create new components, refactor UI into HTML+TS templates, or add stories/docs for existing components in this codebase. Use when this capability is needed.4---56# Ui Component78## Overview910Build UI components as four files (.html, .ts, .stories.ts, .context.md). Use <template> cloning and data attributes for wiring, keep UI copy in Hebrew (RTL), and keep the docs concise and technical.1112## Workflow13141. Confirm or create the component set at src/components/<Component>.html, src/components/<Component>.ts, src/components/<Component>.stories.ts, and src/components/<Component>.context.md.152. Put all markup and Tailwind classes in the HTML file inside a <template>.163. Keep logic, state, and event wiring in the TS file; do not embed scripts in HTML.174. In the .html file, put all text in Hebrew; except for PlanIt and other english-only phrases.185. Use data attributes in the HTML to target elements from TS.196. Ensure all interactive controls prevent mobile double-tap zoom by applying `touch-manipulation` (or equivalent `touch-action: manipulation`) to tap targets (`button`, `a`, `[role="button"]`, and other clickable wrappers).207. Export a component factory (e.g., `AppHeader()`) that returns a root element.218. Add a concise Markdown doc in `src/components/<Component>.context.md` following the structure below. Component `.context.md` docs are always written in English.229. Create `src/components/<Component>.stories.ts` alongside the component.2310. Define `Default` and `Dark` stories, setting `globals: { theme: 'dark' }` for dark.2411. Rely on `.storybook/preview.ts` for the wrapper; do not create custom preview shells.2512. Mount by `replaceWith()` or `appendChild()` in the caller (avoid `outerHTML`).2613. For async or data-driven components, add skeleton placeholders in the27 template using the shared `skeleton-shimmer` utility and a `data-skeleton`28 attribute that the TS layer toggles off once data is populated.2930## Component Contract3132- Files:33 - src/components/<Component>.html34 - src/components/<Component>.ts35 - src/components/<Component>.stories.ts36 - src/components/<Component>.context.md37- HTML:38 - Wrap the component in a single <template> element.39 - Keep UI-only concerns here: structure, Tailwind classes, semantic tags.40 - Use data attributes like data-role, data-action, data-slot for hooks.41- TypeScript:42 - Import the HTML as text (`?raw` with Vite).43 - Clone the template into a root element.44 - Bind events and return a single root element.4546## Documentation File4748- Use `src/components/<Component>.context.md` to explain the component behavior.49- Keep it short and technical; follow the CourseTable example.50- Write the documentation in English.51- Recommended sections: Overview, Template Structure, Data Flow, Dependencies, Notes.5253## Verification Script5455Run the repository check script to ensure every component has the required56files:5758`python3 .agents/skills/ui-component/scripts/verify_components.py`5960## Implementation Notes6162- Prefer small, explicit factories: `Component()` returns an element.63- Keep state in TS; avoid inline styles or JS in HTML.64- Use class toggles or data attributes for stateful styling.65- Keep DOM queries scoped to the cloned root element.66- Use `replaceWith(Component())` when swapping placeholders.67- When data is not yet available, keep `data-skeleton="true"` on the root and68 rely on `skeleton-shimmer` placeholders in the HTML. Remove the attribute when69 real data is rendered.7071## Storybook Integration7273- Story files live next to components: src/components/<Component>.stories.ts.74- Use the global Storybook theme toolbar and backgrounds.75- Define two stories: `Default` (light) and `Dark` (set `globals: { theme: 'dark' }`).76- Do not build custom preview wrappers; rely on `.storybook/preview.ts` for theme wrapping.7778## Dark Mode Behavior7980- Components should use CSS variable-based utilities (e.g., `text-text`, `bg-surface-1`).81- Dark mode is applied at the app shell or Storybook wrapper by swapping CSS variables.82- No `dark:` prefixes are required inside components when variables are used.8384## Minimal Template Pattern8586Use this shape unless the codebase already provides a different pattern:8788### Component.html8990```html91<template>92 <section class="..." data-component="Component">93 <h2 class="..." data-role="title"></h2>94 <button class="..." data-action="primary"></button>95 </section>96</template>97```9899### Component.ts100101```ts102import templateHtml from './Component.html?raw';103104export function Component(): HTMLElement {105 const template = document.createElement('template');106 template.innerHTML = templateHtml;107 const templateElement = template.content.firstElementChild;108 if (!(templateElement instanceof HTMLTemplateElement)) {109 throw new Error('Component template element not found');110 }111 const root = templateElement.content.firstElementChild?.cloneNode(true);112 if (!(root instanceof HTMLElement)) {113 throw new Error('Component template root not found');114 }115116 const title = root.querySelector<HTMLElement>("[data-role='title']");117 if (title !== null) {118 title.textContent = 'Title';119 }120121 return root;122}123```124125### Component.stories.ts126127```ts128import type { Meta, StoryObj } from '@storybook/html';129130import { Component } from './Component';131132const meta: Meta = {133 title: 'Components/Component',134};135136export default meta;137138export type Story = StoryObj;139140export const Default: Story = {141 render: () => Component(),142 globals: {143 theme: 'light',144 },145};146147export const Dark: Story = {148 render: () => Component(),149 globals: { theme: 'dark' },150 parameters: {151 backgrounds: {152 default: 'dark',153 },154 },155};156```157158### Component.context.md159160```markdown161# Component162163## Overview164165Describe what the component renders and where it is used.166167## Template Structure168169- Note key layout regions and slots.170171## Data Flow1721731. Outline how the TS wires the template and any events.174175## Dependencies176177- List related modules or assets.178179## Notes180181- Mention constraints or gotchas.182```183184---185> Converted and distributed by [TomeVault](https://tomevault.io/claim/selfint) — claim your Tome and manage your conversions.186<!-- tomevault:4.0:skill_md:2026-04-16 -->