CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-microsoft--fast-element

A library for constructing Web Components

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

css-styling.mddocs/

CSS Styling

Composable CSS system with tagged template literals, design tokens, and efficient style application strategies for building maintainable and performant component styles.

Capabilities

CSS Template Tag

Tagged template literal function for creating reactive CSS styles with interpolation, composition, and behavior attachment.

/**
 * Transforms a template literal string into styles
 * @param strings - The string fragments that are interpolated with the values
 * @param values - The values that are interpolated with the string fragments
 * @returns An ElementStyles instance
 */
function css<TSource = any, TParent = any>(
  strings: TemplateStringsArray,
  ...values: CSSValue<TSource, TParent>[]
): ElementStyles;

/**
 * CSS template tag interface with utilities
 */
interface CSSTemplateTag {
  <TSource = any, TParent = any>(
    strings: TemplateStringsArray,
    ...values: CSSValue<TSource, TParent>[]
  ): ElementStyles;
  
  /**
   * Creates partial CSS directive for composition
   * @param strings - The string fragments
   * @param values - The interpolated values
   * @returns A CSSDirective for composition
   */
  partial<TSource = any, TParent = any>(
    strings: TemplateStringsArray,
    ...values: CSSValue<TSource, TParent>[]
  ): CSSDirective;
}

/**
 * Types that can be interpolated into CSS templates
 */
type CSSValue<TSource, TParent = any> =
  | Expression<TSource, any, TParent>
  | Binding<TSource, any, TParent>
  | ComposableStyles
  | CSSDirective;

Usage Examples:

import { FASTElement, customElement, css, attr } from "@microsoft/fast-element";

// Basic CSS with design tokens
const baseStyles = css`
  :host {
    display: block;
    padding: var(--design-unit) * 2px;
    background: var(--neutral-fill-rest);
    border-radius: var(--corner-radius);
  }
  
  .content {
    color: var(--neutral-foreground-rest);
    font-family: var(--body-font);
  }
`;

// CSS with reactive bindings
const dynamicStyles = css<MyButton>`
  :host {
    opacity: ${x => x.disabled ? 0.5 : 1};
    cursor: ${x => x.disabled ? 'not-allowed' : 'pointer'};
    background: ${x => x.variant === 'primary' 
      ? 'var(--accent-fill-rest)' 
      : 'var(--neutral-fill-rest)'
    };
  }
  
  :host(:hover) {
    background: ${x => x.variant === 'primary'
      ? 'var(--accent-fill-hover)'
      : 'var(--neutral-fill-hover)'
    };
  }
`;

// CSS composition
const buttonBase = css`
  :host {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 8px 16px;
    border: 1px solid transparent;
    border-radius: 4px;
    font-size: 14px;
    cursor: pointer;
    transition: all 0.2s ease;
  }
`;

const primaryButton = css`
  ${buttonBase}
  
  :host {
    background: var(--accent-fill-rest);
    color: var(--foreground-on-accent-rest);
  }
  
  :host(:hover) {
    background: var(--accent-fill-hover);
  }
`;

@customElement({
  name: "my-button",
  styles: dynamicStyles
})
export class MyButton extends FASTElement {
  @attr disabled: boolean = false;
  @attr variant: "primary" | "secondary" = "primary";
}

ElementStyles Class

Core styling class that manages CSS composition, behavior attachment, and style application strategies.

/**
 * Represents styles that can be applied to a custom element
 */
class ElementStyles {
  /** The styles that will be associated with elements */
  readonly styles: ReadonlyArray<ComposableStyles>;
  
  /** The behaviors associated with this set of styles */
  readonly behaviors: ReadonlyArray<HostBehavior<HTMLElement>> | null;
  
  /** Gets the StyleStrategy associated with these element styles */
  readonly strategy: StyleStrategy;
  
  /**
   * Creates an instance of ElementStyles
   * @param styles - The styles that will be associated with elements
   */
  constructor(styles: ReadonlyArray<ComposableStyles>);
  
  /**
   * Associates behaviors with this set of styles
   * @param behaviors - The behaviors to associate
   * @returns This ElementStyles instance for chaining
   */
  withBehaviors(...behaviors: HostBehavior<HTMLElement>[]): this;
  
  /**
   * Sets the strategy that handles adding/removing these styles for an element
   * @param Strategy - The strategy type to use
   * @returns This ElementStyles instance for chaining
   */
  withStrategy(Strategy: ConstructibleStyleStrategy): this;
  
  /**
   * Sets the default strategy type to use when creating style strategies
   * @param Strategy - The strategy type to construct
   */
  static setDefaultStrategy(Strategy: ConstructibleStyleStrategy): void;
  
  /**
   * Normalizes a set of composable style options
   * @param styles - The style options to normalize
   * @returns A singular ElementStyles instance or undefined
   */
  static normalize(
    styles: ComposableStyles | ComposableStyles[] | undefined
  ): ElementStyles | undefined;
  
  /** Indicates whether the DOM supports adoptedStyleSheets feature */
  static readonly supportsAdoptedStyleSheets: boolean;
}

/**
 * Represents styles that can be composed into the ShadowDOM of a custom element
 */
type ComposableStyles = string | ElementStyles | CSSStyleSheet;

Usage Examples:

import { ElementStyles, css, ConstructibleStyleStrategy } from "@microsoft/fast-element";

// Create styles programmatically
const manualStyles = new ElementStyles([
  "p { color: blue; }",
  existingStylesheet,
  otherElementStyles
]);

// Add behaviors to styles
const stylesWithBehaviors = css`
  :host { display: block; }
`.withBehaviors(
  new MyCustomBehavior(),
  new ResponsiveDesignTokens()
);

// Custom style strategy
class CustomStyleStrategy implements StyleStrategy {
  constructor(private styles: (string | CSSStyleSheet)[]) {}
  
  addStylesTo(target: StyleTarget): void {
    // Custom style application logic
  }
  
  removeStylesFrom(target: StyleTarget): void {
    // Custom style removal logic
  }
}

// Use custom strategy
const customStyles = css`
  :host { background: red; }
`.withStrategy(CustomStyleStrategy);

// Set global default strategy
ElementStyles.setDefaultStrategy(CustomStyleStrategy);

// Normalize mixed style inputs
const normalizedStyles = ElementStyles.normalize([
  "div { margin: 0; }",
  css`:host { padding: 16px; }`,
  new CSSStyleSheet()
]);

Style Application Strategies

Pluggable strategies for applying styles to DOM targets with support for adoptedStyleSheets and fallback mechanisms.

/**
 * A node that can be targeted by styles
 */
interface StyleTarget extends Pick<Node, "getRootNode"> {
  /** Stylesheets to be adopted by the node */
  adoptedStyleSheets?: CSSStyleSheet[];
  
  /** Adds styles to the target by appending the styles */
  append(styles: HTMLStyleElement): void;
  
  /** Removes styles from the target */
  removeChild(styles: HTMLStyleElement): void;
  
  /** Returns all element descendants of node that match selectors */
  querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;
}

/**
 * Implemented to provide specific behavior when adding/removing styles for elements
 */
interface StyleStrategy {
  /**
   * Adds styles to the target
   * @param target - The target to add the styles to
   */
  addStylesTo(target: StyleTarget): void;

  /**
   * Removes styles from the target
   * @param target - The target to remove the styles from
   */
  removeStylesFrom(target: StyleTarget): void;
}

/**
 * A type that instantiates a StyleStrategy
 */
interface ConstructibleStyleStrategy {
  /**
   * Creates an instance of the strategy
   * @param styles - The styles to initialize the strategy with
   */
  new (styles: (string | CSSStyleSheet)[]): StyleStrategy;
}

Usage Examples:

import { StyleStrategy, StyleTarget, ElementStyles } from "@microsoft/fast-element";

// Adoptable stylesheet strategy (modern browsers)
class AdoptableStyleStrategy implements StyleStrategy {
  private sheets: CSSStyleSheet[];
  
  constructor(styles: (string | CSSStyleSheet)[]) {
    this.sheets = styles.map(style => 
      typeof style === 'string' 
        ? new CSSStyleSheet() 
        : style
    );
    
    // Initialize string styles
    styles.forEach((style, index) => {
      if (typeof style === 'string') {
        this.sheets[index].replaceSync(style);
      }
    });
  }
  
  addStylesTo(target: StyleTarget): void {
    if (target.adoptedStyleSheets) {
      target.adoptedStyleSheets = [
        ...target.adoptedStyleSheets,
        ...this.sheets
      ];
    }
  }
  
  removeStylesFrom(target: StyleTarget): void {
    if (target.adoptedStyleSheets) {
      target.adoptedStyleSheets = target.adoptedStyleSheets.filter(
        sheet => !this.sheets.includes(sheet)
      );
    }
  }
}

// Style element strategy (fallback)
class StyleElementStrategy implements StyleStrategy {
  private elements: HTMLStyleElement[];
  
  constructor(styles: (string | CSSStyleSheet)[]) {
    this.elements = styles
      .filter(style => typeof style === 'string')
      .map(css => {
        const style = document.createElement('style');
        style.textContent = css as string;
        return style;
      });
  }
  
  addStylesTo(target: StyleTarget): void {
    this.elements.forEach(element => target.append(element.cloneNode(true)));
  }
  
  removeStylesFrom(target: StyleTarget): void {
    this.elements.forEach(element => {
      const existing = target.querySelector(`style[data-id="${element.dataset.id}"]`);
      if (existing) {
        target.removeChild(existing);
      }
    });
  }
}

// Use strategy with ElementStyles
const styles = css`
  :host { display: block; }
`.withStrategy(AdoptableStyleStrategy);

CSS Directives

System for creating reactive CSS behaviors and custom interpolation logic within CSS templates.

/**
 * Directive for use in CSS templates
 */
interface CSSDirective {
  /**
   * Creates a CSS fragment to interpolate into the CSS document
   * @param add - Function to add behaviors during CSS creation
   * @returns The string or styles to interpolate into CSS
   */
  createCSS(add: AddBehavior): ComposableStyles;
}

/**
 * Used to add behaviors when constructing styles
 */
type AddBehavior = (behavior: HostBehavior<HTMLElement>) => void;

/**
 * Defines metadata for a CSSDirective
 */
interface CSSDirectiveDefinition<TType extends Constructable<CSSDirective> = Constructable<CSSDirective>> {
  /** The type that the definition provides metadata for */
  readonly type: TType;
}

/**
 * Instructs the CSS engine to provide dynamic styles or associate behaviors with styles
 */
const CSSDirective: {
  /**
   * Gets the directive definition associated with the instance
   * @param instance - The directive instance to retrieve the definition for
   */
  getForInstance(instance: any): CSSDirectiveDefinition | undefined;
  
  /**
   * Gets the directive definition associated with the specified type
   * @param type - The directive type to retrieve the definition for
   */
  getByType<TType extends Constructable<CSSDirective>>(type: TType): CSSDirectiveDefinition<TType> | undefined;
  
  /**
   * Defines a CSSDirective
   * @param type - The type to define as a directive
   */
  define<TType extends Constructable<CSSDirective>>(type: TType): TType;
};

/**
 * Decorator: Defines a CSSDirective
 */
function cssDirective(): ClassDecorator;

Usage Examples:

import { CSSDirective, cssDirective, HostBehavior, HostController } from "@microsoft/fast-element";

// Custom CSS directive for design tokens
@cssDirective()
export class DesignTokenDirective implements CSSDirective {
  constructor(private tokenName: string, private fallback?: string) {}
  
  createCSS(add: AddBehavior): string {
    add(new DesignTokenBehavior(this.tokenName, this.fallback));
    return `var(--${this.tokenName}${this.fallback ? ', ' + this.fallback : ''})`;
  }
}

// Behavior for managing design tokens
class DesignTokenBehavior implements HostBehavior<HTMLElement> {
  constructor(
    private tokenName: string, 
    private fallback?: string
  ) {}
  
  addedCallback(controller: HostController<HTMLElement>): void {
    // Subscribe to design token changes
    DesignTokens.subscribe(this.tokenName, (value) => {
      controller.element.style.setProperty(`--${this.tokenName}`, value);
    });
  }
  
  removedCallback(controller: HostController<HTMLElement>): void {
    // Unsubscribe from design token changes
    DesignTokens.unsubscribe(this.tokenName);
  }
}

// Use custom directive
const token = (name: string, fallback?: string) => new DesignTokenDirective(name, fallback);

const tokenizedStyles = css`
  :host {
    background: ${token('neutral-fill-rest', '#f0f0f0')};
    color: ${token('neutral-foreground-rest', '#333')};
    padding: ${token('design-unit', '4px')};
  }
`;

// Media query directive
@cssDirective()
export class MediaQueryDirective implements CSSDirective {
  constructor(
    private query: string,
    private styles: ElementStyles
  ) {}
  
  createCSS(add: AddBehavior): string {
    return `@media ${this.query} { ${this.styles} }`;
  }
}

const responsive = (query: string, styles: ElementStyles) => 
  new MediaQueryDirective(query, styles);

const responsiveStyles = css`
  :host {
    padding: 8px;
  }
  
  ${responsive('(min-width: 768px)', css`
    :host {
      padding: 16px;
    }
  `)}
`;

Host Behaviors

System for attaching reactive behaviors to styled elements for managing dynamic styling and design token integration.

/**
 * A behavior that can be attached to a host element
 */
interface HostBehavior<TElement extends HTMLElement = HTMLElement> {
  /**
   * Called when the behavior is added to a host
   * @param controller - The controller managing the host
   */
  addedCallback(controller: HostController<TElement>): void;
  
  /**
   * Called when the behavior is removed from a host
   * @param controller - The controller managing the host
   */
  removedCallback(controller: HostController<TElement>): void;
}

/**
 * Controls host-level behaviors and styling
 */
interface HostController<TElement extends HTMLElement = HTMLElement> {
  /** The host element being controlled */
  readonly element: TElement;
  
  /**
   * Adds styles to the host
   * @param styles - The styles to add
   */
  addStyles(styles: ElementStyles | undefined): void;
  
  /**
   * Removes styles from the host
   * @param styles - The styles to remove
   */
  removeStyles(styles: ElementStyles | undefined): void;
}

Usage Examples:

import { HostBehavior, HostController, css } from "@microsoft/fast-element";

// Responsive behavior
export class ResponsiveBehavior implements HostBehavior {
  private mediaQuery: MediaQueryList;
  
  constructor(private query: string, private styles: ElementStyles) {
    this.mediaQuery = window.matchMedia(query);
  }
  
  addedCallback(controller: HostController): void {
    this.handleChange = this.handleChange.bind(this);
    this.mediaQuery.addEventListener('change', this.handleChange);
    
    if (this.mediaQuery.matches) {
      controller.addStyles(this.styles);
    }
  }
  
  removedCallback(controller: HostController): void {
    this.mediaQuery.removeEventListener('change', this.handleChange);
    controller.removeStyles(this.styles);
  }
  
  private handleChange(controller: HostController): void {
    if (this.mediaQuery.matches) {
      controller.addStyles(this.styles);
    } else {
      controller.removeStyles(this.styles);
    }
  }
}

// Theme behavior
export class ThemeBehavior implements HostBehavior {
  constructor(private lightStyles: ElementStyles, private darkStyles: ElementStyles) {}
  
  addedCallback(controller: HostController): void {
    this.updateTheme(controller);
    document.addEventListener('theme-change', this.handleThemeChange);
  }
  
  removedCallback(controller: HostController): void {
    document.removeEventListener('theme-change', this.handleThemeChange);
    controller.removeStyles(this.lightStyles);
    controller.removeStyles(this.darkStyles);
  }
  
  private handleThemeChange = (controller: HostController) => {
    this.updateTheme(controller);
  };
  
  private updateTheme(controller: HostController): void {
    const isDark = document.documentElement.classList.contains('dark-theme');
    
    if (isDark) {
      controller.removeStyles(this.lightStyles);
      controller.addStyles(this.darkStyles);
    } else {
      controller.removeStyles(this.darkStyles);
      controller.addStyles(this.lightStyles);
    }
  }
}

// Use behaviors with styles
const lightStyles = css`:host { background: white; color: black; }`;
const darkStyles = css`:host { background: black; color: white; }`;

const responsiveStyles = css`
  :host { padding: 8px; }
`.withBehaviors(
  new ResponsiveBehavior('(min-width: 768px)', css`:host { padding: 16px; }`),
  new ThemeBehavior(lightStyles, darkStyles)
);

CSS Binding Directives

Specialized directives for creating reactive CSS property bindings with automatic CSS variable generation.

/**
 * A CSS directive that applies bindings
 */
class CSSBindingDirective implements CSSDirective {
  /**
   * Creates an instance of CSSBindingDirective
   * @param binding - The binding to apply
   * @param targetAspect - The CSS aspect to target
   */
  constructor(
    binding: Binding,
    targetAspect: string
  );
  
  /**
   * Creates CSS with the binding behavior
   * @param add - Function to add behaviors
   */
  createCSS(add: AddBehavior): string;
}

Types

/**
 * Template options for CSS compilation
 */
interface CSSTemplateOptions {
  /** CSS compilation strategy */
  strategy?: CSSCompilationStrategy;
}

/**
 * CSS compilation strategy
 */
interface CSSCompilationStrategy {
  /**
   * Compiles CSS template values
   * @param strings - Template string fragments
   * @param values - Interpolated values
   */
  compile<TSource = any, TParent = any>(
    strings: TemplateStringsArray,
    values: CSSValue<TSource, TParent>[]
  ): { styles: ComposableStyles[]; behaviors: HostBehavior<HTMLElement>[] };
}

docs

attributes.md

context-system.md

css-styling.md

data-binding.md

dependency-injection.md

html-templates.md

index.md

observable-system.md

ssr-hydration.md

state-management.md

template-directives.md

testing-utilities.md

utilities.md

web-components.md

tile.json