Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a8de627
feat: make Light DOM the default rendering mode
mohamedmansour Aug 6, 2026
1d2d411
fix: compare component styles semantically
mohamedmansour Aug 6, 2026
9029955
fix: preserve Light DOM hydration and styles
mohamedmansour Aug 6, 2026
1729c72
fix: place routed styles in owning ShadowRoot
mohamedmansour Aug 6, 2026
d7f9fb5
feat: make Light DOM the component invariant
mohamedmansour Aug 6, 2026
4ad8c8c
fix: eliminate repeated style metadata work
mohamedmansour Aug 6, 2026
b9eecae
fix: remove obsolete fixture DOM option
mohamedmansour Aug 6, 2026
a84e8ac
fix: restore plugin CSS and harden style closures
mohamedmansour Aug 7, 2026
1dad32c
Report component style delivery as plugin context
mohamedmansour Aug 7, 2026
1a98024
Pin the Light CSS scope shape to its measured optimum
mohamedmansour Aug 7, 2026
d2638ef
Pin the CSS module importmap inside its route host
mohamedmansour Aug 7, 2026
5fd70c0
Move authored CSS into the diagnostic cache instead of cloning it
mohamedmansour Aug 7, 2026
9319bf9
Correct the measured verdict on Light CSS scope alternatives
mohamedmansour Aug 8, 2026
ce766e4
Scope Light DOM CSS with build-time markers instead of @scope
mohamedmansour Aug 8, 2026
7d9c2cb
Keep the @scope enclosure for components that render opaque HTML
mohamedmansour Aug 8, 2026
cbfb67c
Never qualify an at-rule prelude when stamping Light CSS
mohamedmansour Aug 9, 2026
a4928a8
Cover the Light CSS stamper with input-only invariants
mohamedmansour Aug 10, 2026
2fd748f
Add shared CSS bundle chunking
mohamedmansour Aug 10, 2026
881b7a0
Fix Light events across Shadow boundaries
mohamedmansour Aug 10, 2026
d6ac74b
Close the code review findings on Light DOM and CSS bundling
mohamedmansour Aug 10, 2026
1bf51ba
Add --css-bundle to the commerce example server
mohamedmansour Aug 10, 2026
bedae70
Document content-hashed filenames for CSS bundle chunks
mohamedmansour Aug 11, 2026
00d2bc7
Align lazy-hydration fixtures with the authored Shadow rule
mohamedmansour Aug 11, 2026
dc47479
Hoist active route styles into head
mohamedmansour Aug 14, 2026
13e8543
Restore Shadow-default mixed DOM strategy
mohamedmansour Aug 18, 2026
c5e2455
Remove duplication introduced by the mixed DOM pivot
mohamedmansour Aug 18, 2026
06e6860
feat: make Light DOM CSS authored and global
mohamedmansour Aug 20, 2026
7b09019
fix: tighten global Light CSS validation
mohamedmansour Aug 20, 2026
c9df99f
feat: let bare templates opt into Light DOM
mohamedmansour Aug 20, 2026
ae35e34
Fix component stylesheet check regressions
mohamedmansour Aug 21, 2026
ddc7ab5
Fix Shadow DOM E2E expectations
mohamedmansour Aug 21, 2026
af2f90f
fix: address CSS delivery review feedback
mohamedmansour Aug 21, 2026
1ce9504
Fix PR workflow indentation
mohamedmansour Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/skills/testing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,16 @@ Create `webui.config.json` to override build options:
{ "css": "module" }
```

### Light-DOM fixtures

The pipeline always produces shadow DOM. For light-DOM hydration tests, use manual template registration with `registerCompiledTemplate` from `@microsoft/webui-test-support` and a hand-written `fixture.html`. See `fixtures/light-dom/` for the pattern.
### Light and Shadow fixtures

Ordinary unwrapped fixture components exercise the Shadow default. Set
`"dom": "light"` in `webui.config.json` to exercise global Light rendering; a
fixture component requiring Shadow inside that build must author a sole
top-level `<template shadowrootmode="open">`. A sole bare top-level
`<template>` explicitly exercises Light even in the default Shadow fixture.
See `fixtures/dom-default/` and
`fixtures/light-dom/` for both paths through the real pipeline. Reserve manual
`registerCompiledTemplate` fixtures for raw template-metadata scenarios.

### Running framework E2E tests

Expand Down
124 changes: 62 additions & 62 deletions .github/skills/webui-dev/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,62 @@
---
name: webui-dev
description: Build interactive WebUI apps with compiled-template hydration, template syntax, component patterns, and CLI usage.
---
# WebUI App Development
Use this skill when building or modifying WebUI applications.
## Critical rules (memorize these)
1. **The template is the UI.** All structure lives in `.html`. Never `document.createElement`, `innerHTML`, `insertAdjacentHTML`, or `appendChild`. Show/hide with `<if>`, repeat with `<for>`. The only exception is mounting a lazily loaded component.
2. **CSS owns all styling and animation.** Never `el.style.x =`, `classList.toggle`, or `adoptedStyleSheets`. Bind `?data-active="{{expr}}"` and select `[data-active]` in CSS. Animate with `transition`, `@keyframes`, `@starting-style` - never `element.animate()` or a JS animation library.
3. **JavaScript is opt-in.** A component needs **no** `.ts` file unless it has an `@event`, a `w-ref` for an imperative API, a lifecycle hook, a fetch, or a public method API. `WebUIElement`, `@observable`, and `@attr` are optional - add them only when TypeScript reads/writes the value or it is public API. Otherwise the value belongs in the server state JSON.
4. **Use the web platform.** `<dialog>` over a div modal, `popover` over a JS dropdown, `<details>` over a JS accordion. Prefer `:has()`, `@container`, `color-mix()`, `light-dark()`, `content-visibility`.
5. **Every template binding must exist in the server state JSON.** Missing keys render empty, silently.
6. **HTML, CSS, TypeScript are separate files.** No JSX. No CSS-in-JS. No JS in templates.
7. **The `<template>` tag is optional.** The build tool auto-injects it. Include it only for root host events (`@custom-event` on the component root, which also catch events targeted at the host element itself).
8. **Components inside `<for>` loops do NOT inherit loop variables.** Pass data via attributes.
9. **Text bindings are path lookups; comparisons belong in conditions.** `{{count}}` and `{{user.name}}` resolve a dotted state path - nothing else. `{{count > 0}}` is looked up as a key literally named `count > 0` and renders empty. Comparisons go in `<if condition="count > 0">` or `?active="{{section == 'guide'}}"`. Operators: `==`, `!=`, `<`, `>`, `<=`, `>=`, `&&`, `||`, `!`. **Forbidden everywhere:** ternary (`? :`), function calls, arithmetic (`items.length - 1` resolves as a path and silently fails - send a precomputed `lastIndex`), mixing `&&` with `||`, more than 5 logical operators.
10. **`w-ref` requires braces.** `w-ref="{inputEl}"`, never `w-ref="inputEl"` - non-braced fails the build with `invalid-w-ref`. Use it only for imperative APIs (focus, scroll, `showModal`), never to read state.
11. **`@attr({ mode: 'boolean' })` for true/false.** Present = true, absent = false. Never use string `"false"`.
## Quick reference
Most components need only HTML and CSS:
```html
<!-- user-card.html - no .ts file -->
<h2>{{user.name}}</h2>
<if condition="user.isAdmin"><span class="badge">Admin</span></if>
```
Add a class only when something interactive happens:
```typescript
import { WebUIElement, attr, observable } from '@microsoft/webui-framework';
export class MyComponent extends WebUIElement {
@attr label = ''; // set by a parent template
@attr({ mode: 'boolean' }) disabled = false;
@observable count = 0; // mutated by increment()
inputEl!: HTMLInputElement; // populated by w-ref="{inputEl}"
increment(): void { this.count += 1; }
onKeydown(e: KeyboardEvent): void { if (e.key === 'Enter') this.submit(); }
}
MyComponent.define('my-component');
```
```bash
webui build ./src --out ./dist --plugin=webui
webui serve ./src --state ./data/state.json --plugin=webui --watch
```
## Full reference
The complete guide covering all template syntax, styling and animation rules, anti-patterns, routing, and a pre-flight checklist:
**[docs/ai/SKILL.md](/docs/ai/SKILL.md)**
Read that file before generating any WebUI code.
---
name: webui-dev
description: Build interactive WebUI apps with compiled-template hydration, template syntax, component patterns, and CLI usage.
---

# WebUI App Development

Use this skill when building or modifying WebUI applications.

## Critical rules (memorize these)

1. **The template is the UI.** All structure lives in `.html`. Never `document.createElement`, `innerHTML`, `insertAdjacentHTML`, or `appendChild`. Show/hide with `<if>`, repeat with `<for>`. The only exception is mounting a lazily loaded component.
2. **CSS owns all styling and animation.** Never `el.style.x =`, `classList.toggle`, or `adoptedStyleSheets`. Bind `?data-active="{{expr}}"` and select `[data-active]` in CSS. Animate with `transition`, `@keyframes`, `@starting-style` - never `element.animate()` or a JS animation library.
3. **JavaScript is opt-in.** A component needs **no** `.ts` file unless it has an `@event`, a `w-ref` for an imperative API, a lifecycle hook, a fetch, or a public method API. `WebUIElement`, `@observable`, and `@attr` are optional - add them only when TypeScript reads/writes the value or it is public API. Otherwise the value belongs in the server state JSON.
4. **Use the web platform.** `<dialog>` over a div modal, `popover` over a JS dropdown, `<details>` over a JS accordion. Prefer `:has()`, `@container`, `color-mix()`, `light-dark()`, `content-visibility`.
5. **Every template binding must exist in the server state JSON.** Missing keys render empty, silently.
6. **HTML, CSS, TypeScript are separate files.** No JSX. No CSS-in-JS. No JS in templates.
7. **Unwrapped components default to Shadow; `--dom light` makes them global Light DOM while authored open wrappers stay Shadow.** A sole bare top-level `<template>` explicitly selects Light and is unwrapped even under the Shadow fallback. Light CSS uses ordinary selectors in its owning CSS tree. Use one sole top-level `<template shadowrootmode="open">` when the component needs native `<slot>` projection, Shadow encapsulation, CSS-heavy frequent restyling, root host events, or Shadow-only selectors such as `:host`. A `<slot>` and `:host` fail only in an effective Light component.
8. **Components inside `<for>` loops do NOT inherit loop variables.** Pass data via attributes.
9. **Text bindings are path lookups; comparisons belong in conditions.** `{{count}}` and `{{user.name}}` resolve a dotted state path - nothing else. `{{count > 0}}` is looked up as a key literally named `count > 0` and renders empty. Comparisons go in `<if condition="count > 0">` or `?active="{{section == 'guide'}}"`. Operators: `==`, `!=`, `<`, `>`, `<=`, `>=`, `&&`, `||`, `!`. **Forbidden everywhere:** ternary (`? :`), function calls, arithmetic (`items.length - 1` resolves as a path and silently fails - send a precomputed `lastIndex`), mixing `&&` with `||`, more than 5 logical operators.
10. **`w-ref` requires braces.** `w-ref="{inputEl}"`, never `w-ref="inputEl"` - non-braced fails the build with `invalid-w-ref`. Use it only for imperative APIs (focus, scroll, `showModal`), never to read state.
11. **`@attr({ mode: 'boolean' })` for true/false.** Present = true, absent = false. Never use string `"false"`.

## Quick reference

Most components need only HTML and CSS:

```html
<!-- user-card.html - no .ts file -->
<h2>{{user.name}}</h2>
<if condition="user.isAdmin"><span class="badge">Admin</span></if>
```

Add a class only when something interactive happens:

```typescript
import { WebUIElement, attr, observable } from '@microsoft/webui-framework';

export class MyComponent extends WebUIElement {
@attr label = ''; // set by a parent template
@attr({ mode: 'boolean' }) disabled = false;
@observable count = 0; // mutated by increment()
inputEl!: HTMLInputElement; // populated by w-ref="{inputEl}"

increment(): void { this.count += 1; }
onKeydown(e: KeyboardEvent): void { if (e.key === 'Enter') this.submit(); }
}
MyComponent.define('my-component');
```

```bash
webui build ./src --out ./dist --plugin=webui
webui serve ./src --state ./data/state.json --plugin=webui --watch
```

## Full reference

The complete guide covering all template syntax, styling and animation rules, anti-patterns, routing, and a pre-flight checklist:

**[docs/ai/SKILL.md](/docs/ai/SKILL.md)**

Read that file before generating any WebUI code.
Loading
Loading