# `MaterialPatch`

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `configurator-runtime.d.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

Type · `import type { MaterialPatch } from '@holostep-configurators/runtime-client/contract';` · stable · since 2.0.0 · section 8. Materials and textures

A MaterialSpec as a JSON Merge Patch (RFC 7396): what a rule's `material`
takes. Every field also takes `null`, which removes your value of it in that layer, so the layer
below shows (spec section 6.10, R5); a field you omit keeps its value. That holds for the
top-level fields, those of `specular`, `sheen`, `clearcoat`, `transmission` and `labs`, and
those of a map slot's texture, which merge one by one (R10): `{ maps: { baseColor: { scale:
null } } }` removes your `scale` and keeps the slot's image and its other fields, and on a slot
that brings its own image the field then takes its default (R9). Colours, points, `wrap`,
`sampling` and images are whole values. [`materials.create`](Materials.create.md) and [`apply({ materials })`](Writer.apply.md) take a
MaterialSpec, which has no nulls.
Not for: dropping the whole rule material, which is `material: null`.

## Declaration

```ts
export type MaterialPatch = {
  [K in keyof MaterialSpec]?: K extends 'specular' | 'sheen' | 'clearcoat' | 'transmission' | 'labs' ? {
    [F in keyof NonNullable<MaterialSpec[K]>]?: NonNullable<MaterialSpec[K]>[F] | null;
  } | null : K extends 'maps' ? Partial<Record<MapSlot, string | 'none' | null | {
    [F in keyof TextureSpec]?: TextureSpec[F] | null;
  }>> | null : MaterialSpec[K] | null;
};
```

## Example

Imports: values (`has`, `validate`, the `Holo*Error` classes, the pure helpers) from `@holostep-configurators/runtime-client`, types from `@holostep-configurators/runtime-client/contract`, `/math` and `/testing` names from those entries; `boot` from the runtime module the root's `loadRuntimeAtFloor` resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (`examples/declaration/08-MaterialPatch.ts`):

```ts
import type { Query, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare const glass: Query;
// the tint goes; the rule's other material fields, its draw order included, stay
await session.find(glass).set({ material: { baseColor: null, alpha: null } });
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `MaterialPatch` names no product |
| 2. A precedent | pass: [glTF KHR_texture_transform](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform) (row `G34`) |
| 3. Two apps, or labs | pass: 2 apps (ledger: app-0c773a4ee3, app-f3598fad77) |
| 4. The cheaper fix first | decided per need in Appendix B: rows `G34` at tier stable (a member only where no rule, data, asset or app code serves the need) |
