# Configuration

Every snippet has a `snippet.json` that declares where and when its files
load, so the files themselves hold only the code. Edit it in the editor like
any other file, or through **Snippet configuration** in the
[workspace](/docs/byte/workspace.md), which writes the same file into the draft;
save to apply either.

## Minimal setup

The smallest valid configuration is an empty object:

```json
{}
```

With it, every PHP file runs on `init` outside wp-admin, and every CSS, SCSS
and JavaScript file loads on the front end.

`snippet.json` must be strict JSON holding an object. A trailing comma, a
comment or a top-level array makes it invalid, and an invalid snippet does not
run at all: the file tree marks it **Snippet (invalid snippet.json)**, the
configuration dialog asks you to fix the file first, and WP-CLI lists it as
disabled.

The configuration dialog writes only the fields you change, each in the form
`snippet.json` already holds it, so a key saved as one string stays one
string while it holds one value. Its **Contexts** and **Environments** cards
offer a switch per [context](#where-it-runs) and per environment type, with
any other stored value kept as a switch of its own, and **Tags** takes one tag
per line.

## Metadata

| Key           | Type   | Meaning                                                                                              |
| ------------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `name`        | string | The snippet's display name, used by the export dialog and by WP-CLI. Defaults to the folder name.    |
| `description` | string | What the snippet does.                                                                               |
| `version`     | string | A version such as `1.0.0`, used as the exported plugin's version.                                    |
| `tags`        | array  | Labels for your own organisation.                                                                    |
| `export`      | object | `author`, `uri` and `textDomain` for the header of an [exported plugin](/docs/byte/snippets/export.md). |

## Whether it runs

| Key            | Type    | Meaning                                                                                                                                                                            |
| -------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`      | boolean | `false` keeps the snippet without running it. Omitted means enabled. The **Enable snippet** switch writes it.                                                                      |
| `environments` | array   | The environment types the snippet runs in, compared with `wp_get_environment_type()`: `local`, `development`, `staging` or `production`. Omitted or empty means every environment. |
| `schedule`     | object  | A time window, described below.                                                                                                                                                    |

### Schedule

`schedule` limits a snippet to a period and, within it, to recurring times. It
has no field in the configuration dialog, so write it in `snippet.json`:

```json
{
  "schedule": {
    "timezone": "Europe/Berlin",
    "start": "2026-11-27 00:00",
    "end": "2026-12-01 23:59",
    "recurring": {
      "type": "weekly",
      "days": ["friday", "saturday"],
      "start_time": "09:00",
      "end_time": "18:00"
    }
  }
}
```

- `timezone` is a PHP timezone name; without it, or when it is invalid, times
  are UTC.
- `start` and `end` are date and time strings PHP can read. Before `start` or
  after `end` the snippet does not run; an unreadable value is ignored.
- `recurring.type` is `daily`, `weekly`, `monthly` or `time_range`. `daily`
  and `time_range` run between `start_time` and `end_time` (`HH:MM` or `H:MM`,
  both required); a window whose end comes before its start, such as `22:00`
  to `02:00`, runs across midnight. `weekly` also takes `days` as weekday
  names, and `monthly` takes `days` as days of the month (`[1, 15]`). An
  unknown type is ignored.
- A schedule, or a part of it, that is not shaped as above is ignored like an
  unreadable value; it never stops the site.

## Where it runs

**`context`** (string or array) is where the files load. The default is
`frontend`, which the `sleekByte/snippets/defaults/context` filter changes.

| Context        | PHP files run                                                             | CSS, SCSS and JavaScript load                       |
| -------------- | ------------------------------------------------------------------------- | --------------------------------------------------- |
| `frontend`     | on every request outside wp-admin, which includes REST, cron and WP-CLI   | on the front end (`wp_enqueue_scripts`)             |
| `admin`        | in wp-admin outside the block editor, including `admin-ajax.php` requests | in wp-admin (`admin_enqueue_scripts`)               |
| `block-editor` | on block editor screens                                                   | in the block editor (`enqueue_block_editor_assets`) |

Byte tells the block editor apart from the rest of wp-admin through the
current admin screen. Before `current_screen` there is none, so on an earlier
hook such as `init` it treats every `post.php?post=<id>&action=edit` request
as the block editor and no other; give `admin` and `block-editor` PHP files a
hook from `current_screen` on when the difference matters.

**`hook`** (string) is the WordPress action PHP files run on. The default is
`init`, which the `sleekByte/snippets/defaults/hook` filter changes; CSS, SCSS
and JavaScript files ignore `hook`. Byte registers snippet files during
`after_setup_theme`, so `muplugins_loaded`, `plugins_loaded`, `setup_theme` and
`after_setup_theme` itself never run a file, and the configuration dialog and
WP-CLI refuse them.

| Hook                   | When it fires                                   |
| ---------------------- | ----------------------------------------------- |
| `init`                 | on every request, once WordPress has loaded     |
| `wp`                   | after the main query, on the front end only     |
| `template_redirect`    | on the front end, before the template is chosen |
| `wp_head`, `wp_footer` | while the front-end page is printed             |
| `admin_init`           | at the start of every wp-admin request          |
| `current_screen`       | in wp-admin, once the admin screen is known     |

## Conditions

**`conditions`** decides on each request whether the files load. It is a list
of groups: the snippet loads when any group matches, and a group matches when
all of its conditions do.

```json
{
  "hook": "wp",
  "conditions": [
    [
      {
        "parameter": "userRole",
        "operator": "equals",
        "value": "administrator"
      },
      { "parameter": "isSingle", "operator": "equals", "value": true }
    ],
    [
      {
        "parameter": "postType",
        "operator": "equals",
        "value": ["post", "page"]
      }
    ]
  ]
}
```

Each condition has a `parameter`, an `operator` (default `equals`), a `value`
and an optional `negate`, which reverses the result. The configuration
dialog's Conditions card edits the same groups.

### When conditions are checked

A condition reads WordPress's state at the moment it is checked, and a value
WordPress has not worked out yet reads as empty or false, so the condition
fails and the file is skipped without an error.

- **PHP files** are checked when their `hook` fires.
- **CSS, SCSS and JavaScript files** with conditions are checked on the hook
  that enqueues their context: `wp_enqueue_scripts`, `admin_enqueue_scripts`
  or `enqueue_block_editor_assets`. The main query and the admin screen are
  known by then, so every parameter works for them.

| Parameters                                                                                                                                                                                                                                                                                                                                                                                                               | Known from                      | Hook for PHP files                                                                   |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------ |
| `userId`, `userRole`, `isUserLoggedIn`, `isAdmin`, `isAjax`, `isCustomizePreview`, `isNetworkAdmin`, `currentTime`, `currentDate`, `dayOfWeek`, `timeOfDay`, `deviceType`, `browserName`, `browserVersion`, `operatingSystem`, `isMobile`, `isTablet`, `isDesktop`, `requestUri`, `queryString`, `httpMethod`, `userAgent`, `referer`, `ipAddress`, `currentBlogId`, `isMultisite`, `isMainSite`, `option:`, `themeMod:` | `init`                          | any, including the default `init`                                                    |
| `isSingle`, `isPage`, `isHome`, `isFrontPage`, `isCategory`, `isTag`, `isArchive`, `isSearch`, `is404`, `isAttachment`, `isFeed`, `isPreview`, `isEmbed`, `postId`, `postType`, `postStatus`, `postParent`, `postAuthor`, `pageTemplate`, `meta:`                                                                                                                                                                        | the main query, front end only  | `wp` or a later front-end hook such as `template_redirect`, `wp_head` or `wp_footer` |
| `currentScreen`                                                                                                                                                                                                                                                                                                                                                                                                          | the admin screen, wp-admin only | `current_screen` or a later admin hook, with `"context": ["admin"]`                  |
| `isRest`                                                                                                                                                                                                                                                                                                                                                                                                                 | a REST request being served     | `rest_api_init`                                                                      |
| `currentLanguage`                                                                                                                                                                                                                                                                                                                                                                                                        | your multilingual plugin        | `init` for the site locale; `wp` or later when WPML or Polylang provides it          |

On archive pages the post parameters read the first post of the main query,
such as the first product of the shop page; combine them with `isSingle` or
`isPage` to target single views.

### Parameters

Every parameter a condition can test. **Label** is its name in the
Conditions card, **Type** decides which [operators](#operators) the card
offers for it, and **Example** is a `value` it can be compared with.

| Parameter            | Label              | Type      | Value                                                                                                                                                 | Example                                       |
| -------------------- | ------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `userId`             | User ID            | `number`  | the current user's ID, 0 when logged out; the card picks it from the site's users, where **Current user** saves `"currentUser"`, whoever is signed in | `1`                                           |
| `userRole`           | User role          | `string`  | the current user's first role, picked from the site's roles                                                                                           | `"editor"`                                    |
| `isUserLoggedIn`     | User logged in     | `boolean` | whether a user is logged in                                                                                                                           | `true`                                        |
| `postId`             | Post ID            | `number`  | the current post's ID                                                                                                                                 | `42`                                          |
| `postType`           | Post type          | `string`  | the current post's type, picked from the post types                                                                                                   | `"product"`                                   |
| `postStatus`         | Post status        | `string`  | the current post's status, picked from the statuses                                                                                                   | `"publish"`                                   |
| `postParent`         | Post parent        | `number`  | the current post's parent ID, 0 without one                                                                                                           | `12`                                          |
| `postAuthor`         | Post author        | `number`  | the current post's author ID, picked like `userId`                                                                                                    | `"currentUser"`                               |
| `pageTemplate`       | Page template      | `string`  | the current post's page template file, empty for the default template                                                                                 | `"templates/full-width.php"`                  |
| `isSingle`           | Single             | `boolean` | `is_single()`                                                                                                                                         | `true`                                        |
| `isPage`             | Page               | `boolean` | `is_page()`                                                                                                                                           | `true`                                        |
| `isHome`             | Home               | `boolean` | `is_home()`, the posts page                                                                                                                           | `true`                                        |
| `isFrontPage`        | Front page         | `boolean` | `is_front_page()`                                                                                                                                     | `true`                                        |
| `isCategory`         | Category           | `boolean` | `is_category()`                                                                                                                                       | `true`                                        |
| `isTag`              | Tag                | `boolean` | `is_tag()`                                                                                                                                            | `true`                                        |
| `isArchive`          | Archive            | `boolean` | `is_archive()`                                                                                                                                        | `true`                                        |
| `isSearch`           | Search             | `boolean` | `is_search()`                                                                                                                                         | `true`                                        |
| `is404`              | Not found          | `boolean` | `is_404()`                                                                                                                                            | `true`                                        |
| `isAttachment`       | Attachment         | `boolean` | `is_attachment()`                                                                                                                                     | `true`                                        |
| `isAdmin`            | Admin area         | `boolean` | `is_admin()`, whether the request is in wp-admin                                                                                                      | `true`                                        |
| `isAjax`             | Ajax request       | `boolean` | `wp_doing_ajax()`, whether it is an AJAX request                                                                                                      | `true`                                        |
| `isRest`             | REST request       | `boolean` | whether it is a REST request                                                                                                                          | `true`                                        |
| `isFeed`             | Feed               | `boolean` | `is_feed()`                                                                                                                                           | `true`                                        |
| `isPreview`          | Preview            | `boolean` | `is_preview()`, a post preview                                                                                                                        | `true`                                        |
| `isCustomizePreview` | Customizer preview | `boolean` | `is_customize_preview()`, the Customizer preview                                                                                                      | `true`                                        |
| `isEmbed`            | Embed              | `boolean` | `is_embed()`                                                                                                                                          | `true`                                        |
| `currentScreen`      | Current screen     | `string`  | the admin screen ID, such as `dashboard`, or empty before the screen is known and outside wp-admin                                                    | `"edit-post"`                                 |
| `currentTime`        | Current time       | `time`    | the site's time as `HH:MM`                                                                                                                            | `"14:30"`                                     |
| `currentDate`        | Current date       | `date`    | the site's date as `YYYY-MM-DD`                                                                                                                       | `"2026-12-24"`                                |
| `dayOfWeek`          | Day of week        | `number`  | the day of the week, `0` (Sunday) to `6` (Saturday)                                                                                                   | `[1, 2, 3, 4, 5]`                             |
| `timeOfDay`          | Time of day        | `number`  | the hour, 0 to 23                                                                                                                                     | `9`                                           |
| `deviceType`         | Device type        | `string`  | `mobile`, `tablet` or `desktop`                                                                                                                       | `"mobile"`                                    |
| `browserName`        | Browser name       | `string`  | `edge`, `opera`, `samsung`, `firefox`, `chrome`, `safari`, `internet-explorer` or `unknown`                                                           | `"firefox"`                                   |
| `browserVersion`     | Browser version    | `string`  | the browser's version, such as `126.0.0.0`, or `unknown`                                                                                              | `"126."` with `startsWith`                    |
| `operatingSystem`    | Operating system   | `string`  | `ios`, `windows-10`, `windows-8.1`, `windows-8`, `windows-7`, `windows`, `macos-catalina`, `macos-mojave`, `macos`, `android`, `linux` or `unknown`   | `"macos"` with `startsWith`                   |
| `isMobile`           | Mobile             | `boolean` | whether `deviceType` is `mobile`                                                                                                                      | `true`                                        |
| `isTablet`           | Tablet             | `boolean` | whether `deviceType` is `tablet`                                                                                                                      | `true`                                        |
| `isDesktop`          | Desktop            | `boolean` | whether `deviceType` is `desktop`                                                                                                                     | `true`                                        |
| `requestUri`         | Request URI        | `string`  | the requested path with its query string                                                                                                              | `"/shop/"` with `startsWith`                  |
| `queryString`        | Query string       | `string`  | the query string, without the `?`                                                                                                                     | `"utm_source=newsletter"` with `contains`     |
| `httpMethod`         | HTTP method        | `string`  | the request method, such as `GET` or `POST`                                                                                                           | `"POST"`                                      |
| `userAgent`          | User agent         | `string`  | the browser's user agent string                                                                                                                       | `"Googlebot"` with `contains`                 |
| `referer`            | Referrer           | `string`  | the referring address, empty without one                                                                                                              | `"https://www.google.com/"` with `startsWith` |
| `ipAddress`          | IP address         | `string`  | the visitor's IP address, as described below                                                                                                          | `"203.0.113.7"`                               |
| `currentBlogId`      | Current site ID    | `number`  | the current site's ID                                                                                                                                 | `2`                                           |
| `isMultisite`        | Multisite          | `boolean` | whether WordPress runs a network                                                                                                                      | `true`                                        |
| `isMainSite`         | Main site          | `boolean` | whether this is the network's main site                                                                                                               | `true`                                        |
| `isNetworkAdmin`     | Network admin      | `boolean` | whether the request is in the network admin                                                                                                           | `true`                                        |
| `currentLanguage`    | Current language   | `string`  | the language code from WPML or Polylang, such as `en`, otherwise the site locale, such as `en_US`                                                     | `"de"`                                        |
| `option:<name>`      | none               | any       | the value of the WordPress option `<name>`                                                                                                            | `option:blog_public` with `"1"`               |
| `meta:<key>`         | none               | any       | the current post's meta value `<key>`                                                                                                                 | `meta:featured` with `"yes"`                  |
| `themeMod:<name>`    | none               | any       | the theme modification `<name>`                                                                                                                       | `themeMod:header_layout` with `"wide"`        |

The card offers `currentBlogId`, `isMainSite` and `isNetworkAdmin` only on a
multisite network. `option:`, `meta:` and `themeMod:` take the name after the
colon; they are not in the card's list, so write them in `snippet.json`, and
they compare whatever value WordPress stores.

The device, browser and system values come from the user agent string, which
browsers simplify: Windows 11 reports `windows-10` and current Macs report
`macos-catalina`, so match the family with `startsWith`, such as `windows` or
`macos`. Current Edge, Opera and Samsung Internet report `edge`, `opera` and
`samsung`, and Chrome, Firefox and Edge on iOS report `chrome`, `firefox` and
`edge`.

By default `ipAddress` is the direct connection address. Behind a trusted
reverse proxy that overwrites forwarded headers, define the constant in
`wp-config.php`:

```php
define('SLEEK_BYTE_TRUSTED_PROXY', true);
```

Byte then uses the first of `CF-Connecting-IP`, `Client-IP` and
`X-Forwarded-For` that is present, taking the first address of a list. Byte
checks only that the constant is defined, whatever its value. Do not define it
where visitors can send those headers directly, because they can be forged.

### Operators

The Conditions card offers the operators of the parameter's type, under these
labels; a parameter without a type, such as `option:<name>`, gets those of
`string`:

| Type           | Operators                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `boolean`      | `equals` (Equals)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `number`       | `equals` (Equals), `greaterThan` (Greater than), `greaterThanOrEqual` (Greater than or equal), `lessThan` (Less than), `lessThanOrEqual` (Less than or equal)                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `date`, `time` | `equals` (Equals), `greaterThan` (After), `greaterThanOrEqual` (On or after), `lessThan` (Before), `lessThanOrEqual` (On or before)                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `string`       | `equals` (Equals), `contains` (Contains), `startsWith` (Starts with), `endsWith` (Ends with), `lengthEquals` (Length equals), `lengthGreaterThan` (Length greater than), `lengthGreaterThanOrEqual` (Length greater than or equal), `lengthLessThan` (Length less than), `lengthLessThanOrEqual` (Length less than or equal), `wordCountEquals` (Word count equals), `wordCountGreaterThan` (Word count greater than), `wordCountGreaterThanOrEqual` (Word count greater than or equal), `wordCountLessThan` (Word count less than), `wordCountLessThanOrEqual` (Word count less than or equal) |

In `snippet.json` any operator works with any parameter, compared as follows;
an unknown operator does not match:

| Operators                                                                                                                 | Compare                                                                                                                     |
| ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `equals`                                                                                                                  | equality; with a list on either side, whether any item matches, compared as text                                            |
| `contains`, `startsWith`, `endsWith`                                                                                      | text containing, starting with or ending with the value                                                                     |
| `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual`                                                        | numbers, such as the hour of `timeOfDay`, and dates and times in their fixed formats, such as `currentTime` after `"09:00"` |
| `lengthEquals`, `lengthGreaterThan`, `lengthGreaterThanOrEqual`, `lengthLessThan`, `lengthLessThanOrEqual`                | the length of a text                                                                                                        |
| `wordCountEquals`, `wordCountGreaterThan`, `wordCountGreaterThanOrEqual`, `wordCountLessThan`, `wordCountLessThanOrEqual` | the number of words in a text                                                                                               |

Weekdays during business hours:

```json
{
  "conditions": [
    [
      {
        "parameter": "timeOfDay",
        "operator": "greaterThanOrEqual",
        "value": 9
      },
      { "parameter": "timeOfDay", "operator": "lessThan", "value": 17 },
      {
        "parameter": "dayOfWeek",
        "operator": "equals",
        "value": [1, 2, 3, 4, 5]
      }
    ]
  ]
}
```

Everyone except subscribers:

```json
{
  "conditions": [
    [
      {
        "parameter": "userRole",
        "operator": "equals",
        "value": "subscriber",
        "negate": true
      }
    ]
  ]
}
```

## External assets

**`assets`** loads stylesheets and scripts from other hosts, keyed by the
WordPress handle they register under. They load in the snippet's contexts,
before the snippet's own files, and follow the snippet's conditions.

```json
{
  "assets": {
    "alpinejs": {
      "url": "https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js",
      "version": "3",
      "dependencies": [],
      "local": true
    }
  }
}
```

| Key            | Meaning                                                                                                                                                                                         |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`          | The asset's address. A path ending in `.css` is a stylesheet; anything else is a script.                                                                                                        |
| `version`      | The version WordPress appends to the address.                                                                                                                                                   |
| `dependencies` | Handles that must load first.                                                                                                                                                                   |
| `module`       | `true` loads a script as a JavaScript module and adds it to the page's import map under its handle.                                                                                             |
| `local`        | `true` serves a copy from `uploads/sleek-byte-assets` instead of the remote address. Byte downloads it the first time a request needs it and keeps the remote address while the download fails. |

The configuration dialog's Assets card edits the same list, and its download
button fetches a copy straight away through `Actions_downloadAsset`, which
accepts only public HTTP and HTTPS addresses and files up to 5 MB.

## Per-file options

**`files`** maps a file's path inside the snippet to options that override the
snippet's for that file. The configuration dialog's Files card lists every
file of the snippet.

```json
{
  "context": ["frontend"],
  "files": {
    "admin/settings.php": { "context": ["admin"], "hook": "admin_init" },
    "assets/admin.css": { "context": ["admin"] },
    "assets/app.js": { "dependencies": ["jquery"], "module": true }
  }
}
```

| Key            | Meaning                                                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `context`      | Replaces the snippet's contexts for this file.                                                                           |
| `hook`         | Replaces the snippet's hook, for PHP files only.                                                                         |
| `dependencies` | Handles of scripts or stylesheets that must load first; see [Files](/docs/byte/snippets/files.md#handles-and-dependencies). |
| `module`       | `true` loads a JavaScript file as a module.                                                                              |
| `conditions`   | Replaces the snippet's conditions for this file.                                                                         |

A file's conditions replace the snippet's rather than adding to them. To
require both, repeat the snippet's conditions inside each of the file's
groups.

```json
{
  "hook": "wp",
  "conditions": [
    [
      { "parameter": "postType", "operator": "equals", "value": "product" },
      { "parameter": "isSingle", "operator": "equals", "value": true }
    ]
  ],
  "files": {
    "product/gallery.php": {},
    "checkout/fields.php": {
      "conditions": [
        [
          {
            "parameter": "requestUri",
            "operator": "contains",
            "value": "checkout"
          }
        ]
      ]
    }
  }
}
```

`product/gallery.php` loads on single product pages and runs on `wp`, because
`postType` and `isSingle` need the main query. `checkout/fields.php` carries its
own condition, so it loads on addresses that contain `checkout` instead.
