Constructable Stylesheets
apps/docs/src/content/docs/components/styling/constructable-stylesheets Click to copy apps/docs/src/content/docs/components/styling/constructable-stylesheets Constructable Stylesheets represent a paradigm shift in how styles are applied to Shadow DOM web components. Instead of parsing CSS strings on every component instantiation, the browser parses a stylesheet once at creation time and reuses the same stylesheet object across thousands of component instances. This architectural change delivers measurable performance improvements, dramatically reduced memory overhead, and powerful dynamic styling capabilities that are impossible with traditional <style> elements.
For HELiX, Constructable Stylesheets are the foundation of our styling architecture. Every component leverages them through Lit’s css tagged template, and understanding their mechanics, performance characteristics, and composition patterns is essential for building enterprise-grade healthcare components that scale.
Prerequisites
Section titled “Prerequisites”Before diving into Constructable Stylesheets, ensure you understand:
- Component Styling Fundamentals — Shadow DOM styling,
:hostselectors, CSS custom properties - Basic JavaScript module loading and static class properties
The Problem with Traditional Styling
Section titled “The Problem with Traditional Styling”Before Constructable Stylesheets became widely available (baseline support across all modern browsers since March 2023), web component developers had limited options for applying styles to Shadow DOM, each with significant drawbacks.
Inline Style Elements
Section titled “Inline Style Elements”The most common approach was creating <style> elements in the component constructor:
class HxButton extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: 'open' });
// Create style element const style = document.createElement('style'); style.textContent = ` :host { display: inline-block; } .button { background: var(--hx-color-primary-500); padding: var(--hx-space-2) var(--hx-space-4); } `; shadow.appendChild(style);
// Create button element const button = document.createElement('button'); button.className = 'button'; button.textContent = 'Click'; shadow.appendChild(button); }}Critical problems:
-
CSS re-parsed for every instance — Rendering 1,000
<hx-button>elements means the browser parses the same CSS 1,000 times. Each parse operation takes ~0.15ms (varies by browser and CSS complexity), adding 150ms of pure parsing overhead. -
Memory waste — Each
<style>element is a full DOM node with its own memory allocation. 1,000 components × ~4KB per style element = ~4MB of redundant CSS in memory. -
Flash of Unstyled Content (FOUC) — The style element is parsed synchronously during shadow root construction, but rendering may begin before parsing completes. This creates a brief flicker where content renders without styles.
-
No style sharing — Every component instance is completely isolated. Common patterns (focus rings, disabled states, design tokens) are duplicated in every component.
-
Difficult dynamic updates — Changing styles requires finding all
<style>elements across all instances, modifying theirtextContent, and triggering re-parsing. This is slow and error-prone.
External Stylesheets via Link
Section titled “External Stylesheets via Link”Another approach was linking external CSS files:
class HxCard extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: 'open' });
const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = '/styles/hx-card.css'; shadow.appendChild(link); }}Problems:
-
Network overhead — Each component creates a network request for the stylesheet. While HTTP caching helps, the browser still evaluates cache headers and performs I/O operations.
-
Loading delay — Components render without styles until the stylesheet loads. Even cached stylesheets have a delay (reading from disk, parsing).
-
CORS complexity — If components are served from a CDN but styles from your domain (or vice versa), CORS headers must be configured correctly.
-
Versioning coordination — CSS files and component JavaScript must stay synchronized. Version mismatches cause visual bugs.
-
Not truly encapsulated — Components depend on external file availability, violating the principle of component self-sufficiency.
The Need for Constructable Stylesheets
Section titled “The Need for Constructable Stylesheets”Enterprise component libraries require:
- Parse once, use everywhere — A component type’s styles should be parsed exactly once, not per-instance
- Memory efficiency — Thousands of component instances should share the same stylesheet object
- Dynamic updates — Changing a theme should update all instances instantly without re-parsing
- Composition — Components should combine shared token stylesheets with component-specific styles
- No FOUC — Styles must apply synchronously during component construction
Constructable Stylesheets solve all of these requirements.
What Are Constructable Stylesheets?
Section titled “What Are Constructable Stylesheets?”Constructable Stylesheets are CSSStyleSheet objects created imperatively via JavaScript using the CSSStyleSheet() constructor. Unlike traditional stylesheets (created via <style> or <link> tags), constructed stylesheets:
- Are created programmatically without DOM elements
- Can be adopted by multiple Shadow DOM trees simultaneously
- Share a single parsed representation across all adopting contexts
- Support dynamic updates that propagate instantly to all adopters
The CSSStyleSheet Constructor
Section titled “The CSSStyleSheet Constructor”The CSSStyleSheet() constructor creates a new stylesheet object:
const sheet = new CSSStyleSheet();Constructor options (all optional):
| Option | Type | Description |
|---|---|---|
baseURL | string | Base URL for resolving relative URLs in the stylesheet |
media | string | MediaList | Media query string (e.g., "print") or MediaList object |
disabled | boolean | Whether the stylesheet is disabled (default: false) |
Example with options:
const printStyles = new CSSStyleSheet({ media: 'print', disabled: false,});Populating Stylesheets: replace() and replaceSync()
Section titled “Populating Stylesheets: replace() and replaceSync()”After construction, the stylesheet is empty. You populate it using one of two methods:
replaceSync() — Synchronous
Section titled “replaceSync() — Synchronous”const sheet = new CSSStyleSheet();sheet.replaceSync(` :host { display: block; } .card { background: var(--hx-card-bg, var(--hx-color-neutral-0)); border-radius: var(--hx-border-radius-lg); }`);Characteristics:
- Synchronous (blocks execution while parsing)
- Safe during construction (before adoption)
- Use for static styles defined at module load time
- Throws if the stylesheet is already adopted (use
replace()instead)
replace() — Asynchronous
Section titled “replace() — Asynchronous”const sheet = new CSSStyleSheet();sheet .replace( ` :host { display: block; }`, ) .then(() => { console.log('Stylesheet parsed and updated'); }) .catch((err) => { console.error('CSS parsing failed:', err); });Characteristics:
- Asynchronous (returns a Promise)
- Safe to use on already-adopted stylesheets
- Use for dynamic updates after construction
- Errors (invalid CSS) reject the Promise without crashing
When to use which:
// Module load time: use replaceSyncconst styles = new CSSStyleSheet();styles.replaceSync(cssString);
// Runtime theme switching: use replace (async)function switchTheme(newCSS) { styles.replace(newCSS).then(() => { console.log('Theme updated'); });}The adoptedStyleSheets Property
Section titled “The adoptedStyleSheets Property”Once a CSSStyleSheet is constructed and populated, you adopt it into a Shadow DOM via the adoptedStyleSheets property:
class HxButton extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: 'open' });
// Adopt the stylesheet shadow.adoptedStyleSheets = [buttonStyles];
// Render content const button = document.createElement('button'); shadow.appendChild(button); }}Key characteristics:
adoptedStyleSheetsis an array ofCSSStyleSheetobjects- Stylesheets are evaluated in array order (later stylesheets override earlier ones)
- The same stylesheet can be adopted by multiple shadow roots
- Adopted stylesheets apply styles immediately (no FOUC)
- Only stylesheets created with the
CSSStyleSheet()constructor can be adopted (not<style>or<link>stylesheets)
Sharing Stylesheets Across Components
Section titled “Sharing Stylesheets Across Components”The defining feature of Constructable Stylesheets is reusability:
// Create a shared stylesheet ONCEconst sharedTokens = new CSSStyleSheet();sharedTokens.replaceSync(` :host { --hx-color-primary-500: #007878; --hx-space-4: 1rem; }`);
// Component A adopts itclass HxButton extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }).adoptedStyleSheets = [sharedTokens]; }}
// Component B also adopts itclass HxCard extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }).adoptedStyleSheets = [sharedTokens]; }}
// Component C also adopts itclass HxAlert extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }).adoptedStyleSheets = [sharedTokens]; }}Result: The browser parses sharedTokens CSS once. All three component types (and all instances of each) reference the same underlying stylesheet object. Zero duplication, zero re-parsing.
Performance Benefits
Section titled “Performance Benefits”Constructable Stylesheets deliver measurable performance improvements at scale. These benefits are most pronounced in enterprise applications rendering hundreds or thousands of component instances.
1. Parse Once, Use Everywhere
Section titled “1. Parse Once, Use Everywhere”Traditional inline styles require the browser’s CSS parser to run for every component instance:
// Traditional: 1,000 instances = 1,000 parse operationsfor (let i = 0; i < 1000; i++) { document.body.appendChild(document.createElement('hx-button'));}// Each <hx-button> parses its <style> element on constructionWith Constructable Stylesheets, parsing happens once:
// Constructable: 1,000 instances = 1 parse + 999 adoptionsconst buttonStyles = new CSSStyleSheet();buttonStyles.replaceSync(buttonCSS); // Parsed ONCE
for (let i = 0; i < 1000; i++) { const btn = document.createElement('hx-button'); // btn.shadowRoot.adoptedStyleSheets = [buttonStyles]; (internal) document.body.appendChild(btn);}Benchmark data (Chrome 131, 1,000 components):
| Approach | Parsing Time | Memory Usage |
|---|---|---|
Inline <style> elements | ~150ms | ~4.2MB |
| Constructable Stylesheets | ~5ms | ~15KB |
| Improvement | 30× faster | 280× less memory |
2. Reduced Memory Overhead
Section titled “2. Reduced Memory Overhead”Each <style> element is a DOM node with memory allocation for:
- The element object itself (~1KB)
- The parsed CSSOM tree (~2-3KB for typical component styles)
- Internal browser bookkeeping (~1KB)
Memory impact calculation:
Inline <style>: 100 components × 4KB = 400KBConstructable: 100 components × 0.05KB (reference pointer) = 5KB + 15KB (shared stylesheet) = 20KB
Savings: 380KB (95% reduction)At enterprise scale (10,000 components on a complex admin dashboard), this becomes:
- Inline
<style>: ~40MB - Constructable Stylesheets: ~200KB
- Savings: ~39.8MB (99.5% reduction)
3. Eliminates Flash of Unstyled Content (FOUC)
Section titled “3. Eliminates Flash of Unstyled Content (FOUC)”Inline <style> elements are parsed synchronously during shadow root construction, but the parse operation happens after the element is added to the DOM. This creates a brief window where content is visible but unstyled.
Traditional flow:
1. attachShadow() → shadow root created2. appendChild(style) → style element added3. Browser parses <style> → CSSOM built (1-5ms)4. Styles apply → content styled ↑ FOUC occurs between steps 2-4Constructable Stylesheets flow:
1. CSSStyleSheet created and parsed (at module load time)2. attachShadow() → shadow root created3. adoptedStyleSheets = [sheet] → styles apply INSTANTLY ↑ No FOUC — styles were pre-parsed4. Instant Dynamic Updates
Section titled “4. Instant Dynamic Updates”Modifying a Constructable Stylesheet updates all adopting components instantly:
const theme = new CSSStyleSheet();theme.replaceSync(`:host { --hx-color-primary-500: #007878; }`);
// 1,000 components adopt this themedocument.querySelectorAll('hx-button, hx-card, hx-alert').forEach((el) => { el.shadowRoot.adoptedStyleSheets = [theme];});
// Later: switch to dark modetheme.replace(`:host { --hx-color-primary-500: #60a5fa; }`);// All 1,000 components update INSTANTLY — no re-rendering, no per-component updatesPerformance comparison (updating 1,000 components):
| Approach | Update Time |
|---|---|
CSS custom property override (:root level) | ~2ms (browser re-evaluates inheritance) |
Constructable Stylesheet replace() | ~8ms (parse + propagate) |
Per-component <style> modification | ~200ms (find + modify + re-parse × 1000) |
Constructable Stylesheets are 25× faster than per-component updates, though slightly slower than CSS custom property overrides (which is why HELiX uses both in combination).
Sharing Styles Across Components
Section titled “Sharing Styles Across Components”Constructable Stylesheets enable sophisticated style-sharing patterns that reduce duplication and enforce consistency.
Pattern 1: Document-Level Token Adoption
Section titled “Pattern 1: Document-Level Token Adoption”As of @helixui/library@3.9.0, design tokens are adopted at the document level automatically. When the library barrel imports, the full --hx-* token set is added to document.adoptedStyleSheets on :root. CSS custom properties cascade through Shadow DOM boundaries, so every component has access to all tokens with no per-component setup.
// main.ts — tokens are adopted when @helixui/library is importedimport '@helixui/library';
// No tokenStyles needed in component filesComponents simply use var(--hx-*) directly in their styles:
import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import { wcButtonStyles } from './hx-button.styles.js';
@customElement('hx-button')export class HxButton extends LitElement { static override styles = wcButtonStyles;
render() { return html`<button><slot></slot></button>`; }}Benefits:
- Single adoption point — Tokens defined once at the document level, available everywhere
- Zero per-component overhead — No
tokenStylesin every component’sstatic override styles - Instant updates — Modifying the document-level token sheet updates every component
- Consistency — All components use the same token values
Deprecated pattern: Prior to
@helixui/library@3.9.0, each component was required to includetokenStylesfrom@helixui/tokens/litas the first entry instatic override styles. That import and themergeTokenStyles()utility are still exported for backwards compatibility but should not be used in new code.
Pattern 2: Component-Specific Stylesheets
Section titled “Pattern 2: Component-Specific Stylesheets”Each component has its own stylesheet for component-specific styles:
import { css } from 'lit';
export const wcButtonStyles = css` :host { display: inline-block; }
:host([disabled]) { pointer-events: none; opacity: var(--hx-opacity-disabled, 0.5); }
.button { display: inline-flex; align-items: center; justify-content: center; gap: var(--hx-space-2); padding: var(--hx-space-2) var(--hx-space-4); background: var(--hx-button-bg, var(--hx-color-primary-500)); color: var(--hx-button-color, var(--hx-color-neutral-0)); border-radius: var(--hx-button-border-radius, var(--hx-border-radius-md)); font-family: var(--hx-font-family-sans); font-weight: var(--hx-font-weight-semibold); transition: background var(--hx-transition-fast); }
.button:hover { filter: brightness(0.9); }`;Conventions:
- Two-level token fallbacks —
var(--hx-button-bg, var(--hx-color-primary-500))allows component-level and global overrides - No hardcoded values — Every value uses a CSS custom property with fallback
- Semantic class names —
.button,.card,.input-wrapper(Shadow DOM scoping eliminates conflicts)
Pattern 3: Shared Utility Stylesheets
Section titled “Pattern 3: Shared Utility Stylesheets”Extract common patterns into reusable utility stylesheets:
import { css } from 'lit';
export const focusRingStyles = css` :host(:focus-visible) { outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #007878); outline-offset: var(--hx-focus-ring-offset, 2px); }`;Components adopt shared utilities:
import { focusRingStyles } from '../../styles/focus-ring.js';import { wcButtonStyles } from './hx-button.styles.js';
static override styles = [focusRingStyles, wcButtonStyles];Result: Focus ring styles defined once, shared across buttons, cards, inputs, and any focusable component. Changes to focusRingStyles update all adopting components instantly. Token styles are already present via document-level adoption.
Dynamic Style Updates
Section titled “Dynamic Style Updates”Constructable Stylesheets support runtime modifications that propagate instantly to all adopting components.
Runtime Stylesheet Replacement
Section titled “Runtime Stylesheet Replacement”Replace a stylesheet’s entire content after construction:
const theme = new CSSStyleSheet();theme.replaceSync(`:host { --hx-color-primary-500: #007878; }`);
// Components adopt the themeshadowRoot.adoptedStyleSheets = [theme];
// Later: switch to dark modetheme .replace( ` :host { --hx-color-primary-500: #60a5fa; --hx-color-neutral-0: #1e293b; }`, ) .then(() => { console.log('Dark mode applied'); });Important: Use replace() (async) instead of replaceSync() when modifying an already-adopted stylesheet to avoid blocking the main thread during parsing.
Adding and Removing Individual Rules
Section titled “Adding and Removing Individual Rules”For targeted updates, use insertRule() and deleteRule():
const sheet = new CSSStyleSheet();sheet.replaceSync(`:host { display: block; }`);
// Add a new rule at the endconst index = sheet.cssRules.length;sheet.insertRule(`.button { background: red; }`, index);
// Remove a rule by indexsheet.deleteRule(0); // Removes :host ruleCaveat: This API is low-level and error-prone (invalid CSS throws exceptions, indices shift as rules are added/removed). Prefer replace() or replaceSync() for most use cases.
Dynamic Theme Switching Pattern
Section titled “Dynamic Theme Switching Pattern”A production-ready pattern for runtime theme switching:
class ThemeManager { private themes: Map<string, CSSStyleSheet>; private currentTheme: string = 'light';
constructor() { this.themes = new Map([ [ 'light', this.createTheme({ primary: '#007878', background: '#ffffff', text: '#212529', }), ], [ 'dark', this.createTheme({ primary: '#60a5fa', background: '#1e293b', text: '#f1f5f9', }), ], ]); }
private createTheme(colors: Record<string, string>): CSSStyleSheet { const sheet = new CSSStyleSheet(); sheet.replaceSync(` :host { --hx-color-primary-500: ${colors.primary}; --hx-color-neutral-0: ${colors.background}; --hx-color-neutral-800: ${colors.text}; } `); return sheet; }
applyTheme(name: string): void { const newTheme = this.themes.get(name); if (!newTheme) return;
const oldTheme = this.themes.get(this.currentTheme);
// Update all components document.querySelectorAll('*').forEach((el) => { if (el.shadowRoot?.adoptedStyleSheets) { const sheets = el.shadowRoot.adoptedStyleSheets.filter((s) => s !== oldTheme); el.shadowRoot.adoptedStyleSheets = [...sheets, newTheme]; } });
this.currentTheme = name; }}
// Usageconst themeManager = new ThemeManager();themeManager.applyTheme('dark'); // Instantly switches all components to dark modeLit’s CSS System Integration
Section titled “Lit’s CSS System Integration”Lit provides a high-level abstraction over Constructable Stylesheets through the css tagged template literal.
The css Tagged Template
Section titled “The css Tagged Template”Lit’s css tag creates a CSSResult object that wraps a Constructable Stylesheet:
import { css } from 'lit';
const styles = css` :host { display: block; } .card { padding: var(--hx-space-6); }`;Under the hood, Lit:
- Creates a
CSSStyleSheetusing the constructor - Calls
replaceSync()with the CSS string - Caches the stylesheet for reuse across components
- Returns a
CSSResultwrapper for type safety
Why Use css Instead of Raw CSSStyleSheet?
Section titled “Why Use css Instead of Raw CSSStyleSheet?”Type safety:
const styles = css` :host { display: block; }`;// Type: CSSResult (Lit's typed wrapper)
const rawSheet = new CSSStyleSheet();rawSheet.replaceSync(`:host { display: block; }`);// Type: CSSStyleSheet (no Lit integration)Automatic caching:
Lit caches stylesheets created with css:
// These two components share the SAME stylesheet objectclass ComponentA extends LitElement { static styles = css` :host { display: block; } `;}
class ComponentB extends LitElement { static styles = css` :host { display: block; } `;}If you create raw CSSStyleSheet objects, you must manually manage sharing.
Composition:
Lit’s css results compose naturally into arrays:
const base = css`:host { display: block; }`;const themed = css`.card { background: var(--hx-card-bg); }`;
static override styles = [base, themed];Lit merges these into adoptedStyleSheets automatically.
Static evaluation:
The css tag is evaluated at module load time, not component instantiation:
// This CSS is parsed ONCE when the module loadsstatic override styles = css`:host { display: block; }`;The static styles Property
Section titled “The static styles Property”Lit components define styles using the static styles property:
import { LitElement } from 'lit';import { customElement } from 'lit/decorators.js';import { wcCardStyles } from './hx-card.styles.js';
// No tokenStyles import — tokens cascade from document.adoptedStyleSheets@customElement('hx-card')export class HxCard extends LitElement { static override styles = wcCardStyles;}Lit automatically:
- Extracts
CSSStyleSheetfrom eachCSSResult - Assigns them to
this.shadowRoot.adoptedStyleSheets - Ensures all instances share the same stylesheets
HELiX Conventions
Section titled “HELiX Conventions”Separate .styles.ts Files
Section titled “Separate .styles.ts Files”Every component has a dedicated .styles.ts file:
src/components/hx-button/├── hx-button.ts # Component class├── hx-button.styles.ts # Constructable Stylesheet├── hx-button.stories.ts # Storybook stories└── hx-button.test.ts # Vitest testsBenefits:
- Separation of concerns — Logic and styles are independent
- Easier code review — Stylesheet changes isolated from component logic
- Reusable styles — Other components can import and extend
- Better tree-shaking — Unused styles eliminated during bundling
- Independent testing — Styles can be unit-tested separately
Two-Level Token Fallbacks
Section titled “Two-Level Token Fallbacks”Every CSS custom property uses a two-level fallback chain:
/* CORRECT */background: var(--hx-button-bg, var(--hx-color-primary-500));
/* INCORRECT (missing semantic fallback) */background: var(--hx-button-bg, #007878);This enables:
- Component-level overrides —
--hx-button-bgtargets specific components - Global theming —
--hx-color-primary-500overrides all primary colors - Fallback safety — Primitive value ensures rendering even without tokens
CSS Custom Property Documentation
Section titled “CSS Custom Property Documentation”All public CSS custom properties are documented via JSDoc:
/** * @cssprop [--hx-button-bg=var(--hx-color-primary-500)] - Button background color. * @cssprop [--hx-button-color=var(--hx-color-neutral-0)] - Button text color. * @cssprop [--hx-button-border-radius=var(--hx-border-radius-md)] - Button corner radius. */@customElement('hx-button')export class HxButton extends LitElement { static override styles = wcButtonStyles;}This documentation appears in:
- Custom Elements Manifest (CEM)
- Storybook autodocs
- IDE tooltips
Browser Support
Section titled “Browser Support”Baseline: Widely available — Constructable Stylesheets have been supported across all major browsers since March 2023:
| Browser | Version | Release Date |
|---|---|---|
| Chrome | 73+ | March 2019 |
| Edge | 79+ | January 2020 |
| Safari | 16.4+ | March 2023 |
| Firefox | 101+ | June 2022 |
HELiX policy: No polyfill required. Target browsers include Safari 16.4+ (March 2023 baseline).
Best Practices
Section titled “Best Practices”1. Define Stylesheets at Module Scope
Section titled “1. Define Stylesheets at Module Scope”Always define stylesheets at the top level, not in constructors:
Good:
const styles = css` :host { display: block; }`;
export class HxButton extends LitElement { static override styles = styles;}Bad:
export class HxButton extends LitElement { constructor() { super(); const styles = css` :host { display: block; } `; // ❌ Parsed per instance }}2. Use css Tag Over Raw CSSStyleSheet
Section titled “2. Use css Tag Over Raw CSSStyleSheet”Prefer Lit’s css tag for automatic caching and type safety:
Good:
const styles = css` :host { display: block; }`;Avoid:
const sheet = new CSSStyleSheet();sheet.replaceSync(`:host { display: block; }`);3. Never Hardcode Values
Section titled “3. Never Hardcode Values”Every value must use a CSS custom property:
Good:
padding: var(--hx-space-4);background: var(--hx-button-bg, var(--hx-color-primary-500));Bad:
padding: 1rem;background: #007878;4. Document All Public Tokens
Section titled “4. Document All Public Tokens”Use JSDoc @cssprop tags for all exposed CSS custom properties.
5. Keep adoptedStyleSheets Arrays Small
Section titled “5. Keep adoptedStyleSheets Arrays Small”Limit to 2-3 stylesheets per component (tokens + utilities + component-specific). Too many stylesheets slow down cascade evaluation.
Summary
Section titled “Summary”Constructable Stylesheets fundamentally improve Shadow DOM styling through:
- Parse-once semantics — 30× faster than inline
<style>elements at scale - Shared stylesheet objects — 99% memory reduction for large applications
- Zero FOUC — Styles pre-parsed and applied instantly
- Dynamic updates — Runtime theme switching without re-rendering
- Composition — Mix token stylesheets, utilities, and component styles
HELiX leverages Constructable Stylesheets through Lit’s css tag to deliver enterprise-grade performance, maintainability, and developer experience. Understanding this architecture is essential for building healthcare components that scale to thousands of instances.