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.
ソース情報
- リポジトリ
- atomicojs/atomico
- ソースの最終更新活動
- 2026年7月18日 21:24
- 検出された SKILL.md の言語
- 英語
- スター
- 1,278
- フォーク
- 44
インストール方法
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
ソースファイルを確認
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
ファイルエクスプローラー
13 ファイルSKILL.md を表示中
SKILL.md
ソースの指示 · 読み取り専用プレビュー- 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: .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
```ts
declare function useRef<T>(): { current: T | undefined };
```
```tsx
// Parameterless - typed as T | undefined, no null needed
const inputRef = useRef<HTMLInputElement>();
if (inputRef.current) inputRef.current.focus(); // guard is mandatory
// Reference to another Atomico constructor
const btnRef = useRef<typeof MyButton>();
GitHubで見るこの SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る