# cssFunction

Emit an `@function` rule and return a callable that writes calls to it.

`cssFunction` emits a native CSS custom function. The compiler replaces the call with a function that formats a CSS call expression, so the browser evaluates the body and JavaScript never does.

```tsx title="Box.tsx"
import { style } from 'zyzz'
import { cssFunction } from 'zyzz/web'

// Emits `@function --z-cssfunction-double(--size <length>) returns <length>{…}`
const double = cssFunction({
  body: { result: 'calc(var(--size) * 2)' },
  parameters: [{ name: '--size', syntax: '<length>' }],
  returns: '<length>',
})

export function Box() {
  return <div {...styles.box()} />
}

namespace styles {
  // Compiles to `width:--z-cssfunction-double(2rem)`
  export const box = style({ width: double('2rem') })
}
```

## Signature

```ts
// Parameters, an optional return syntax, and a body
cssFunction(options, context?)
```

## Parameters

### options.parameters

* **Type:** `readonly cssFunction.Parameter[]`

Ordered parameters, each with a dashed `name`, an optional `syntax`, and an optional `default`. A parameter without `syntax` accepts any CSS value. Parameters with defaults become optional trailing arguments.

```ts
import { cssFunction } from 'zyzz/web'

export const fluid = cssFunction({
  body: { result: 'clamp(var(--min), 4vw, var(--max))' },
  parameters: [
    { name: '--min', syntax: '<length>' },
    // Callers may omit the maximum
    { default: '3rem', name: '--max', syntax: '<length>' },
  ],
  returns: '<length>',
})
```

A syntax accepts one component, `+` or `#` multipliers, and `type(...)` alternatives. Typed parameters narrow the accepted arguments, so `<length>` rejects a bare number.

### options.returns

* **Type:** `string`
* **Default:** `undefined`

The syntax of the result. TypeScript accepts the call only in properties whose values fit that syntax. An omitted syntax accepts any CSS value.

```ts
import { cssFunction } from 'zyzz/web'

export const tint = cssFunction({
  body: { result: 'color-mix(in srgb, var(--color), white 20%)' },
  parameters: [{ name: '--color', syntax: '<color>' }],
  // Calls fit color properties
  returns: '<color>',
})
```

### options.body

* **Type:** `cssFunction.Body`

The function body: a `result`, local custom properties, and `@media`, `@supports`, or `@container` groups that override them. An omitted `result` makes the call invalid at computed-value time.

```ts
import { cssFunction } from 'zyzz/web'

export const gutter = cssFunction({
  body: {
    // A local custom property, then a conditional result
    '--base': 'var(--size)',
    result: 'var(--base)',
    '@media (width >= 48rem)': { result: 'calc(var(--base) * 2)' },
  },
  parameters: [{ name: '--size', syntax: '<length>' }],
  returns: '<length>',
})
```

### options\[atRule]

* **Type:** `` '@layer' | `@${'container' | 'layer' | 'media' | 'supports'}${' ' | '\t' | '\n' | '\r' | '\f' | '(' | `/*${string}*/`}${string}` ``

Grouping keys around a complete definition. Every branch must declare the same parameters and return syntax, while bodies may differ. Each value holds a complete definition or further grouping keys, and a bare `@layer` key emits an anonymous layer.

```ts
import { cssFunction } from 'zyzz/web'

export const half = cssFunction({
  // Emits the function inside `@layer functions`
  '@layer functions': {
    body: { result: 'calc(var(--size) / 2)' },
    parameters: [{ name: '--size', syntax: '<length>' }],
  },
})
```

### context.id

* **Type:** `string`
* **Default:** `undefined`

A fixed identity, which replaces the name derived from the binding. It also separates two modules that declare the same binding name. Without a compiler transform, a call requires it.

```ts
// Calls format as `--z-cssfunctionid-double(…)`
cssFunction({ body: { result: '0px' }, parameters: [] }, { id: 'double' })
```

## Returns

### reference

* **Type:** `cssFunction.Reference<parameters, returns>`

A function that formats a call expression from its arguments. Arguments that contain commas are wrapped in braces, as CSS requires. Imports, re-exports, and packed libraries keep the same name.

```ts
// Compiles to `font-size:--z-cssfunction-fluid(1rem,2rem)`
style({ fontSize: fluid('1rem', '2rem') })
```

## Types

* **`cssFunction.Arguments<parameters>`:** The accepted argument tuple.
* **`cssFunction.Body`:** The accepted body.
* **`cssFunction.Input<parameter>`:** The argument type implied by one parameter's syntax.
* **`cssFunction.Options`:** The accepted definition.
* **`cssFunction.Parameter`:** One parameter declaration.
* **`cssFunction.Reference<parameters, returns>`:** The returned callable.
* **`cssFunction.Syntax`:** A syntax string.

```ts
import type { cssFunction } from 'zyzz/web'

// A reusable parameter declaration
export const size = {
  name: '--size',
  syntax: '<length>',
} as const satisfies cssFunction.Parameter
```

## Errors

TypeScript rejects invalid syntax strings and arguments outside a parameter's syntax.

```ts
import { cssFunction } from 'zyzz/web'

const double = cssFunction({
  body: { result: 'calc(var(--size) * 2)' },
  parameters: [{ name: '--size', syntax: '<length>' }],
  returns: '<length>',
})

// A length requires a unit
double(2)
// error: Argument of type '2' is not assignable to parameter of type 'Input<{ readonly name: "--size"; readonly syntax: "<length>"; }>'.
```

The compiler reports `Source.ExtractError` for invalid syntax and branches whose signatures differ. Without a compiler transform, a call without `context.id` throws an `Error`. Native builds reject the call with `Native.CompileError`.
