Skip to main content

atomico

Core entry-point router, orchestrator, and reference manual for all Atomico.js tasks. Contains coding standards, API cheat sheets, architectural guidelines, and validation rules.

Informations de source

Dépôt
atomicojs/atomico
Dernière activité de la source
18 juillet 2026 à 21:24
Langue détectée de SKILL.md
anglais
Étoiles
1 281
Forks
45

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
13 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
atomico
description
Core entry-point router, orchestrator, and reference manual for all Atomico.js tasks. Contains coding standards, API cheat sheets, architectural guidelines, and validation rules.
license
MIT
compatibility
Atomico >=2.0, TypeScript >=5.0
metadata
{"category":"core","priority":"highest"}
# Atomico.js — Consolidated Development & Validation Guide Unified reference for building, auditing, and validating Atomico.js web components. > **AGENT CONTRACT**: You may not respond to a code generation request > without completing the Validation Report in §7. > If any check fails, fix the code before responding — do not report > failures as acceptable output. --- ## 1. Hello World Component Every Atomico component is defined with `c()` and **must** return a `<host>` root element. ```tsx import { c, useProp, css } from "atomico"; export const MyCounter = c( ({ label }) => { const [value, setValue] = useProp<number>("value"); return ( <host shadowDom> <button onclick={() => setValue(value + 1)}> {label}: {value} </button> </host> ); }, { props: { label: { type: String, value: () => "Increment" }, size: { type: String, reflect: true, value: (): "normal" | "small" => "normal" }, value: { type: Number, value: () => 0 } }, styles: css` :host { --font-size: 1em; } :host([size="small"]) { --font-size: 0.5em; } ` } ); ``` --- ## 2. Architectural Rules 1. **Modular by Default**: Avoid monolithic files. Split complex views into a `components/` folder. 2. **Reuse First**: Audit existing workspace components before generating new ones. 3. **Prop-Driven Communication**: Pass data down via props, dispatch events up. 4. **Separate Registration**: NEVER call `customElements.define` inside the component file. Centralize all registrations in an index file. ```tsx // components/index.ts <- the ONLY place where elements are registered import { MyCounter } from "./my-counter.js"; customElements.define("my-counter", MyCounter); ``` --- ## 3. Props ### Type system ```ts // Valid constructors for the `type:` field in props type AtomicoPropType = | StringConstructor // type: String | NumberConstructor // type: Number | BooleanConstructor // type: Boolean | ArrayConstructor // type: Array -> requires value: (): T[] => [] | ObjectConstructor // type: Object -> requires value: (): T => ({...}) | MapConstructor | SetConstructor | PromiseConstructor | (new (...args: any[]) => HTMLElement); // HTMLElement or any subclass // Short form - prop without default or reflect type PropShorthand = AtomicoPropType; // Long form - prop with default or reflect interface PropConfig<T> { type: AtomicoPropType; value?: () => T; // MUST be a factory callback - never a static value reflect?: boolean; // only String | Number | Boolean } ``` ### Declaration syntax Use the simplest form that satisfies the requirement: ```tsx props: { // Shorthand: no default, no reflect label: String, // Config object: only when a default value is needed count: { type: Number, value: () => 0 }, // Reflect: only to control CSS via attribute selectors variant: { type: String, reflect: true, value: (): "primary" | "danger" => "primary" } } ``` **Forbidden:** ```tsx props: { name: { type: String }, // Verbose config with no default or reflect - use shorthand count: { type: Number, value: 0 } // Static value - must be a factory callback } // Manual type on destructured render argument: // ({ label }: { label: string }) => ... Atomico infers types automatically from props block ``` ### Arrays & Objects — strict return-type annotation Without an explicit return type, TypeScript resolves `Array` to `never[]` and `Object` to `{}`. Annotate the factory return type to guarantee correct inference in TSX: ```tsx interface Option { value: string; label: string; } interface Config { theme: "light" | "dark"; debug: boolean; } props: { // Return type annotation on the factory - inferred as Option[] in TSX options: { type: Array, value: (): Option[] => [] }, config: { type: Object, value: (): Config => ({ theme: "light", debug: false }) } } ``` ### Reflect rules `reflect: true` is only for **visual / CSS states**. Allowed types: `String`, `Number`, `Boolean`. ```tsx props: { disabled: { type: Boolean, reflect: true }, variant: { type: String, reflect: true, value: () => "primary" } }, styles: css` :host([disabled]) { opacity: 0.5; pointer-events: none; } :host([variant="danger"]) { background: red; } ` ``` Never use `reflect: true` on `Array` or `Object` — it triggers expensive DOM serialization. ### Events & Callbacks ```ts // event<Detail>() - Fire-and-Forget // Name WITHOUT "on" prefix. The JSX consumer receives it as "on<propName>" // Calling props.myEvent(detail) dispatches CustomEvent with event.detail = detail declare function event<Detail = void>(options?: { bubbles?: boolean; composed?: boolean; cancelable?: boolean; }): EventDescriptor<Detail>; // callback<Fn>() - Request-Response // Fn MUST NOT return void - if no return value is needed, use event() instead declare function callback< Fn extends (...args: any[]) => NonNullable<unknown> >(): Fn | undefined; ``` ```tsx export const ActionButton = c( (props) => ( <host> <button onclick={() => props.action({ id: 42 })}>Fire</button> </host> ), { props: { // No "on" prefix - Atomico maps it as "onaction" automatically in JSX action: event<{ id: number }>({ bubbles: true, composed: true }) } } ); ``` > **Shadow DOM boundary rule**: Native events like `change` and `submit` have `composed: false` > and **cannot cross the Shadow DOM boundary**. Always declare a custom `event()` with > `{ bubbles: true, composed: true }` and dispatch it explicitly. ```tsx export const UiSelect = c( ({ change, options }) => ( <host shadowDom> <select onchange={(e) => change(e.currentTarget.value as string)}> {options.map((o) => ( <option value={o.value}>{o.label}</option> ))} </select> </host> ), { props: { options: { type: Array, value: (): { value: string; label: string }[] => [] }, change: event<string>({ bubbles: true, composed: true }) } } ); ``` ```tsx export const TextEditor = c( ({ content, save }) => ( <host shadowDom> <button onclick={async () => { if (save) { const ok = await save(content); if (ok) console.log("Saved!"); } }} > Save </button> </host> ), { props: { content: { type: String, value: () => "" }, // callback MUST return a value - if no return is needed, use event() instead save: callback<(content: string) => Promise<boolean>>() } } ); ``` **Forbidden:** ```tsx // "on" prefix in event or callback name props: { onChange: event(); } // Atomico treats "on*" props as native event subscriptions // callback returning void - use event() instead props: { save: callback<() => void>(); } // Re-dispatching native events that already bubble const dispatchInput = useEvent("input"); // "input" already bubbles causes double-fire ``` --- ## 4. State Management ```ts // Signature reference declare function useProp<T>(name: string): [T, (val: T) => void]; declare function useState<T>(init: T | (() => T)): [T, (val: T) => void]; declare function useObjectState<T extends object>( init: T ): [T, (partial: Partial<T>) => void]; ``` ### Decision tree ``` Does the state need to be read from outside (parent / CSS)? +- YES -> declare it in props | +- Does the child also write to it? -> useProp<T>() | +- Read-only? -> direct destructuring ({ myProp }) +- NO -> private state +- Single value (boolean, string)? -> useState +- Two or more related values? -> useObjectState<T> ``` ### Correct patterns ```tsx // Read-only prop - direct destructuring, full auto-inference export const Badge = c( ({ label, variant }) => ( <host shadowDom> <span class={variant}>{label}</span> </host> ), { props: { label: String, variant: { type: String, reflect: true, value: () => "info" } } } ); // Internally mutable prop - useProp with explicit generic export const Counter = c( ({ label }) => { const [count, setCount] = useProp<number>("count"); return ( <host> <button onclick={() => setCount(count + 1)}> {label}: {count} </button> </host> ); }, { props: { label: String, count: { type: Number, value: () => 0 } } } ); // Grouped private state - useObjectState export const SearchBar = c(() => { const [state, setState] = useObjectState({ query: "", filter: "all" }); return ( <host shadowDom> <input value={state.query} oninput={(e) => setState({ query: e.currentTarget.value as string }) } /> </host> ); }); ``` **Forbidden:** ```tsx // useProp for read-only - use direct destructuring instead const [label] = useProp("label"); // Multiple useState for related values - use useObjectState const [query, setQuery] = useState(""); const [filter, setFilter] = useState("all"); // useMemo on small lists - direct computation is cheaper const filtered = useMemo(() => items.filter(isActive), [items]); // Correct: const filtered = items.filter(isActive); const done = items.length - filtered.length; // math deduction, no second iteration ``` --- ## 5. JSX ### Handlers — Always Inline The TSX compiler automatically infers the event target type when handlers are **inline**. ```ts // With inline handler, Atomico JSX infers automatically: // oninput -> e: { currentTarget: HTMLInputElement } // onchange -> e: { currentTarget: HTMLSelectElement } // e.currentTarget.value -> string | number requires `as string` when passing to setState ``` ```tsx // Inline: e.currentTarget typed as HTMLInputElement - zero manual castings <input oninput={(e) => setState({ query: e.currentTarget.value as string })} />; // Extracted: forces `any`, breaks auto-inference const handleInput = (e: any) => setState({ query: e.currentTarget.value }); <input oninput={handleInput} />; ``` > **Rule**: Single-use handlers go inline. Extract only if the **exact same function** is > shared across multiple elements. ### `useRef` — no null initialization
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub