算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
frontend-lit【设计与创意】Use this skill when the user says 'Lit', 'LitElement', 'lit-html', 'web component Lit', 'reactive element', '@lit/reactive-element', 'Lit SSR', 'Lit component', 'Lit decorator', 'Lit reactive property'. This skill enforces: LitElement base class with reactive properties, declarative templates with lit-html, shadow DOM encapsulation by default, typed …
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Build standard web components with Lit: reactive properties, shadow DOM templates, typed events, and SSR-compatible rendering.
Exact user phrases: "Lit", "LitElement", "lit-html", "web component Lit", "reactive element", "@lit/reactive-element", "Lit SSR", "Lit component", "Lit decorator", "Lit reactive property".
Before activating, verify:
No file output. Produces component design, property configuration, template patterns, and SSR setup as text.
Component: {name} — {purpose}
Properties: {reactive attributes/properties}
Template: {shadow DOM structure}
Events: {CustomEvent dispatch}
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output.
2560 tokens.
| Approach | Trade-off | When to Use |
|---|---|---|
| LitElement | Full API (render, styles, lifecycle) | Most components |
| ReactiveElement | No template system, minimal | When bundle is critical |
| @lit-labs/ssr | Server rendering + hydration | SSR-required projects |
| @lit/react | React wrapper generation | React design systems |
Do you need the full LitElement API (render, styles, lifecycle)?
Yes -> LitElement (default)
No -> Is bundle size critical?
Yes -> ReactiveElement (no template system, manual DOM management)
No -> LitElement for maintainability
Do you need SSR?
Yes -> LitElement + @lit-labs/ssr
No -> LitElement (default)
Is the property part of the public API?
Yes -> @property() — exposes as HTML attribute, triggers update
No -> @state() — internal only, triggers update but no attribute
How should the attribute be named?
CamelCase property -> 'my-prop' attribute (auto kebab-case)
Need custom name -> attribute: 'custom-name'
No attribute needed -> attribute: false
Should the attribute reflect changes back to DOM?
Yes -> reflect: true (useful for CSS attribute selectors)
No -> reflect: false (default, better performance)
How should styles be scoped?
Shadow DOM -> static styles in LitElement (default, best encapsulation)
Light DOM -> override createRenderRoot() for shared styles
Adopted stylesheets -> adoptedStyleSheets for performance (multiple instances)
CSS custom properties -> ::part() for component customization API
import { LitElement, html, css } from 'lit'
import { property, state } from 'lit/decorators.js'
export class MyCounter extends LitElement {
static styles = css`
:host { display: block; padding: 1rem; }
button { cursor: pointer; }
`
@property({ type: Number }) initial = 0
@state() private count = 0
connectedCallback() {
super.connectedCallback()
this.count = this.initial
}
render() {
return html`
<p>Count: ${this.count}</p>
<button @click=${() => this.count++}>+</button>
<button @click=${() => this.count--}>-</button>
`
}
}
import { LitElement, html } from 'lit'
import { property } from 'lit/decorators.js'
export class MyDropdown extends LitElement {
@property({ type: Array }) options: string[] = []
@property({ type: String }) value = ''
private select(option: string) {
this.value = option
this.dispatchEvent(new CustomEvent('select', {
detail: option,
bubbles: true,
composed: true,
}))
}
render() {
return html`
<div @click=${(e: Event) => {
const target = e.target as HTMLElement
if (target.dataset.value) this.select(target.dataset.value)
}}>
${this.options.map(o => html`
<div data-value=${o} class=${o === this.value ? 'active' : ''}>
${o}
</div>
`)}
</div>
`
}
}
import { ReactiveController, ReactiveControllerHost } from 'lit'
export class ResizeController implements ReactiveController {
private entries: ResizeObserverEntry[] = []
private observer: ResizeObserver | null = null
constructor(private host: ReactiveControllerHost) {
this.host.addController(this)
}
hostConnected() {
this.observer = new ResizeObserver(entries => {
this.entries = entries
this.host.requestUpdate()
})
this.observer.observe(this.host as HTMLElement)
}
hostDisconnected() {
this.observer?.disconnect()
}
}
@state() private visible = false
@state() private items: Item[] = []
get total() { return this.items.reduce((s, i) => s + i.price, 0) }
get count() { return this.items.length }
Controllers encapsulate stateful behavior (resize observers, intersection observers, form state) and can be reused across components.
repeat() directive uses key-based identity for efficient list updates.@property({ hasChanged }) to customize change detection.shouldUpdate() to skip renders when inputs haven't meaningfully changed.adoptedStyleSheets for shared styles across many component instances.lit package includes lit-html and LitElement in one import.lit/directives/*.@lit-labs/ssr for Node.js rendering, @lit-labs/ssr-client for hydration.@lit/react creates React wrappers for Lit components.@lit/localize for internationalization.import { fixture, assert } from '@open-wc/testing'
import './my-element.js'
describe('MyElement', () => {
it('renders with default properties', async () => {
const el = await fixture('<my-element></my-element>')
assert.equal(el.shadowRoot?.querySelector('button')?.textContent?.trim(), 'Click me')
})
it('reacts to property changes', async () => {
const el = await fixture<MyElement>('<my-element></my-element>')
el.count = 5
await el.updateComplete
assert.include(el.shadowRoot?.textContent ?? '', '5')
})
})
it('dispatches custom event on button click', async () => {
const el = await fixture<MyElement>('<my-element></my-element>')
const handler = sinon.spy()
el.addEventListener('my-event', handler)
el.shadowRoot?.querySelector('button')?.click()
assert.isTrue(handler.calledOnce)
assert.equal(handler.firstCall.args[0].detail, 'clicked')
})
@open-wc/testing for test helpers (fixture, assert, waitFor).el.updateComplete before asserting after property changes.el.shadowRoot.querySelector.| Vanilla | Lit |
|---|---|
class MyEl extends HTMLElement | class MyEl extends LitElement |
observedAttributes() | @property() decorator |
attributeChangedCallback() | willUpdate() / updated() |
connectedCallback() + manual render | render() auto-called |
this.innerHTML = template | return html\...`` |
| Manual style attachment | static styles = css\...`` |
Migration order: 1) Change extends to LitElement, 2) Replace observedAttributes with @property, 3) Replace innerHTML with render(), 4) Add static styles, 5) Convert lifecycle methods.
| React Concept | Lit Equivalent |
|---|---|
useState | @state() property |
useEffect | updated() / willUpdate() |
useMemo | shouldUpdate() for control |
| JSX | html tagged template literal |
| Props | @property() decorator |
| Event callbacks | CustomEvent dispatch |
super.connectedCallback(), super.disconnectedCallback(), super.updated().super.connectedCallback().| Aspect | Lit | Stencil | Vanilla WC |
|---|---|---|---|
| Bundle size | ~5KB | ~8KB | 0KB |
| Rendering | lit-html (template literal) | JSX (compiled) | Manual |
| Reactivity | @property/@state decorators | @Prop/@State decorators | attributeChangedCallback |
| SSR | @lit-labs/ssr | @stencil/core (SSR) | Manual |
| TypeScript | Full support | Required | Optional |
| Directive | Purpose |
|---|---|
ifDefined | Render attr only if defined |
classMap | Toggle CSS classes |
styleMap | Inline styles |
repeat | Keyed list rendering |
when | Conditional rendering |
until | Promise placeholder |
live | Attribute always matches live value |
keyed | Force DOM reuse |
guard | Memoize template parts |
cache | Cache DOM across conditionals |
| Method | Purpose |
|---|---|
connectedCallback() | Element added to DOM |
disconnectedCallback() | Element removed from DOM |
willUpdate(changed) | Before render |
update(changed) | Before render (read prop values) |
render() | Return lit-html template |
updated(changed) | After render |
firstUpdated(changed) | First render only |
shouldUpdate(changed) | Control whether render fires |
@lit/reactive-element — base class without template@lit-labs/ssr — server-side rendering@lit/react — React wrapper generation@lit/localize — i18nlit-analyzer — type checking@open-wc/testing — testing utilities@web/test-runner — test runner@lit/react creates wrappers<svelte:options tag="my-el" />createRenderRoot() override for light DOM only when unavoidable.detail — always set bubbles and composed.styles — avoid global leak from shadow DOM.repeat, classMap, ifDefined, when) over imperative DOM.No artifact produced. Next skill: frontend-universal-web-components for vanilla custom elements and cross-framework compatibility. Carry forward: LitElement patterns, reactive property config, shadow DOM conventions.
function createModule<T>(config: ModuleConfig): T { const dependencies = initializeDependencies(config); const module = new Module(dependencies); module.hooks.onInit(); return module as T; }
class ConfigBuilder { private config: AppConfig = new AppConfig(); withDatabase(url: string): ConfigBuilder { ... } withCache(ttl: number): ConfigBuilder { ... } withLogging(level: string): ConfigBuilder { ... } build(): AppConfig { return this.config; } }
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% | Critical | Rollback or fix |
| p95 latency | > 500ms | Warning | Profile and optimize |
| Uptime | < 99.9% | Critical | Investigate infrastructure |
| Memory usage | > 80% | Warning | Check for leaks |
| CPU usage | > 80% | Warning | Scale up or optimize |
Does component render dynamic content from properties?
├── No → Pure template with static styles
└── Yes → Are properties primitive values?
├── Yes → @property() with type converter
└── No → @property() with custom converter or @state()
Does the component need to render children/slots?
├── Yes → Use <slot> elements in shadow DOM
└── No → Closed shadow DOM for encapsulated components
Does the component need to react to external state?
├── No → Static component, no reactive updates needed
└── Yes → Is the state from DOM events?
├── Yes → @eventOptions + this.requestUpdate()
└── No → Is the state coming from an observable?
├── Yes → Use LitElement + rxjs with connect() pattern
└── No → Use @property decorators + willUpdate lifecycle