Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "major",
"comment": "Remove ElementStyles.withBehaviors, CSS style behaviors, and CSS bindings in fast-element.",
"packageName": "@microsoft/fast-element",
"email": "7559015+janechu@users.noreply.github.com",
"dependentChangeType": "none"
}
2 changes: 1 addition & 1 deletion packages/fast-element/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ See [ARCHITECTURE_UPDATES.md](./ARCHITECTURE_UPDATES.md) for more detail.

**Files**: `src/styles/css.ts`, `src/styles/element-styles.ts`, `src/styles/css-directive.ts`

The `css` tag (analogous to `html`) builds `ElementStyles` objects. During `ElementController.connect()`, styles are applied to the element's shadow root either via `adoptedStylesheets` (preferred) or an appended `<style>` node, depending on platform support. Styles can also contain dynamic `CSSDirective`s (e.g., CSS custom property bindings) that register as `HostBehavior`s and connect/disconnect alongside the element.
The `css` tag (analogous to `html`) builds `ElementStyles` objects. During `ElementController.connect()`, styles are applied to the element's shadow root either via `adoptedStylesheets` (preferred) or an appended `<style>` node, depending on platform support. `CSSDirective`s can contribute additional static CSS during template composition, but runtime CSS bindings and style-attached `HostBehavior`s are not supported. Arbitrary runtime style toggling is handled through `ElementController.addStyles()` / `removeStyles()`; `ElementStyles` itself is a static container.

---

Expand Down
32 changes: 28 additions & 4 deletions packages/fast-element/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,33 @@ This is a **breaking change** for SSR output format. Any system that produces or
4. Add `await` to `compose()` calls that chain `.define()`:

```typescript
// Before
FASTElementDefinition.compose(MyElement, options).define();
// Before
FASTElementDefinition.compose(MyElement, options).define();

// After
(await FASTElementDefinition.compose(MyElement, options)).define();
// After
(await FASTElementDefinition.compose(MyElement, options)).define();
```

## Dynamic stylesheet behaviors (v3)

### Removed API

| Removed | Replacement |
|---|---|
| `ElementStyles.withBehaviors()` | Move the runtime condition into the element and call `this.$fastController.addStyles()` / `this.$fastController.removeStyles()` directly. |
| `ElementStyles.behaviors` | Move any runtime behavior out of the stylesheet and into the element or controller lifecycle. |
| CSS bindings in `css` (for example ``css`color: ${x => x.color}```) | Move the dynamic value into the element and update classes, attributes, or inline styles from element code. |
| `CSSDirective.createCSS(add)` | Update directives to implement `createCSS()` and return only static CSS content. |

### Changed behavior

- `css` and `css.partial()` no longer compose `HostBehavior`s.
- `css` no longer accepts function or `Binding` interpolations.
- `ElementStyles` is now a fully static style container.

### Migration steps

1. Keep the conditional `ElementStyles` in a separate `css` value.
2. Move the external listener or condition (for example `matchMedia()` or an app event subscription) into the element lifecycle.
3. Call `this.$fastController.addStyles(styles)` when the condition is active and `this.$fastController.removeStyles(styles)` when it is inactive or during cleanup.
4. If you previously interpolated bindings or behavior-producing directives into `css`, replace them with element state and standard DOM or controller updates.
12 changes: 12 additions & 0 deletions packages/fast-element/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ importing `@microsoft/fast-element`.

Bundle sizes for each tree-shakeable export are tracked in [`SIZES.md`](./SIZES.md) and regenerated on every build. See the [Export Sizes](https://www.fast.design/docs/3.x/resources/export-sizes/) documentation page for the latest numbers.

## Dynamic Style Application

When runtime state or external signals need to add or remove styles, create the
`ElementStyles` with `css` and toggle it through
`this.$fastController.addStyles()` / `this.$fastController.removeStyles()` from
the element lifecycle or change handlers.

`css` templates remain static style definitions. Runtime CSS bindings and
behavior-producing CSS directives are no longer supported; keep the condition on
the element and toggle a separate `ElementStyles` instance through the
controller when styles need to change.

## Prerendered Content Optimization

When a FAST element connects and already has an existing shadow root (from server-side rendering or declarative shadow DOM), `ElementController` automatically detects this. The `isPrerendered` property on the controller is a `Promise<boolean>` that resolves to `true` after prerendered content has been hydrated, or `false` when the component is client-side rendered. This enables several optimizations:
Expand Down
14 changes: 7 additions & 7 deletions packages/fast-element/SIZES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ Bundle sizes for `@microsoft/fast-element` exports.

| Export | Minified | Gzip | Brotli |
|--------|----------|------|--------|
| CDN Rollup Bundle | 64.76 KB | 19.24 KB | 17.23 KB |
| FASTElement | 23.69 KB | 7.41 KB | 6.68 KB |
| CDN Rollup Bundle | 63.05 KB | 18.81 KB | 16.84 KB |
| FASTElement | 23.29 KB | 7.32 KB | 6.60 KB |
| Updates | 3.45 KB | 1.43 KB | 1.21 KB |
| Observable | 7.96 KB | 2.94 KB | 2.61 KB |
| observable | 8.00 KB | 2.95 KB | 2.63 KB |
| Observable | 7.96 KB | 2.94 KB | 2.62 KB |
| observable | 8.00 KB | 2.95 KB | 2.62 KB |
| attr | 3.39 KB | 1.37 KB | 1.13 KB |
| children | 5.99 KB | 2.29 KB | 2.01 KB |
| css | 11.16 KB | 4.03 KB | 3.61 KB |
| children | 5.99 KB | 2.29 KB | 2.02 KB |
| css | 4.47 KB | 1.78 KB | 1.54 KB |
| ref | 4.96 KB | 1.96 KB | 1.71 KB |
| slotted | 5.78 KB | 2.23 KB | 1.95 KB |
| volatile | 8.05 KB | 2.97 KB | 2.64 KB |
| volatile | 8.05 KB | 2.97 KB | 2.63 KB |
| when | 2.40 KB | 978 B | 786 B |
| html | 27.13 KB | 8.88 KB | 7.96 KB |
| repeat | 30.79 KB | 9.81 KB | 8.82 KB |
35 changes: 4 additions & 31 deletions packages/fast-element/docs/api-report.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ export interface Accessor {
setValue(source: any, value: any): void;
}

// @public
export type AddBehavior = (behavior: HostBehavior<HTMLElement>) => void;

// @public
export type AddViewBehaviorFactory = (factory: ViewBehaviorFactory) => string;

Expand Down Expand Up @@ -185,30 +182,9 @@ export interface ContentView {
// @public
export const css: CSSTemplateTag;

// @public
export class CSSBindingDirective implements HostBehavior, Subscriber, CSSDirective, BindingDirective {
constructor(dataBinding: Binding, targetAspect: string);
addedCallback(controller: HostController<HTMLElement & {
$cssBindings: Map<CSSBindingDirective, CSSBindingEntry>;
}>): void;
connectedCallback(controller: HostController<HTMLElement & {
$cssBindings: Map<CSSBindingDirective, CSSBindingEntry>;
}>): void;
createCSS(add: AddBehavior): ComposableStyles;
// (undocumented)
readonly dataBinding: Binding;
// @internal
handleChange(_: any, observer: ExpressionObserver): void;
removedCallback(controller: HostController<HTMLElement & {
$cssBindings: Map<CSSBindingDirective, CSSBindingEntry>;
}>): void;
// (undocumented)
readonly targetAspect: string;
}

// @public
export interface CSSDirective {
createCSS(add: AddBehavior): ComposableStyles;
createCSS(): ComposableStyles;
}

// @public
Expand All @@ -227,12 +203,12 @@ export interface CSSDirectiveDefinition<TType extends Constructable<CSSDirective
}

// @public
export type CSSTemplateTag = (<TSource = any, TParent = any>(strings: TemplateStringsArray, ...values: CSSValue<TSource, TParent>[]) => ElementStyles) & {
partial<TSource = any, TParent = any>(strings: TemplateStringsArray, ...values: CSSValue<TSource, TParent>[]): CSSDirective;
export type CSSTemplateTag = ((strings: TemplateStringsArray, ...values: CSSValue[]) => ElementStyles) & {
partial(strings: TemplateStringsArray, ...values: CSSValue[]): CSSDirective;
};

// @public
export type CSSValue<TSource, TParent = any> = Expression<TSource, any, TParent> | Binding<TSource, any, TParent> | ComposableStyles | CSSDirective;
export type CSSValue = ComposableStyles | CSSDirective;

// @public
export function customElement(nameOrDef: string | PartialFASTElementDefinition): (type: Constructable<HTMLElement>) => void;
Expand Down Expand Up @@ -346,7 +322,6 @@ export class ElementStyles {
constructor(styles: ReadonlyArray<ComposableStyles>);
// @internal (undocumented)
addStylesTo(target: StyleTarget): void;
readonly behaviors: ReadonlyArray<HostBehavior<HTMLElement>> | null;
// @internal (undocumented)
isAttachedTo(target: StyleTarget): boolean;
static normalize(styles: ComposableStyles | ComposableStyles[] | undefined): ElementStyles | undefined;
Expand All @@ -357,7 +332,6 @@ export class ElementStyles {
// (undocumented)
readonly styles: ReadonlyArray<ComposableStyles>;
static readonly supportsAdoptedStyleSheets: boolean;
withBehaviors(...behaviors: HostBehavior<HTMLElement>[]): this;
withStrategy(Strategy: ConstructibleStyleStrategy): this;
}

Expand Down Expand Up @@ -1114,7 +1088,6 @@ export function when<TSource = any, TReturn = any, TParent = any>(condition: Exp
// dist/dts/components/fast-element.d.ts:60:5 - (ae-forgotten-export) The symbol "define" needs to be exported by the entry point index.d.ts
// dist/dts/components/fast-element.d.ts:61:5 - (ae-forgotten-export) The symbol "compose" needs to be exported by the entry point index.d.ts
// dist/dts/components/fast-element.d.ts:62:5 - (ae-forgotten-export) The symbol "from" needs to be exported by the entry point index.d.ts
// dist/dts/styles/css-binding-directive.d.ts:35:9 - (ae-forgotten-export) The symbol "CSSBindingEntry" needs to be exported by the entry point index.d.ts

// (No @packageDocumentation comment for this package)

Expand Down
Loading
Loading