This commit is contained in:
PierreLapolla 2026-07-21 23:19:17 +02:00 committed by GitHub
commit 440b6ab16c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 2495 additions and 1 deletions

29
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Documentation
on:
push:
branches:
- master
- main
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/configure-pages@v6
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: 3.x
- run: pip install zensical
- run: zensical build --clean
- uses: actions/upload-pages-artifact@v5
with:
path: site
- uses: actions/deploy-pages@v5
id: deployment

168
.gitignore vendored
View file

@ -4,4 +4,170 @@ cmake-build-release/
.idea/
node_modules/
*.dSYM
.vs/
.vs/
/site
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

57
docs/ecosystem.md Normal file
View file

@ -0,0 +1,57 @@
---
icon: lucide/globe
---
# Ecosystem
## Bindings for non-C
Clay is usable out of the box as a `.h` include in both C99 and C++20 with designated initializer support. Maintained bindings for other languages:
- [Odin bindings](https://github.com/nicbarker/clay/tree/main/bindings/odin)
- [Rust bindings](https://github.com/clay-ui-rs/clay)
- [C++ bindings](https://github.com/nicbarker/clay/tree/main/bindings/cpp)
- [C# bindings](https://github.com/nicbarker/clay/tree/main/bindings/csharp)
- [D bindings](https://github.com/nicbarker/clay/tree/main/bindings/d)
- [Zig bindings](https://github.com/nicbarker/clay/tree/main/bindings/zig)
## Other implementations
Clay has also been reimplemented, rather than bound, in other languages:
- [`glay`](https://github.com/soypat/glay) — Go, line-by-line rewrite prioritizing readability.
- [`totallygamerjet/clay`](https://github.com/totallygamerjet/clay) — Go, via `cxgo` (a C to Go transpiler).
- [`goclay`](https://github.com/igadmg/goclay) — Go, line-by-line rewrite closely matching the reference implementation.
## Renderers
The [`renderers/`](https://github.com/nicbarker/clay/tree/main/renderers) directory contains reference implementations that turn a `Clay_RenderCommandArray` into actual pixels:
`cairo`, `GLES3`, `playdate`, `raylib`, `SDL2`, `SDL3`, `sokol`, `termbox2`, `terminal`, `web` (including the [HTML retained-mode renderer](https://github.com/nicbarker/clay/blob/main/renderers/web/html/clay-html-renderer.html)), `win32_gdi`.
## Examples
The [`examples/`](https://github.com/nicbarker/clay/tree/main/examples) directory pairs each renderer with runnable demos, including:
- `raylib-sidebar-scrolling-container`, `raylib-multi-context`, `raylib-transitions`
- `GLES3-SDL2-sidebar-scrolling-container`, `GLES3-SDL2-video-demo`, `GLES3-GLFW-video-demo`
- `SDL2-video-demo`, `SDL3-simple-demo`
- `sokol-video-demo`, `sokol-corner-radius`
- `cairo-pdf-rendering`
- `termbox2-demo`, `termbox2-image-demo`, `terminal-example`
- `win32_gdi`, `playdate-project-example`, `cpp-project-example`
- `shared-layouts`, `clay-official-website`, `introducing-clay-video-demo`
## Debug tools
Clay includes built-in UI debugging tools, similar to browser inspectors, injecting extra render commands into the output `Clay_RenderCommandArray`. No extra setup is required beyond a working renderer.
```c
Clay_SetDebugModeEnabled(true);
```
This is persistent and does not need to be set every frame. The debug panel renders on the right side of the screen by default, compressing the layout by its width (`400` px, configurable via the internal — and unstable — `Clay__debugViewWidth`).
## Community
For help or discussion, join the [Discord server](https://discord.gg/b4FTWkxdvT).

123
docs/getting-started.md Normal file
View file

@ -0,0 +1,123 @@
---
icon: lucide/rocket
---
# Getting started
## Installing
Clay is distributed as a single header file, `clay.h`, with no external dependencies. Download or clone it from the [Clay repository](https://github.com/nicbarker/clay) and include it after defining `CLAY_IMPLEMENTATION` in **exactly one** translation unit:
```c
// Must be defined in one file, _before_ #include "clay.h"
#define CLAY_IMPLEMENTATION
#include "../../clay.h"
```
Every other file that needs Clay's types and functions can `#include "clay.h"` without the `CLAY_IMPLEMENTATION` define.
## A minimal example
```c
const Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};
const Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255};
const Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255};
void HandleClayErrors(Clay_ErrorData errorData) {
// See the Clay_ErrorData struct for more information
printf("%s", errorData.errorText.chars);
switch(errorData.errorType) {
// etc
}
}
// Example measure text function
static inline Clay_Dimensions MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData) {
// Clay_TextElementConfig contains members such as fontId, fontSize, letterSpacing etc
// Note: Clay_String->chars is not guaranteed to be null terminated
return (Clay_Dimensions) {
.width = text.length * config->fontSize, // <- this will only work for monospace fonts, see the renderers/ directory for more advanced text measurement
.height = config->fontSize
};
}
// Layout config is just a struct that can be declared statically, or inline
Clay_ElementDeclaration sidebarItemConfig = (Clay_ElementDeclaration) {
.layout = {
.sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) }
},
.backgroundColor = COLOR_ORANGE
};
int main() {
// Note: malloc is only used here as an example, any allocator that provides
// a pointer to addressable memory of at least totalMemorySize will work
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
// Note: screenWidth and screenHeight will need to come from your environment, Clay doesn't handle window related tasks
Clay_Initialize(arena, (Clay_Dimensions) { screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors });
Clay_SetMeasureTextFunction(MeasureText, 0);
while (renderLoop()) { // Will be different for each renderer / environment
Clay_SetLayoutDimensions((Clay_Dimensions) { screenWidth, screenHeight });
Clay_SetPointerState((Clay_Vector2) { mousePositionX, mousePositionY }, isMouseDown);
Clay_UpdateScrollContainers(true, (Clay_Vector2) { mouseWheelX, mouseWheelY }, deltaTime);
Clay_BeginLayout();
CLAY(CLAY_ID("OuterContainer"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {
CLAY(CLAY_ID("SideBar"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 },
.backgroundColor = COLOR_LIGHT
}) {
CLAY(CLAY_ID("MainContent"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {}
}
}
Clay_RenderCommandArray renderCommands = Clay_EndLayout(); // deltaTime is used internally for transitions
for (int i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = &renderCommands.internalArray[i];
switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
DrawRectangle(renderCommand->boundingBox, renderCommand->renderData.rectangle.backgroundColor);
break;
}
// ... Implement handling of other command types
}
}
}
}
```
More comprehensive, renderer-specific versions of this example can be found in the [examples directory](https://github.com/nicbarker/clay/tree/main/examples).
## The frame lifecycle
Clay is stateless between frames except for its internal arena, so every frame follows the same fixed order of calls:
1. [`Clay_SetLayoutDimensions`](reference/functions.md#clay_setlayoutdimensions)
2. [`Clay_SetPointerState`](reference/functions.md#clay_setpointerstate)
3. [`Clay_UpdateScrollContainers`](reference/functions.md#clay_updatescrollcontainers)
4. [`Clay_BeginLayout`](reference/functions.md#clay_beginlayout)
5. Declare your layout with the [element macros](reference/macros.md)
6. [`Clay_EndLayout`](reference/functions.md#clay_endlayout)
7. Render the results from the returned [`Clay_RenderCommandArray`](reference/structures.md#clay_rendercommandarray)
Startup is a separate, one-time sequence:
`Clay_MinMemorySize``Clay_CreateArenaWithCapacityAndMemory``Clay_Initialize``Clay_SetMeasureTextFunction`
!!! warning "The measure text function is on the hot path"
`Clay_SetMeasureTextFunction` takes a pointer to a function that Clay calls many times per layout for text-heavy UIs. Despite Clay's internal text measurement cache, a slow measure function can easily dominate total layout cost.
## Naming conventions
- `CAPITAL_LETTERS()` are macros.
- `Clay__` (double underscore) marks internal functions not intended for external use, and subject to change without notice.
- `Clay_` (single underscore) marks the public API.
## Next steps
Continue with [Building UI hierarchies](guide/building-ui.md) to learn how the `CLAY()` macro composes into full layouts.

57
docs/guide/advanced.md Normal file
View file

@ -0,0 +1,57 @@
---
icon: lucide/settings-2
---
# Advanced topics
## Retained mode rendering
Clay was originally designed for [immediate mode](https://www.youtube.com/watch?v=Z1qyvQsjK5Y) rendering, where the entire UI is redrawn every frame. If your platform, renderer, or performance constraints require retained mode instead, two techniques help:
- Each [`Clay_RenderCommand`](../reference/structures.md#clay_rendercommand) includes the `uint32_t id` used to declare the element. With unique IDs, you can map these to persistent graphics objects across frames.
- Render commands are culled to only currently visible elements, and `Clay_RenderCommand` is small enough to memcmp two commands with matching IDs to detect whether an element is "dirty" and needs re-rendering.
See the [HTML renderer](https://github.com/nicbarker/clay/blob/main/renderers/web/html/clay-html-renderer.html) for a worked example that converts Clay layouts into persistent HTML documents with minimal per-frame changes.
## Visibility culling
Clay culls output by default: it only emits render commands for elements with at least one pixel of their bounding box inside the viewport. Disable this with `#define CLAY_DISABLE_CULLING`.
## Preprocessor directives
Set these either in code (`#define CLAY_DISABLE_CULLING`) or via compiler flags (`clang -DCLAY_DISABLE_CULLING main.c`):
- **`CLAY_WASM`** — required when targeting WebAssembly.
- **`CLAY_DLL`** — required when building a `.dll`.
- **`CLAY_DISABLE_CULLING`** — disables [visibility culling](#visibility-culling).
## Running more than one Clay instance
Clay supports multiple instances in the same program. [`Clay_Initialize`](../reference/functions.md#clay_initialize) returns a `Clay_Context*`. Activate a specific instance with [`Clay_SetCurrentContext`](../reference/functions.md#clay_setcurrentcontext) — if never called, Clay defaults to the most recently initialized context.
```c
Clay_Arena arena1, arena2;
// ... allocate arenas
Clay_Context* instance1 = Clay_Initialize(arena1, layoutDimensions, errorHandler);
Clay_Context* instance2 = Clay_Initialize(arena2, layoutDimensions, errorHandler);
Clay_SetCurrentContext(instance1);
Clay_BeginLayout();
// ... declare layout for instance1
Clay_RenderCommandArray renderCommands1 = Clay_EndLayout();
render(renderCommands1);
Clay_SetCurrentContext(instance2);
Clay_BeginLayout();
// ... declare layout for instance2
Clay_RenderCommandArray renderCommands2 = Clay_EndLayout();
render(renderCommands2);
```
!!! warning "Not thread-safe"
Do not render instances across different threads simultaneously — Clay does not currently support multi-threading.
## Sizing an arena for a larger UI
If your layout exceeds the default element count or text cache, use [`Clay_SetMaxElementCount`](../reference/functions.md#clay_setmaxelementcount) or [`Clay_SetMaxMeasureTextCacheWordCount`](../reference/functions.md#clay_setmaxmeasuretextcachewordcount), then call [`Clay_MinMemorySize()`](../reference/functions.md#clay_minmemorysize) again and reinitialize with the new size. See [Error handling](../reference/errors.md) for the errors Clay raises when capacity is exceeded.

114
docs/guide/building-ui.md Normal file
View file

@ -0,0 +1,114 @@
---
icon: lucide/layers
---
# Building UI hierarchies
## The CLAY() macro
Clay UIs are built using the C macro `CLAY(id, { configuration })`. This macro creates a new, empty element in the UI hierarchy and supports modular customization of layout, styling, and functionality. `CLAY()` can be nested, similar to other declarative UI systems like HTML.
Child elements are added by opening a block (`{}`) after calling `CLAY()`, exactly like you would with an `if` statement or `for` loop:
```c
// Parent element with 8px of padding
CLAY(CLAY_ID("parent"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
// Child element 1
CLAY_TEXT(CLAY_STRING("Hello World"), { .fontSize = 16 });
// Child element 2 with red background
CLAY(CLAY_ID("child"), { .backgroundColor = COLOR_RED }) {
// etc
}
}
```
Unlike HTML and other declarative DSLs, this macro is just C. You can use arbitrary C code — loops, functions, conditionals — inside your layout declaration:
```c
// Re-usable "components" are just functions that declare more UI
void ButtonComponent(Clay_String buttonText) {
// Red box button with 8px of padding
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(8) }, .backgroundColor = COLOR_RED }) {
CLAY_TEXT(buttonText, textConfig);
}
}
// Parent element
CLAY(CLAY_ID("parent"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Render a bunch of text elements
for (int i = 0; i < textArray.length; i++) {
CLAY_TEXT(textArray.elements[i], textConfig);
}
// Only render this element if we're on a mobile screen
if (isMobileScreen) {
CLAY(0) {
// etc
}
}
// Re-usable components
ButtonComponent(CLAY_STRING("Click me!"));
ButtonComponent(CLAY_STRING("No, click me!"));
}
```
## Configuring layout and styling
The layout and style of an element is configured with the [`Clay_ElementDeclaration`](../reference/structures.md#clay_elementdeclaration) struct passed to `CLAY()`:
```c
CLAY(CLAY_ID("box"), { .layout = { .padding = { 8, 8, 8, 8 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Children are 8px inset into parent, and laid out top to bottom
}
```
This macro isn't magic — it just wraps standard designated initializer syntax. A `Clay_ElementDeclaration` can also be defined in file scope and reused across elements:
```c
// Define a style in the global / file scope
Clay_ElementDeclaration reuseableStyle = (Clay_ElementDeclaration) {
.layout = { .padding = { .left = 12 } },
.backgroundColor = { 120, 120, 120, 255 },
.cornerRadius = { 12, 12, 12, 12 }
};
CLAY(CLAY_ID("box"), reuseableStyle) {
// ...
}
```
See [Layout and styling](styling-layout.md) for the full set of layout options.
## Element IDs
The `CLAY()` macro accepts an ID as its first argument, usually provided by [`CLAY_ID()`](../reference/macros.md#clay_id). Elements can also be created with auto-generated IDs using [`CLAY_AUTO_ID()`](../reference/macros.md#clay_auto_id):
```c
// Will always produce the same ID from the same input string
CLAY(CLAY_ID("OuterContainer"), { ...configuration }) {}
// Generates a unique ID that may not be the same between two layout calls
CLAY_AUTO_ID({ ...configuration }) {}
```
IDs serve two purposes:
1. **Querying** — tagging an element with an ID lets you later ask about its [hover state](../reference/functions.md#clay_hovered) or dimensions.
2. **Readability** — IDs make it easier to read and modify UI code, and are used by the built-in [debug tools](../ecosystem.md#debug-tools).
To avoid constructing dynamic strings at runtime inside loops, use [`CLAY_IDI(string, index)`](../reference/macros.md#clay_idi) ("**ID** + **I**ndex") to generate different IDs from a single input string:
```c
// This is the equivalent of calling CLAY_ID("Item0"), CLAY_ID("Item1") etc
for (int index = 0; index < items.length; index++) {
CLAY(CLAY_IDI("Item", index), { ..configuration }) {}
}
```
This ID is forwarded to the final [`Clay_RenderCommandArray`](../reference/structures.md#clay_rendercommandarray) for use in retained mode UIs. **Duplicate IDs may cause some functionality to misbehave** — for example, if you're attaching a [floating container](floating-elements.md) to a specific element by ID and that ID is duplicated, it may not attach where you expect.
For components and loops where an ID only needs to be unique among siblings (not globally), use [`CLAY_ID_LOCAL()`](../reference/macros.md#clay_id_local) instead.
## Next steps
- [Layout and styling](styling-layout.md) — sizing, padding, alignment, and text
- [Mouse and pointer interactions](interactions.md) — hover and click handling

View file

@ -0,0 +1,58 @@
---
icon: lucide/puzzle
---
# Custom elements
Clay only ships primitives for rectangles, text, borders, and images. `.custom` ([`Clay_CustomElementConfig`](../reference/structures.md#clay_customelementconfig)) is Clay's escape hatch for anything else — 3D models, video players, or other renderer-specific content:
```c
typedef enum {
CUSTOM_ELEMENT_TYPE_MODEL,
CUSTOM_ELEMENT_TYPE_VIDEO
} CustomElementType;
typedef struct {
CustomElementType type;
union {
Model model;
Video video;
};
} CustomElementData;
```
Custom elements only take a single `void *`, so extended data needs to live somewhere that outlives the current layout call — typically a per-frame arena:
```c
typedef struct {
void* memory;
uintptr_t offset;
} Arena;
// During init
Arena frameArena = (Arena) { .memory = malloc(1024) };
// During layout
CustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
frameArena.offset += sizeof(CustomElementData);
CLAY(CLAY_ID("3DModelViewer"), { .custom = { .customData = modelData } }) {}
```
The pointer is passed straight through to the render command:
```c
switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
CustomElementData *customElement = renderCommand->renderData.custom.customData;
if (!customElement) continue;
switch (customElement->type) {
case CUSTOM_ELEMENT_TYPE_MODEL: /* render your 3d model here */ break;
case CUSTOM_ELEMENT_TYPE_VIDEO: /* render your video here */ break;
}
break;
}
}
```
Custom elements are subject to [visibility culling](advanced.md#visibility-culling); otherwise a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM` is emitted per element.

View file

@ -0,0 +1,72 @@
---
icon: lucide/layers-2
---
# Floating elements ("absolute" positioning)
Standard Clay elements are laid out on top of, and _within_, their parent, positioned according to the parent's layout rules, and they affect sibling and parent sizing. **Floating** elements are the exception — configured with `.floating`, they:
- Attach by default to the top-left corner of their "parent"
- Don't affect the width, height, or child positioning of their parent
- Don't affect the positioning of sibling elements
- Have a **z-index**, and can appear above or below other elements
- Otherwise behave like standard elements — expanding to fit children, etc.
The easiest mental model: a floating container is a **separate UI hierarchy**, attached to a specific x/y point on its "parent". Typical use cases are tooltips and modals.
```c
CLAY(CLAY_ID("Outer"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_TEXT(text, &headerTextConfig);
CLAY(CLAY_ID("Tooltip"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }) {}
CLAY_TEXT(text, &headerTextConfig);
}
```
## Key fields ([`Clay_FloatingElementConfig`](../reference/structures.md#clay_floatingelementconfig))
- **`.offset`** — position offset applied *after* layout is calculated.
- **`.expand`** — expands width/height of the floating container *before* laying out children.
- **`.zIndex`** — floating elements (and their subtrees) are sorted by `.zIndex` before conversion to render commands; higher draws on top.
- **`.attachPoints`** — `{ .element, .parent }` pair that specifies which point on the floating container lines up with which point on the parent (e.g. `CLAY_ATTACH_POINT_LEFT_BOTTOM` on the element attached to `CLAY_ATTACH_POINT_CENTER_CENTER` on the parent).
- **`.pointerCaptureMode`** — `CLAY_POINTER_CAPTURE_MODE_CAPTURE` (default) or `CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH`, controlling whether hover/click pass through to content underneath.
- **`.attachTo`** — `CLAY_ATTACH_TO_NONE` (default), `CLAY_ATTACH_TO_PARENT`, `CLAY_ATTACH_TO_ELEMENT_WITH_ID`, `CLAY_ATTACH_TO_ROOT`.
- **`.clipTo`** — `CLAY_CLIP_TO_NONE` (default) or `CLAY_CLIP_TO_ATTACHED_PARENT`, to clip the floating element by its parent's `.clip` rectangle instead of always rendering above it.
`Clay_FloatingElementConfig` does not generate any render commands of its own.
## Attaching by ID instead of by declaration position
By default, a floating container attaches to whatever element it's declared inside. `.parentId` lets you attach it to **any** element by ID, declared anywhere in the hierarchy — before or after the floating container itself, and even to an ID that doesn't exist yet when the floating config is written:
```c
// Instead of conditionally nesting a tooltip inside every possible button...
CLAY(CLAY_IDI("SidebarButton", 1), { }) { /* ... */ }
CLAY(CLAY_IDI("SidebarButton", 2), { }) { /* ... */ }
// ...
// ...declare it once, anywhere, and point at the target by id:
CLAY(CLAY_ID("OptionTooltip"), {
.floating = {
.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,
.parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id
}
}) {
// Tooltip contents...
}
```
This avoids polluting the layout declaration of many elements just to conditionally attach a tooltip to one of them.
## Example
```c
CLAY(CLAY_ID("OptionsList"), { .layout = { .childGap = 16 } }) {
CLAY(CLAY_IDI("Option", 2), { .layout = { .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = COLOR_BLUE }) {
CLAY_TEXT(CLAY_STRING("Option 2"), {});
// Attaches above "Option 2" without affecting sibling widths or positions
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .zIndex = 1, .attachPoints = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP } } }) {
CLAY_TEXT(CLAY_STRING("Most popular!"), {});
}
}
}
```

View file

@ -0,0 +1,85 @@
---
icon: lucide/mouse-pointer-click
---
# Mouse and pointer interactions
All pointer interactions depend on [`Clay_SetPointerState(position, isPointerDown)`](../reference/functions.md#clay_setpointerstate) being called every frame, before any other Clay functions.
## During UI declaration
### Clay_Hovered
`bool Clay_Hovered()` can be called during element construction, or in the body of an element, and returns `true` if the pointer is over the currently open element:
```c
// An orange button that turns blue when hovered
CLAY(CLAY_ID("Button"), { .backgroundColor = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }) {
bool buttonHovered = Clay_Hovered();
CLAY_TEXT(buttonHovered ? CLAY_STRING("Hovered") : CLAY_STRING("Hover me!"), headerTextConfig);
}
```
### Clay_OnHover
`void Clay_OnHover(handler, userData)` attaches a function pointer to the currently open element, called once per layout while the pointer is over it:
```c
void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData) {
ButtonData *buttonData = (ButtonData *)userData;
// Pointer state allows you to detect mouse down / hold / release
if (pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
NavigateTo(buttonData->link);
}
}
ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" };
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
Clay_OnHover(HandleButtonInteraction, &linkButton);
CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig);
}
```
## Before / after UI declaration
To query mouse overlap outside of layout declarations, use `bool Clay_PointerOver(Clay_ElementId id)`:
```c
// Reminder: Clay_SetPointerState must be called before functions that rely on pointer position otherwise it will have no effect
Clay_Vector2 mousePosition = { x, y };
Clay_SetPointerState(mousePosition, mouseButtonDown(0));
// ...
if (mouseButtonDown(0) && Clay_PointerOver(Clay_GetElementId("ProfilePicture"))) {
// Handle profile picture clicked
}
```
!!! note "One frame of latency"
`Clay_PointerOver` uses the bounding box from the **last** frame. This rarely matters except for fast-moving animations. If frame-accurate detection is required, run your layout code twice per frame with the same data — the second call will be accurate.
## Pointer interaction states
[`Clay_PointerData`](../reference/structures.md#clay_pointerdata) reports one of four states via `.state`:
```c
typedef enum {
CLAY_POINTER_DATA_PRESSED_THIS_FRAME,
CLAY_POINTER_DATA_PRESSED,
CLAY_POINTER_DATA_RELEASED_THIS_FRAME,
CLAY_POINTER_DATA_RELEASED,
} Clay_PointerDataInteractionState;
```
For example, a user hovering a button, clicking, holding, then releasing:
1. Hovering, button not pressed: `CLAY_POINTER_DATA_RELEASED`
2. First frame the button is pressed: `CLAY_POINTER_DATA_PRESSED_THIS_FRAME`
3. Subsequent frames held down: `CLAY_POINTER_DATA_PRESSED`
4. The frame the button is released: `CLAY_POINTER_DATA_RELEASED_THIS_FRAME`
5. Subsequent frames while still hovering: `CLAY_POINTER_DATA_RELEASED`
## Next steps
- [Scrolling elements](scrolling.md) builds on `Clay_SetPointerState` for drag scrolling.
- [Floating elements](floating-elements.md) covers `.pointerCaptureMode` for tooltips and modals.

63
docs/guide/scrolling.md Normal file
View file

@ -0,0 +1,63 @@
---
icon: lucide/scroll
---
# Scrolling elements
Elements become scrollable via the `.clip` configuration ([`Clay_ClipElementConfig`](../reference/structures.md#clay_clipelementconfig)). Clipping instructs the renderer not to draw pixels outside the clipped element's bounds; specifying `.childOffset` shifts the clipped content to produce scrolling.
You can calculate scroll offsets yourself and pass them to `.childOffset` each frame, or let Clay manage scroll state for you.
## Built-in scroll tracking
Two functions must be called **before** `Clay_BeginLayout()` each frame to enable mouse wheel and drag scrolling:
```c
Clay_Vector2 mousePosition = { x, y };
// Reminder: Clay_SetPointerState must be called before Clay_UpdateScrollContainers otherwise it will have no effect
Clay_SetPointerState(mousePosition, isPointerDown);
// Clay_UpdateScrollContainers needs to be called before Clay_BeginLayout for the position to avoid a 1 frame delay
Clay_UpdateScrollContainers(
true, // Enable drag scrolling
scrollDelta, // Clay_Vector2 scrollwheel / trackpad scroll x and y delta this frame
deltaTime, // Time since last frame in seconds
);
```
Then use [`Clay_GetScrollOffset()`](../reference/functions.md#clay_getscrolloffset) to read the internally tracked offset for the currently open element:
```c
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
// Scrolling contents
}
```
## Managing scroll position yourself
`.childOffset` can also be provided directly, bypassing Clay's internal tracking:
```c
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {
// Scrolling contents
}
```
To disable drag/touch scrolling while still processing mouse wheel input, call `Clay_UpdateScrollContainers` with `false` as the first argument.
## Reading and mutating scroll state
[`Clay_GetScrollContainerData(id)`](../reference/functions.md#clay_getscrollcontainerdata) returns [`Clay_ScrollContainerData`](../reference/structures.md#clay_scrollcontainerdata), which includes a **live pointer** to the internal scroll position. This is intended for building scroll bars, "jump to" buttons, or auto-scroll — mutating `.scrollPosition` shifts the container's contents on the next layout.
## Rendering clip elements
Enabling `.clip` emits two extra render commands, neither subject to [visibility culling](advanced.md#visibility-culling):
- `CLAY_RENDER_COMMAND_TYPE_SCISSOR_START` — create a rectangle mask with `.boundingBox`
- `CLAY_RENDER_COMMAND_TYPE_SCISSOR_END` — disable the previous mask
```c
CLAY(CLAY_ID("ScrollOuter"), { .clip = { .vertical = true } }) {
// Fixed-height child content larger than the container triggers clipping
CLAY(CLAY_ID("ScrollInner"), { .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}
}
```

View file

@ -0,0 +1,143 @@
---
icon: lucide/ruler
---
# Layout and styling
Layout options — settings that affect the final position and size of an element, its parents, siblings, and children — live under `.layout` in [`Clay_LayoutConfig`](../reference/structures.md#clay_layoutconfig). Visual-only options such as `.backgroundColor` and `.cornerRadius` live directly on [`Clay_ElementDeclaration`](../reference/structures.md#clay_elementdeclaration).
## Layout direction
`.layoutDirection` controls the axis along which children are laid out: `CLAY_LEFT_TO_RIGHT` (default) or `CLAY_TOP_TO_BOTTOM`.
```c
CLAY(CLAY_ID("Element"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } })
```
## Padding
`.padding` controls white-space around the **outside** of child elements:
```c
CLAY(CLAY_ID("Element"), { .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } })
```
## Child gap
`.childGap` controls white-space **between** child elements. It applies horizontally when `.layoutDirection` is `CLAY_LEFT_TO_RIGHT` (default), and vertically for `CLAY_TOP_TO_BOTTOM`:
```c
CLAY(CLAY_ID("Element"), { .layout = { .childGap = 16 } })
```
## Child alignment
`.childAlignment` positions children relative to the height and width of the parent container:
```c
CLAY(CLAY_ID("Element"), { .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } })
```
Available values:
```c
.x = CLAY_ALIGN_X_LEFT (default) | CLAY_ALIGN_X_CENTER | CLAY_ALIGN_X_RIGHT;
.y = CLAY_ALIGN_Y_TOP (default) | CLAY_ALIGN_Y_CENTER | CLAY_ALIGN_Y_BOTTOM;
```
## Sizing
`.sizing` controls how the final width and height of an element are calculated. The same options apply to both `.width` and `.height`:
- **`CLAY_SIZING_FIT(min, max)`** (default) — sized to fit children (plus padding and gaps), up to `max` (defaults to `FLOAT_MAX`). Won't shrink below `min` when compressed.
- **`CLAY_SIZING_GROW(min, max)`** — grows to fill available space in its parent, up to `max` (defaults to `FLOAT_MAX`). Won't shrink below `min` when compressed.
- **`CLAY_SIZING_FIXED(fixed)`** — always exactly `fixed`. Shorthand for `CLAY_SIZING_FIT(fixed, fixed)`.
- **`CLAY_SIZING_PERCENT(percent)`** — a percentage (`0``1`) of parent size, minus padding and child gaps.
```c
CLAY(CLAY_ID("Button"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 } }) {
// Children will be laid out vertically with 16px of padding around and between
}
```
## Background color, corner radius, and overlays
```c
CLAY(CLAY_ID("Element"), { .backgroundColor = { 120, 120, 120, 255 } })
CLAY(CLAY_ID("Element"), { .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })
```
Use the `CLAY_CORNER_RADIUS(radius)` shorthand to set all four corners at once: `CLAY_CORNER_RADIUS(10)`.
`.overlayColor` blends a color over an element and its children — similar to GLSL's `mix(source, target, alpha)`. Zero alpha leaves child colors unchanged; full alpha replaces them entirely. This is a cheap way to fade elements in and out without true alpha compositing:
```c
CLAY(CLAY_ID("Element"), { .overlayColor = { 255, 120, 120, 255 } })
```
Specifying `.overlayColor` emits two extra render commands: `OVERLAY_COLOR_BEGIN` before the subtree, and `OVERLAY_COLOR_END` after it.
## Text
Text is rendered with `CLAY_TEXT(text, config)`, using [`Clay_TextElementConfig`](../reference/structures.md#clay_textelementconfig):
```c
CLAY_TEXT(CLAY_STRING("John Smith"), { .fontId = FONT_ID_LATO, .fontSize = 24, .textColor = {255, 0, 0, 255} });
```
Key fields:
| Field | Type | Notes |
|---|---|---|
| `.textColor` | `Clay_Color` | Conventionally `rgba` 0255 |
| `.fontId` | `uint16_t` | Mapped to a real font in user code |
| `.fontSize` | `uint16_t` | Generally "x pixels tall" |
| `.letterSpacing` | `uint16_t` | Horizontal space between characters |
| `.lineHeight` | `uint16_t` | Forces wrapped line height; `0` uses the measured font height |
| `.wrapMode` | enum | `CLAY_TEXT_WRAP_WORDS` (default), `CLAY_TEXT_WRAP_NEWLINES`, `CLAY_TEXT_WRAP_NONE` |
| `.textAlignment` | enum | `CLAY_TEXT_ALIGN_LEFT` (default), `CLAY_TEXT_ALIGN_CENTER`, `CLAY_TEXT_ALIGN_RIGHT` — controls alignment of **wrapping** lines; for single-line alignment use the parent's `.childAlignment` |
Text elements are subject to [visibility culling](advanced.md#visibility-culling) and may emit multiple `Clay_RenderCommand`s — one per wrapped line.
## Images and aspect ratio
`.image` configures an element to render an image background, via [`Clay_ImageElementConfig`](../reference/structures.md#clay_imageelementconfig):
```c
CLAY(CLAY_ID("Image"), { .image = { .imageData = &profilePicture } }) {}
```
`.imageData` is an opaque `void *` your renderer resolves to an actual image/texture. To preserve an image's original aspect ratio while scaling, combine it with `.aspectRatio` ([`Clay_AspectRatioElementConfig`](../reference/structures.md#clay_aspectratioelementconfig)):
```c
CLAY(CLAY_ID("ProfilePicture"), {
.layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } },
.aspectRatio = profilePicture.width / profilePicture.height,
.image = { .imageData = &profilePicture },
}) {}
```
## Borders
`.border` draws colored lines along an element's edges, via [`Clay_BorderElementConfig`](../reference/structures.md#clay_borderelementconfig). Border width is inset and **does not affect layout** — it overlaps content rather than adding to it:
```c
// 1px red border around all edges
CLAY(CLAY_ID("OuterBorder"), {
.layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) } },
.border = { .width = { 1, 1, 1, 1, 0 }, .color = COLOR_RED }
}) {}
// 5px blue border between top-to-bottom children
CLAY(CLAY_ID("OuterBorder"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
.border = { .width = { 5, 5, 5, 5, 5 }, .color = COLOR_BLUE }
}) {}
```
`.width.betweenChildren` draws lines between siblings — vertical for `CLAY_LEFT_TO_RIGHT` parents, horizontal for `CLAY_TOP_TO_BOTTOM`. Unlike the edge widths, these generate additional rectangle render commands, so the renderer needs no special handling for them.
## Next steps
- [Mouse and pointer interactions](interactions.md)
- [Scrolling elements](scrolling.md)

83
docs/guide/transitions.md Normal file
View file

@ -0,0 +1,83 @@
---
icon: lucide/wand-sparkles
---
# Transitions
Clay's transition API smoothly animates between states for both layout-affecting properties (`width`, `height`, position) and non-layout properties (`backgroundColor`, `overlayColor`, etc). Transitions activate as soon as `.transition.handler` is set:
```c
// Note: for transitions to work, elements need a stable ID from one frame to the next —
// using loop indexes or CLAY_AUTO_ID will not work.
CLAY(CLAY_IDI("box", colors[index].id), {
.layout.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() },
.layout.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },
.backgroundColor = boxColor,
.overlayColor = Clay_Hovered() ? (Clay_Color) { 140, 140, 140, 80 } : (Clay_Color) { 255, 255, 255, 0 },
.transition = {
.handler = Clay_EaseOut,
.duration = 0.5f,
// Bitwise OR to transition multiple properties
.properties = CLAY_TRANSITION_PROPERTY_WIDTH
| CLAY_TRANSITION_PROPERTY_POSITION
| CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR
| CLAY_TRANSITION_PROPERTY_BACKGROUND_COLOR,
.enter = { .setInitialState = EnterExitSlideUp },
.exit = { .setFinalState = EnterExitSlideUp },
}
}) {
CLAY_TEXT(CLAY_STRING("Animated Box"), { .fontSize = 32 });
}
```
## Fields ([`Clay_TransitionElementConfig`](../reference/structures.md#clay_transitionelementconfig))
- **`.handler`** — called each frame during a transition to compute current state; returns `true` when finished. Clay ships `Clay_EaseOut`, a standard [ease-out](https://easings.net/) curve. Write your own by inspecting `arguments.initial`, `.target`, `.elapsedTime`, and `.duration`.
- **`.duration`** — time in seconds to reach the target state.
- **`.properties`** — bitfield of which properties to animate; anything not listed snaps immediately.
- **`.interactionHandling`** — `CLAY_TRANSITION_DISABLE_INTERACTIONS_WHILE_TRANSITIONING_POSITION` (default) or `CLAY_TRANSITION_ALLOW_INTERACTIONS_WHILE_TRANSITIONING_POSITION`. Controls whether functions like `Clay_Hovered()` respond while an element's position is animating.
### Transition properties
```c
CLAY_TRANSITION_PROPERTY_NONE (default)
CLAY_TRANSITION_PROPERTY_X
CLAY_TRANSITION_PROPERTY_Y
CLAY_TRANSITION_PROPERTY_POSITION = X | Y
CLAY_TRANSITION_PROPERTY_WIDTH
CLAY_TRANSITION_PROPERTY_HEIGHT
CLAY_TRANSITION_PROPERTY_DIMENSIONS = WIDTH | HEIGHT
CLAY_TRANSITION_PROPERTY_BOUNDING_BOX = POSITION | DIMENSIONS
CLAY_TRANSITION_PROPERTY_BACKGROUND_COLOR
CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR
CLAY_TRANSITION_PROPERTY_CORNER_RADIUS
CLAY_TRANSITION_PROPERTY_BORDER_COLOR
CLAY_TRANSITION_PROPERTY_BORDER_WIDTH
CLAY_TRANSITION_PROPERTY_BORDER = BORDER_COLOR | BORDER_WIDTH
```
## Enter and exit transitions
**Enter** transitions fire the first frame an element appears; **exit** transitions fire the frame an element disappears from the tree. Both require a function pointer to be set — omit it and no enter/exit animation occurs.
```c
Clay_TransitionData EnterSlideUp(Clay_TransitionData initialState, Clay_TransitionProperty properties) {
Clay_TransitionData targetState = initialState;
if (properties & CLAY_TRANSITION_PROPERTY_Y) {
targetState.boundingBox.y += 20;
}
if (properties & CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR) {
targetState.overlayColor = (Clay_Color) { 255, 255, 255, 255 }; // fade in
}
return targetState;
}
```
- **`.enter.setInitialState`** — modifies the first-frame state to create the entry animation (e.g. offset Y to slide up, or set an overlay color to fade in).
- **`.enter.trigger`** — `CLAY_TRANSITION_ENTER_SKIP_ON_FIRST_PARENT_FRAME` (default, avoids all list items animating in at once when their parent first appears) or `CLAY_TRANSITION_ENTER_TRIGGER_ON_FIRST_PARENT_FRAME`.
- **`.exit.setFinalState`** — mirror of `.enter.setInitialState` for the element's last frame.
- **`.exit.trigger`** — `CLAY_TRANSITION_EXIT_SKIP_WHEN_PARENT_EXITS` (default) or `CLAY_TRANSITION_EXIT_TRIGGER_WHEN_PARENT_EXITS`.
- **`.exit.siblingOrdering`** — z-order of exiting elements relative to siblings: `CLAY_EXIT_TRANSITION_ORDERING_UNDERNEATH_SIBLINGS` (default), `CLAY_EXIT_TRANSITION_ORDERING_NATURAL_ORDER`, or `CLAY_EXIT_TRANSITION_ORDERING_ABOVE_SIBLINGS`.
!!! warning "Stable IDs required"
Transitions correlate an element across frames by its ID. Elements using `CLAY_AUTO_ID()` or loop-index-derived IDs will not transition correctly — use [`CLAY_IDI()`](../reference/macros.md#clay_idi) with a stable index/key instead.

32
docs/index.md Normal file
View file

@ -0,0 +1,32 @@
---
icon: lucide/home
---
# Clay
**Clay** (short for **C Layout**) is a high performance 2D UI layout library.
## Major features
- Microsecond layout performance
- Flex-box like layout model for complex, responsive layouts including text wrapping, scrolling containers and aspect ratio scaling
- Transition API for easy layout animations
- Single ~4.8k LOC `clay.h` file with **zero** dependencies (including no standard library linking)
- Wasm support: compile with clang to a 15kb uncompressed `.wasm` file for use in the browser
- Static arena based memory use with no malloc / free, and low total memory overhead (e.g. ~3.5mb for 8192 layout elements)
- React-like nested declarative syntax
- Renderer agnostic: outputs a sorted list of rendering primitives that can be easily composited in any 3D engine, and even compiled to HTML
Take a look at the [clay website](https://nicbarker.com/clay) for an example of Clay compiled to Wasm and running in the browser, or browse the [examples directory](https://github.com/nicbarker/clay/tree/main/examples) for renderer-specific demos.
You can also watch the [introduction video](https://youtu.be/DYWTw19_8r4) for an overview of the motivation behind Clay's development and a short demo of its usage.
## Where to go next
- [Getting started](getting-started.md) — install Clay and run your first layout
- [Building UI hierarchies](guide/building-ui.md) — the `CLAY()` macro and element IDs
- [API reference](reference/functions.md) — full function, macro, and struct reference
- [Ecosystem](ecosystem.md) — renderers, language bindings, and other implementations
!!! note "Single source header"
Clay ships as a single `clay.h` header. There is nothing to build or link — `#define CLAY_IMPLEMENTATION` once, then `#include "clay.h"`.

View file

@ -0,0 +1,6 @@
document$.subscribe(function () {
var el = document.getElementById("copyright-year")
if (el) {
el.textContent = new Date().getFullYear()
}
})

70
docs/reference/errors.md Normal file
View file

@ -0,0 +1,70 @@
---
icon: lucide/triangle-alert
---
# Error handling
Clay reports internal problems through a single callback rather than return codes, so error handling stays out of the hot layout path.
## Clay_ErrorHandler
```c
typedef struct {
void (*errorHandlerFunction)(Clay_ErrorData errorText);
uintptr_t userData;
} Clay_ErrorHandler;
```
- **`.errorHandlerFunction`** — called whenever Clay encounters an internal error.
- **`.userData`** — generic pointer transparently passed from [`Clay_Initialize`](functions.md#clay_initialize) through to the callback. Defaults to `NULL`.
```c
void HandleClayErrors(Clay_ErrorData errorData) {
printf("%s", errorData.errorText.chars);
switch (errorData.errorType) {
// handle by type, see below
}
}
Clay_Initialize(arena, dimensions, (Clay_ErrorHandler) { HandleClayErrors });
```
## Clay_ErrorData
```c
typedef struct {
Clay_ErrorType errorType;
Clay_String errorText;
uintptr_t userData;
} Clay_ErrorData;
```
- **`.errorText`** — human-readable description. May change between versions; don't pattern-match on it.
- **`.userData`** — same value passed to `Clay_Initialize`.
## Clay_ErrorType
```c
typedef enum {
CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,
CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,
CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,
CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,
CLAY_ERROR_TYPE_DUPLICATE_ID,
CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,
CLAY_ERROR_TYPE_INTERNAL_ERROR,
} Clay_ErrorType;
```
| Error | Cause | Fix |
|---|---|---|
| `TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED` | `CLAY_TEXT` used without calling [`Clay_SetMeasureTextFunction`](functions.md#clay_setmeasuretextfunction), or a null pointer was passed | Set a valid measure function before laying out text |
| `ARENA_CAPACITY_EXCEEDED` | Arena too small for the configured [`Clay_SetMaxElementCount`](functions.md#clay_setmaxelementcount) | Recompute the required size with [`Clay_MinMemorySize()`](functions.md#clay_minmemorysize) |
| `ELEMENTS_CAPACITY_EXCEEDED` | UI hierarchy exceeds the configured max element count | Raise the max with `Clay_SetMaxElementCount`, then re-derive memory size and reinitialize |
| `TEXT_MEASUREMENT_CAPACITY_EXCEEDED` | UI hierarchy has more text than the configured measure-text cache | Raise the max with [`Clay_SetMaxMeasureTextCacheWordCount`](functions.md#clay_setmaxmeasuretextcachewordcount), then re-derive memory size and reinitialize |
| `DUPLICATE_ID` | Two elements declared with exactly the same ID | Set a breakpoint in your error handler for a stack trace back to the offending declaration |
| `FLOATING_CONTAINER_PARENT_NOT_FOUND` | A floating element's `.parentId` matched no element | Set a breakpoint in your error handler for a stack trace |
| `INTERNAL_ERROR` | Clay hit an internal logic/memory bug | Please [report it upstream](https://github.com/nicbarker/clay) with a stack trace |
!!! tip "Debugging duplicate IDs and missing parents"
Both `DUPLICATE_ID` and `FLOATING_CONTAINER_PARENT_NOT_FOUND` are best diagnosed by setting a debugger breakpoint inside your error handler function — the call stack at that point leads directly back to the offending `CLAY()` declaration.

193
docs/reference/functions.md Normal file
View file

@ -0,0 +1,193 @@
---
icon: lucide/function-square
---
# Public functions
## Lifecycle
**At startup, run once:**
`Clay_MinMemorySize``Clay_CreateArenaWithCapacityAndMemory``Clay_Initialize``Clay_SetMeasureTextFunction`
**Every frame:**
`Clay_SetLayoutDimensions``Clay_SetPointerState``Clay_UpdateScrollContainers``Clay_BeginLayout``CLAY() etc...``Clay_EndLayout`
---
### Clay_MinMemorySize
`uint32_t Clay_MinMemorySize()`
Returns the minimum bytes Clay needs to accommodate the current [`CLAY_MAX_ELEMENT_COUNT`](../guide/advanced.md#preprocessor-directives).
---
### Clay_CreateArenaWithCapacityAndMemory
`Clay_Arena Clay_CreateArenaWithCapacityAndMemory(size_t capacity, void *memory)`
Creates a `Clay_Arena` with the given capacity and base memory pointer, for use with [`Clay_Initialize`](#clay_initialize).
---
### Clay_SetMeasureTextFunction
`void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void* userData), void* userData)`
Sets the function Clay uses to measure `width, height` of a string during layout, for sizing and wrapping `CLAY_TEXT` elements.
**The provided string is not guaranteed to be null terminated** — Clay uses slices to avoid cloning strings during wrapping. If your renderer needs null-terminated C strings (e.g. Raylib), clone before rendering.
**This function is on the hot path.** For text-heavy UIs it is called many times per layout; despite Clay's internal caching, a slow measure function easily dominates total layout cost.
---
### Clay_ResetMeasureTextCache
`void Clay_ResetMeasureTextCache(void)`
Clay caches measurements from the measure-text function. Call this to force recalculation when an external factor the cache doesn't know about changes (e.g. a DPI change).
---
### Clay_SetMaxElementCount
`void Clay_SetMaxElementCount(int32_t maxElementCount)`
Sets the max element count used by subsequent [`Clay_Initialize`](#clay_initialize) / [`Clay_MinMemorySize`](#clay_minmemorysize) calls. **Requires reinitializing Clay** with a recalculated memory size afterward.
---
### Clay_SetMaxMeasureTextCacheWordCount
`void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount)`
Sets how many separate words the text measurement cache can hold, used by subsequent [`Clay_Initialize`](#clay_initialize) / [`Clay_MinMemorySize`](#clay_minmemorysize) calls. **Requires reinitializing Clay** afterward.
---
### Clay_Initialize
`Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler)`
Initializes internal memory mapping, sets layout dimensions, and binds the error handler. Returns a `Clay_Context*` usable with [`Clay_SetCurrentContext`](#clay_setcurrentcontext) for [multiple Clay instances](../guide/advanced.md#running-more-than-one-clay-instance), and sets it as the current context.
---
### Clay_SetCurrentContext
`void Clay_SetCurrentContext(Clay_Context* context)`
Sets the context subsequent Clay commands operate on. See [multiple instances](../guide/advanced.md#running-more-than-one-clay-instance).
---
### Clay_GetCurrentContext
`Clay_Context* Clay_GetCurrentContext()`
Returns the currently active context, or null if none has been set.
---
### Clay_SetLayoutDimensions
`void Clay_SetLayoutDimensions(Clay_Dimensions dimensions)`
Sets internal layout dimensions. Cheap enough to call every frame to respond to window resizing.
---
### Clay_SetPointerState
`void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown)`
Sets internal pointer position/state and recalculates overlap info, used by [`Clay_PointerOver`](#clay_pointerover) and [`Clay_UpdateScrollContainers`](#clay_updatescrollcontainers). `isPointerDown` should reflect the current frame's state — `true` for the entire duration the button is held. Clay handles detecting click/touch start & end internally.
---
### Clay_UpdateScrollContainers
`void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime)`
Handles scrolling from `scrollDelta` (mouse wheel / trackpad this frame) and touch/drag scrolling. Drag scrolling only occurs if `enableDragScrolling` is `true` **and** [`Clay_SetPointerState`](#clay_setpointerstate) was called this frame — pass `false` to disable touch scrolling entirely. `deltaTime` is seconds since the last frame, used to normalize scrolling across refresh rates.
---
### Clay_GetScrollOffset
`Clay_Vector2 Clay_GetScrollOffset()`
Returns the internally tracked scroll offset for the currently open element. Intended for `.clip.childOffset` — see [Scrolling elements](../guide/scrolling.md).
---
### Clay_BeginLayout
`void Clay_BeginLayout()`
Prepares Clay to calculate a new layout. Call once per frame before any [element macros](macros.md).
---
### Clay_EndLayout
`Clay_RenderCommandArray Clay_EndLayout()`
Ends element declaration and calculates the layout. Returns a [`Clay_RenderCommandArray`](structures.md#clay_rendercommandarray) with the results.
---
### Clay_Hovered
`bool Clay_Hovered()`
Called **during** layout declaration; returns `true` if the last-set pointer position is inside the currently open element's **previous frame** bounding box.
---
### Clay_OnHover
`void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData), void *userData)`
Attaches a function pointer to the currently open element, called once per layout while the pointer overlaps it. See [Mouse and pointer interactions](../guide/interactions.md).
---
### Clay_PointerOver
`bool Clay_PointerOver(Clay_ElementId id)`
Returns `true` if the last-set pointer position is inside the bounding box of the element matching `id`, using **last frame's** position. For frame-accurate results, run layout twice in the same frame.
---
### Clay_GetOpenElementId
`Clay_ElementId Clay_GetOpenElementId()`
Returns the [`Clay_ElementId`](structures.md#clay_elementid) of the currently open element — useful for elements opened with [`CLAY_AUTO_ID`](macros.md#clay_auto_id).
---
### Clay_GetScrollContainerData
`Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id)`
Returns [`Clay_ScrollContainerData`](structures.md#clay_scrollcontainerdata) for the scroll container matching `id`, for imperative scroll position manipulation (scroll bars, "jump to" buttons, etc).
---
### Clay_GetElementData
`Clay_ElementData Clay_GetElementData(Clay_ElementId id)`
Returns [`Clay_ElementData`](structures.md#clay_elementdata) for the element matching `id`, including its final calculated bounding box.
---
### Clay_GetElementId
`Clay_ElementId Clay_GetElementId(Clay_String idString)`
Returns a [`Clay_ElementId`](structures.md#clay_elementid) for the given string, for querying hover state, scroll data, etc. outside of layout declaration.

148
docs/reference/macros.md Normal file
View file

@ -0,0 +1,148 @@
---
icon: lucide/braces
---
# Element macros
### CLAY()
**Usage:** `CLAY(...configuration) { ...children }`
**Lifecycle:** `Clay_BeginLayout()``CLAY()``Clay_EndLayout()`
Opens a generic, configurable container that supports nested children. Requires a parameter — use `CLAY(0)` for an unconfigured element.
```c
CLAY(CLAY_ID("Outer"), { .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
CLAY(CLAY_ID("SideBar"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
// Children laid out top to bottom with a 16 px gap between them
}
CLAY(CLAY_ID("ScrollContainer"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10),
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
}) {
// child elements
}
}
```
---
### CLAY_AUTO_ID()
A version of `CLAY()` that generates an ID automatically instead of requiring one as the first argument.
The generated ID is based on the element's position in the hierarchy (same mechanism as [`CLAY_ID_LOCAL()`](#clay_id_local)) and may change between layout calls if elements are added or removed before it. For [transitions](../guide/transitions.md) and retained-mode backends to work correctly, prefer explicit IDs.
```c
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
// child elements
}
```
---
### CLAY_TEXT()
**Usage:** `CLAY_TEXT(Clay_String textContents, Clay_TextElementConfig *textConfig);`
A measured, auto-wrapped text element. `.fontId` is a `uint32_t` mapped to a font asset in user code and passed through to render commands. See [Layout and styling — Text](../guide/styling-layout.md#text) for the full field list.
```c
const uint32_t FONT_ID_LATO = 3;
CLAY_TEXT(CLAY_STRING("John Smith"), { .fontId = FONT_ID_LATO, .fontSize = 24, .textColor = {255, 0, 0, 255} });
```
Subject to [visibility culling](../guide/advanced.md#visibility-culling); may emit multiple `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_TEXT`, one per wrapped line. `Clay_RenderCommand.textContent` is a `Clay_String` **slice** of the original string — wrapping never reallocates.
---
### CLAY_ID()
`Clay_ElementId CLAY_ID(STRING_LITERAL idString)`
Generates and attaches a [`Clay_ElementId`](structures.md#clay_elementid) during declaration. Only works with string literals — for heap-allocated `char *`, use [`CLAY_SID`](#clay_sid). To regenerate the same ID outside layout declaration (e.g. for [`Clay_PointerOver`](functions.md#clay_pointerover)), use [`Clay_GetElementId`](functions.md#clay_getelementid).
```c
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(16) } }) { /* ... */ }
// Later, outside layout code
bool buttonIsHovered = Clay_PointerOver(Clay_GetElementId("Button"));
```
---
### CLAY_SID()
`Clay_ElementId CLAY_SID(Clay_String idString)`
Version of [`CLAY_ID`](#clay_id) for heap-allocated `char *` data. The underlying data is not copied and must live until at least the next frame.
---
### CLAY_IDI()
`Clay_ElementId CLAY_IDI(STRING_LITERAL idString, int32_t index)`
An offset version of [`CLAY_ID`](#clay_id) — "**ID** + **I**ndex". Combines a label with an index to generate IDs for sequential elements (e.g. inside a `for` loop) without constructing dynamic strings at runtime. String-literal only; use [`CLAY_SIDI`](#clay_sidi) for heap-allocated strings.
```c
for (int index = 0; index < items.length; index++) {
CLAY(CLAY_IDI("Item", index), { /* ... */ }) {}
}
```
---
### CLAY_SIDI()
`Clay_ElementId CLAY_SIDI(Clay_String idString, int32_t index)`
Version of [`CLAY_IDI`](#clay_idi) for heap-allocated `char *` data.
---
### CLAY_ID_LOCAL()
**Usage:** `Clay_ElementId CLAY_ID_LOCAL(STRING_LITERAL idString)`
Unlike [`CLAY_ID`](#clay_id), which must be globally unique, a local ID only needs to be unique among its siblings — it's based on the ID of its parent. Ideal for reusable components and loops.
```c
void RenderHeaderButton(ButtonData button) {
CLAY({
.id = CLAY_ID_LOCAL("HeaderButton"),
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = CLAY_PADDING_ALL(16) }
}) { /* ... */ }
}
for (int i = 0; i < headerButtons.length; i++) {
RenderHeaderButton(headerButtons.items[i]);
}
```
---
### CLAY_SID_LOCAL()
`Clay_ElementId CLAY_SID_LOCAL(Clay_String idString)`
Version of [`CLAY_ID_LOCAL`](#clay_id_local) for heap-allocated `char *` data.
---
### CLAY_IDI_LOCAL()
`Clay_ElementId CLAY_IDI_LOCAL(STRING_LITERAL idString, int32_t index)`
Offset version of [`CLAY_ID_LOCAL`](#clay_id_local), for sequential elements inside loops without dynamic string construction.
---
### CLAY_SIDI_LOCAL()
`Clay_ElementId CLAY_SIDI_LOCAL(Clay_String idString, int32_t index)`
Version of [`CLAY_IDI_LOCAL`](#clay_idi_local) for heap-allocated `char *` data.

View file

@ -0,0 +1,354 @@
---
icon: lucide/box
---
# Data structures
### Clay_TextElementConfig
The configuration struct for [`CLAY_TEXT()`](macros.md#clay_text).
```c
Clay_TextElementConfig {
Clay_Color textColor;
uint16_t fontId;
uint16_t fontSize;
uint16_t letterSpacing;
uint16_t lineHeight;
Clay_TextElementConfigWrapMode wrapMode;
Clay_TextAlignment textAlignment;
void *userData;
};
```
See [Layout and styling — Text](../guide/styling-layout.md#text) for the full field-by-field breakdown.
---
### Clay_ElementDeclaration
The only argument to `CLAY()`.
```c
typedef struct {
Clay_LayoutConfig layout;
Clay_Color backgroundColor;
Clay_Color overlayColor;
Clay_CornerRadius cornerRadius;
Clay_AspectRatioElementConfig aspectRatio;
Clay_ImageElementConfig image;
Clay_FloatingElementConfig floating;
Clay_CustomElementConfig custom;
Clay_ClipElementConfig clip;
Clay_BorderElementConfig border;
Clay_TransitionElementConfig transition;
void *userData;
} Clay_ElementDeclaration;
```
`.overlayColor` blends a color over the element and its children — see [Layout and styling — Background color, corner radius, and overlays](../guide/styling-layout.md#background-color-corner-radius-and-overlays). `.transition` configures [element transitions](../guide/transitions.md). `.userData` is passed through transparently to the corresponding `Clay_RenderCommand`s. See [Building UI hierarchies](../guide/building-ui.md) and [Layout and styling](../guide/styling-layout.md) for field-by-field usage.
---
### Clay_LayoutConfig
Layout-affecting options — settings that change the position/size of an element, its parents, siblings, and children.
```c
Clay_LayoutConfig {
Clay_LayoutDirection layoutDirection = CLAY_LEFT_TO_RIGHT (default) | CLAY_TOP_TO_BOTTOM;
Clay_Padding padding { u16 left; u16 right; u16 top; u16 bottom; };
uint16_t childGap;
Clay_ChildAlignment childAlignment {
.x = CLAY_ALIGN_X_LEFT (default) | CLAY_ALIGN_X_CENTER | CLAY_ALIGN_X_RIGHT;
.y = CLAY_ALIGN_Y_TOP (default) | CLAY_ALIGN_Y_CENTER | CLAY_ALIGN_Y_BOTTOM;
};
Clay_Sizing sizing {
.width = CLAY_SIZING_FIT(min, max) (default) | CLAY_SIZING_GROW(min, max) | CLAY_SIZING_FIXED(w) | CLAY_SIZING_PERCENT(p)
.height = CLAY_SIZING_FIT(min, max) (default) | CLAY_SIZING_GROW(min, max) | CLAY_SIZING_FIXED(h) | CLAY_SIZING_PERCENT(p)
};
};
```
See [Layout and styling](../guide/styling-layout.md) for details on every field.
---
### Clay_ImageElementConfig
```c
Clay_ImageElementConfig {
void * imageData;
};
```
`.imageData` is an opaque pointer passed through to the renderer. Combine with `.aspectRatio` to preserve an image's original proportions when scaling. See [Layout and styling — Images](../guide/styling-layout.md#images-and-aspect-ratio).
---
### Clay_AspectRatioElementConfig
```c
Clay_AspectRatioElementConfig {
float aspectRatio;
};
```
Enforces a fixed width/height ratio — mostly for images, but usable on any element. `.aspectRatio = 16.0 / 9.0` and `.aspectRatio = { .aspectRatio = 16.0 / 9.0 }` are equivalent (C passes the value to the first nested field).
---
### Clay_ClipElementConfig
```c
Clay_ClipElementConfig {
bool horizontal;
bool vertical;
Clay_Vector2 childOffset;
};
```
Configures scrolling/clipping. `Clay_SetPointerState()` and `Clay_UpdateScrollContainers()` must be called **before** `BeginLayout` for scroll input to register. See [Scrolling elements](../guide/scrolling.md).
---
### Clay_BorderElementConfig
```c
typedef struct Clay_BorderElementConfig {
Clay_Color color;
Clay_BorderWidth width {
uint16_t left; uint16_t right; uint16_t top; uint16_t bottom; uint16_t betweenChildren;
};
} Clay_BorderElementConfig;
```
Border width insets and overlaps content — it does not affect layout sizing. See [Layout and styling — Borders](../guide/styling-layout.md#borders).
---
### Clay_FloatingElementConfig
```c
Clay_FloatingElementConfig {
Clay_Vector2 offset;
Clay_Dimensions expand;
uint32_t parentId;
int16_t zIndex;
Clay_FloatingAttachPoints attachPoints { .element = ...; .parent = ...; };
Clay_PointerCaptureMode pointerCaptureMode {
CLAY_POINTER_CAPTURE_MODE_CAPTURE (default), CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH
};
Clay_FloatingAttachToElement attachTo {
CLAY_ATTACH_TO_NONE (default), CLAY_ATTACH_TO_PARENT, CLAY_ATTACH_TO_ELEMENT_WITH_ID, CLAY_ATTACH_TO_ROOT
};
Clay_FloatingClipToElement clipTo {
CLAY_CLIP_TO_NONE (default), CLAY_CLIP_TO_ATTACHED_PARENT
};
};
```
Attach point values: `CLAY_ATTACH_POINT_{LEFT,CENTER,RIGHT}_{TOP,CENTER,BOTTOM}`. See [Floating elements](../guide/floating-elements.md).
---
### Clay_CustomElementConfig
```c
typedef struct { void * customData; } Clay_CustomElementConfig;
```
See [Custom elements](../guide/custom-elements.md).
---
### Clay_TransitionElementConfig
```c
typedef struct Clay_TransitionElementConfig {
bool (*handler)(Clay_TransitionCallbackArguments arguments);
float duration;
Clay_TransitionProperty properties; // bitfield, see below
Clay_TransitionInteractionHandlingType interactionHandling;
struct {
Clay_TransitionData (*setInitialState)(Clay_TransitionData targetState, Clay_TransitionProperty properties);
Clay_TransitionEnterTriggerType trigger;
} enter;
struct {
Clay_TransitionData (*setFinalState)(Clay_TransitionData initialState, Clay_TransitionProperty properties);
Clay_TransitionExitTriggerType trigger;
Clay_ExitTransitionSiblingOrdering siblingOrdering;
} exit;
} Clay_TransitionElementConfig;
```
See [Transitions](../guide/transitions.md) for the full field breakdown and the `Clay_TransitionProperty` flag values.
---
### Clay_Color
```c
typedef struct { float r, g, b, a; } Clay_Color;
```
RGBA color. By convention channels are `0``255`, but interpretation is left to the renderer.
---
### Clay_String
```c
typedef struct {
bool isStaticallyAllocated;
int32_t length;
const char *chars;
} Clay_String;
```
Clay's internal string representation.
- **`.isStaticallyAllocated`** — whether the string lives for the entire program lifetime.
- **`.length`** — character count, **excluding** any null terminator.
- **`.chars`** — pointer to string data; **not guaranteed to be null terminated.**
---
### Clay_ElementId
```c
typedef struct {
uint32_t id;
uint32_t offset;
uint32_t baseId;
Clay_String stringId;
} Clay_ElementId;
```
Returned by [`CLAY_ID`](macros.md#clay_id) / [`CLAY_IDI`](macros.md#clay_idi).
- **`.id`** — unique hash derived from the input string.
- **`.offset`** — the index passed to `CLAY_IDI`; always `0` for `CLAY_ID`.
- **`.baseId`** — hash of the base string before hashing with `.offset`; equal to `.id` for `CLAY_ID`.
- **`.stringId`** — the original source string.
---
### Clay_RenderCommandArray
```c
typedef struct {
int32_t capacity;
int32_t length;
Clay_RenderCommand *internalArray;
} Clay_RenderCommandArray;
```
Returned by [`Clay_EndLayout`](functions.md#clay_endlayout). Valid entries run from `.internalArray[0]` to `.internalArray[.length - 1]`.
---
### Clay_RenderCommand
```c
typedef struct {
Clay_BoundingBox boundingBox;
Clay_RenderData renderData;
void* userData;
uint32_t id;
int16_t zIndex;
Clay_RenderCommandType commandType;
} Clay_RenderCommand;
```
**`.commandType`** values:
| Value | Meaning |
|---|---|
| `CLAY_RENDER_COMMAND_TYPE_NONE` | Ignored; never emitted under normal conditions |
| `CLAY_RENDER_COMMAND_TYPE_RECTANGLE` | Draw a rectangle — `.renderData.rectangle` |
| `CLAY_RENDER_COMMAND_TYPE_BORDER` | Draw a border — `.renderData.border` |
| `CLAY_RENDER_COMMAND_TYPE_TEXT` | Draw text — `.renderData.text` |
| `CLAY_RENDER_COMMAND_TYPE_IMAGE` | Draw an image — `.renderData.image` |
| `CLAY_RENDER_COMMAND_TYPE_SCISSOR_START` / `_END` | Begin/end pixel culling outside `.boundingBox` ([glScissor](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glScissor.xhtml)-style); not subject to visibility culling |
| `CLAY_RENDER_COMMAND_TYPE_OVERLAY_COLOR_START` / `_END` | Begin/end an overlay color blend, similar to `mix(source, target, alpha)`; nested overlays may need a renderer-side stack |
| `CLAY_RENDER_COMMAND_TYPE_CUSTOM` | User-controlled — `.renderData.custom` |
`.boundingBox` is a `{x, y, width, height}` rect with `.x/.y` at the top-left. `.id` is the originating element's ID. `.zIndex` derives from the enclosing floating configuration, if any — higher renders on top.
**`.renderData`** is a union selected by `.commandType`:
```c
typedef union {
Clay_RectangleRenderData rectangle;
Clay_TextRenderData text;
Clay_ImageRenderData image;
Clay_CustomRenderData custom;
Clay_BorderRenderData border;
} Clay_RenderData;
```
```c
typedef struct { Clay_StringSlice stringContents; Clay_Color textColor; uint16_t fontId; uint16_t fontSize; uint16_t letterSpacing; uint16_t lineHeight; } Clay_TextRenderData;
typedef struct { Clay_Color backgroundColor; Clay_CornerRadius cornerRadius; } Clay_RectangleRenderData;
typedef struct { Clay_Color backgroundColor; Clay_CornerRadius cornerRadius; void* imageData; } Clay_ImageRenderData;
typedef struct { Clay_Color backgroundColor; Clay_CornerRadius cornerRadius; void* customData; } Clay_CustomRenderData;
typedef struct { Clay_Color color; Clay_CornerRadius cornerRadius; Clay_BorderWidth width; } Clay_BorderRenderData;
```
---
### Clay_ScrollContainerData
```c
typedef struct {
Clay_Vector2 *scrollPosition; // live pointer — mutating it changes final layout
Clay_Dimensions scrollContainerDimensions;
Clay_Dimensions contentDimensions;
Clay_ClipElementConfig config;
bool found;
} Clay_ScrollContainerData;
```
Returned by [`Clay_GetScrollContainerData`](functions.md#clay_getscrollcontainerdata). `.scrollPosition` is a pointer into Clay's real internal state — intended for scroll bars or auto-scroll logic. Scrolling is only possible when `.contentDimensions` exceeds `.scrollContainerDimensions` in at least one axis.
---
### Clay_ElementData
```c
typedef struct {
Clay_BoundingBox boundingBox; // relative to the root of the layout
bool found;
} Clay_ElementData;
```
Returned by [`Clay_GetElementData`](functions.md#clay_getelementdata). If `.found` is `false`, `.boundingBox` is zeroed.
---
### Clay_PointerData
```c
typedef struct {
Clay_Vector2 position;
Clay_PointerDataInteractionState state;
} Clay_PointerData;
```
`.state` values — see [Mouse and pointer interactions](../guide/interactions.md#pointer-interaction-states):
```c
typedef enum {
CLAY_POINTER_DATA_PRESSED_THIS_FRAME,
CLAY_POINTER_DATA_PRESSED,
CLAY_POINTER_DATA_RELEASED_THIS_FRAME,
CLAY_POINTER_DATA_RELEASED,
} Clay_PointerDataInteractionState;
```
---
### Clay_ErrorHandler / Clay_ErrorData
See [Error handling](errors.md).

12
pyproject.toml Normal file
View file

@ -0,0 +1,12 @@
[project]
name = "clay"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []
[dependency-groups]
dev = [
"zensical>=0.0.51",
]

245
uv.lock generated Normal file
View file

@ -0,0 +1,245 @@
version = 1
revision = 3
requires-python = ">=3.13"
[[package]]
name = "clay"
version = "0.1.0"
source = { virtual = "." }
[package.dev-dependencies]
dev = [
{ name = "zensical" },
]
[package.metadata]
[package.metadata.requires-dev]
dev = [{ name = "zensical", specifier = ">=0.0.51" }]
[[package]]
name = "click"
version = "8.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "deepmerge"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2a/78/6e9e20106224083cfb817d2d3c26e80e72258d617b616721a169b87081e0/deepmerge-2.1.0.tar.gz", hash = "sha256:07ca7a7b8935df596c512fa8161877c0487ac61f691c07766e7d71d2b23bdd2f", size = 21449, upload-time = "2026-06-22T05:46:07.669Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/51/25/2a75b47cb057b1e164c604fb81ab690a6cdb5e2260ce651194eae90f64a3/deepmerge-2.1.0-py3-none-any.whl", hash = "sha256:8f148339a91d680a75ecb74ade235d9e759a93df373a0b04e9d31c8666cfeb75", size = 14345, upload-time = "2026-06-22T05:46:06.742Z" },
]
[[package]]
name = "jinja2"
version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "markdown"
version = "3.10.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" },
]
[[package]]
name = "markupsafe"
version = "3.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
[[package]]
name = "pygments"
version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
[[package]]
name = "pymdown-extensions"
version = "11.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/21/a9/5f0c535ba3b08fe09270c16808e053a968868242ecbd5676d4e3a488bf28/pymdown_extensions-11.0.1.tar.gz", hash = "sha256:dd2905ae6fc5b75582fafb139a1266ffc754705efa902aa50067fa7ff4f94ec0", size = 857113, upload-time = "2026-07-02T17:59:22.955Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d6/54/da572c98c0b77626a91b5d3b89f0231d8bff5125c225420908632f8b342d/pymdown_extensions-11.0.1-py3-none-any.whl", hash = "sha256:db3943a62bab7e03af1364f0c4083e64b91fb097675a4b6cceccfbe9a77e5eb2", size = 269455, upload-time = "2026-07-02T17:59:21.271Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]
[[package]]
name = "tomli"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
{ url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
{ url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
{ url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
{ url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
{ url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
{ url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
{ url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
{ url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
{ url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
{ url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
{ url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
{ url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
{ url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
{ url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
{ url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
{ url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
{ url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
{ url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
{ url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
{ url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
{ url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
{ url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
{ url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
{ url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
{ url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
{ url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
]
[[package]]
name = "zensical"
version = "0.0.51"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "deepmerge" },
{ name = "jinja2" },
{ name = "markdown" },
{ name = "pygments" },
{ name = "pymdown-extensions" },
{ name = "pyyaml" },
{ name = "tomli" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b8/f7/d07ffb268ca86afb26b7f32dbabe25dec03d3aa63ba4d876720c84681d33/zensical-0.0.51.tar.gz", hash = "sha256:de25de067bedfa18f916d7f366fd64a7fbf09bfcc615b44d1ddbe3b5fe02ab49", size = 3979640, upload-time = "2026-07-17T18:08:03.445Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/48/21/02db3e1fb3904016bfac310037c95b9f1eaaf0ffe7b4a84f14263a7d95df/zensical-0.0.51-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:134d776afa526098e05e34713e2f577c075e57a232e01b97842bb0206716afce", size = 12791154, upload-time = "2026-07-17T18:07:20.748Z" },
{ url = "https://files.pythonhosted.org/packages/a2/35/b0d96f58253514cb3d08f5779020ab01ee5472334fb984b92e3fc9e9c9ac/zensical-0.0.51-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e97ab39668ae3b452c550634e921a0336443743aae5e1fe031c7bb57d049e535", size = 12692190, upload-time = "2026-07-17T18:07:24.553Z" },
{ url = "https://files.pythonhosted.org/packages/2e/90/7a60e126a10c37c6b789938ff17e73fe76bba707fa029cb40ac659aeaa82/zensical-0.0.51-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c9579809f88608e7aa2cff516fff9d267d74a843cf6088a5f4227de2f092bb5", size = 13139337, upload-time = "2026-07-17T18:07:27.885Z" },
{ url = "https://files.pythonhosted.org/packages/ae/c3/9101c97b90d4713ef2816db03366a45ae4762efebffd296737a2dd2df325/zensical-0.0.51-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296dc7a14aa28b81a58eb57df2d5c9c9a4b0de7e90c11d99c943354287952925", size = 13069851, upload-time = "2026-07-17T18:07:31.814Z" },
{ url = "https://files.pythonhosted.org/packages/d2/79/0474df9e15a2c18f6281a786e10177c1b6e16feac1c568e7f36ad39b339c/zensical-0.0.51-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f779d2d87b4bf228cf2e279bc0ae6bcf3b36a9335ff283a317d01f7c15ae46b2", size = 13451083, upload-time = "2026-07-17T18:07:35.543Z" },
{ url = "https://files.pythonhosted.org/packages/fe/6f/91bbf78f704d5fd4c0c9be27d6bce3b6e4c2c339e4dcd6e7cf19ecda643c/zensical-0.0.51-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f813a1514a90890ca86248a8d54b81b2164bcbff11a6bcf11b01e1c01a1454", size = 13110446, upload-time = "2026-07-17T18:07:38.783Z" },
{ url = "https://files.pythonhosted.org/packages/d9/89/aa9a95f81771614c37bdc52b8ab21fcdef4c8de7c9cedf34e9bf62674281/zensical-0.0.51-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:186ef37e0eee0e969e2cfae47b1b97775e3164e2cba95c71faa4dd6ef47ed009", size = 13315871, upload-time = "2026-07-17T18:07:42.43Z" },
{ url = "https://files.pythonhosted.org/packages/08/11/1bf6e9ded29d376f8c12644cc4de04676b010fee8caa17f682606b1f16d5/zensical-0.0.51-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5d91ce246ed930224603083cef02ae8947132fc7c52901d72015ea03526fa58", size = 13344382, upload-time = "2026-07-17T18:07:46.066Z" },
{ url = "https://files.pythonhosted.org/packages/d6/ec/663f16ff82d08b212e7c3236a88bd332f73331f94ddac1c91aaf882bbd1e/zensical-0.0.51-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:b1108eae82c6e8ffc33026f60b485c1512647a5333be4f547166b7c8877b98af", size = 13499628, upload-time = "2026-07-17T18:07:49.196Z" },
{ url = "https://files.pythonhosted.org/packages/60/b4/7f1b6c3cf06d9f6ff5216523168a5d6ccc693444d5ceeb911eca97b30d98/zensical-0.0.51-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6fa0ecaf14f56841bfc595fa141396350c72aafbec73a016ebe3c824ed21ac72", size = 13451420, upload-time = "2026-07-17T18:07:52.563Z" },
{ url = "https://files.pythonhosted.org/packages/0a/44/be4bc09ec8f69e7be1b07b875887961c4e0e478b03a10d2cc624ef28fbe6/zensical-0.0.51-cp310-abi3-win32.whl", hash = "sha256:fb7ff4946b72168759c6af0a29cf5de4c38aebe633a83292e8cd4145b5213cc2", size = 12375639, upload-time = "2026-07-17T18:07:56.081Z" },
{ url = "https://files.pythonhosted.org/packages/1f/85/aa827c244ed4f404e99a91c3ecf5e5adb62eca806a9e9c8e3333bbad8660/zensical-0.0.51-cp310-abi3-win_amd64.whl", hash = "sha256:12529d3d3991b63820952111dc1d1edc29b2c9b3a3abb16c243bcb649631ebf2", size = 12628965, upload-time = "2026-07-17T18:07:59.741Z" },
]

384
zensical.toml Normal file
View file

@ -0,0 +1,384 @@
# ============================================================================
#
# The configuration produced by default is meant to highlight the features
# that Zensical provides and to serve as a starting point for your own
# projects.
#
# ============================================================================
[project]
repo_url = "https://github.com/nicbarker/clay"
repo_name = "nicbarker/clay"
# The site_name is shown in the page header and the browser window title
#
# Read more: https://zensical.org/docs/setup/basics/#site_name
site_name = "Clay"
# The site_description is included in the HTML head and should contain a
# meaningful description of the site content for use by search engines.
#
# Read more: https://zensical.org/docs/setup/basics/#site_description
site_description = "Clay is a high performance, renderer agnostic 2D UI layout library with a React-like declarative C API, zero dependencies, and Wasm support."
# The site_author attribute. This is used in the HTML head element.
#
# Read more: https://zensical.org/docs/setup/basics/#site_author
site_author = "Nic Barker"
# The site_url is the canonical URL for your site. When building online
# documentation you should set this.
# Read more: https://zensical.org/docs/setup/basics/#site_url
#site_url = "https://www.example.com/"
# The copyright notice appears in the page footer and can contain an HTML
# fragment.
#
# Read more: https://zensical.org/docs/setup/basics/#copyright
copyright = """
Copyright &copy; <span id="copyright-year">2026</span> Nic Barker
"""
# Zensical supports both implicit navigation and explicitly defined navigation.
# If you decide not to define a navigation here then Zensical will simply
# derive the navigation structure from the directory structure of your
# "docs_dir". The definition below demonstrates how a navigation structure
# can be defined using TOML syntax.
#
# Read more: https://zensical.org/docs/setup/navigation/
nav = [
{ "Home" = "index.md" },
{ "Getting started" = "getting-started.md" },
{ "Guide" = [
{ "Building UI hierarchies" = "guide/building-ui.md" },
{ "Layout and styling" = "guide/styling-layout.md" },
{ "Mouse and pointer interactions" = "guide/interactions.md" },
{ "Scrolling elements" = "guide/scrolling.md" },
{ "Floating elements" = "guide/floating-elements.md" },
{ "Custom elements" = "guide/custom-elements.md" },
{ "Transitions" = "guide/transitions.md" },
{ "Advanced topics" = "guide/advanced.md" },
] },
{ "API Reference" = [
{ "Public functions" = "reference/functions.md" },
{ "Element macros" = "reference/macros.md" },
{ "Data structures" = "reference/structures.md" },
{ "Error handling" = "reference/errors.md" },
] },
{ "Ecosystem" = "ecosystem.md" },
]
# With the "extra_css" option you can add your own CSS styling to customize
# your Zensical project according to your needs. You can add any number of
# CSS files.
#
# The path provided should be relative to the "docs_dir".
#
# Read more: https://zensical.org/docs/customization/#additional-css
#
#extra_css = ["stylesheets/extra.css"]
# With the `extra_javascript` option you can add your own JavaScript to your
# project to customize the behavior according to your needs.
#
# The path provided should be relative to the "docs_dir".
#
# Read more: https://zensical.org/docs/customization/#additional-javascript
extra_javascript = ["javascripts/copyright-year.js"]
# ----------------------------------------------------------------------------
# Section for configuring theme options
# ----------------------------------------------------------------------------
[project.theme]
# change this to "classic" to use the traditional Material for MkDocs look.
#variant = "classic"
# Zensical allows you to override specific blocks, partials, or whole
# templates as well as to define your own templates. To do this, uncomment
# the custom_dir setting below and set it to a directory in which you
# keep your template overrides.
#
# Read more:
# - https://zensical.org/docs/customization/#extending-the-theme
#
#custom_dir = "overrides"
# With the "favicon" option you can set your own image to use as the icon
# browsers will use in the browser title bar or tab bar. The path provided
# must be relative to the "docs_dir".
#
# Read more:
# - https://zensical.org/docs/setup/logo-and-icons/#favicon
# - https://developer.mozilla.org/en-US/docs/Glossary/Favicon
#
#favicon = "images/favicon.png"
# Zensical supports more than 60 different languages. This means that the
# labels and tooltips that Zensical's templates produce are translated.
# The "language" option allows you to set the language used. This language
# is also indicated in the HTML head element to help with accessibility
# and guide search engines and translation tools.
#
# The default language is "en" (English). It is possible to create
# sites with multiple languages and configure a language selector. See
# the documentation for details.
#
# Read more:
# - https://zensical.org/docs/setup/language/
#
language = "en"
# Zensical provides a number of feature toggles that change the behavior
# of the documentation site.
features = [
# Zensical includes an announcement bar. This feature allows users to
# dismiss it when they have read the announcement.
# https://zensical.org/docs/setup/header/#announcement-bar
"announce.dismiss",
# If you have a repository configured and turn on this feature, Zensical
# will generate an edit button for the page. This works for common
# repository hosting services.
# https://zensical.org/docs/setup/repository/#content-actions
#"content.action.edit",
# If you have a repository configured and turn on this feature, Zensical
# will generate a button that allows the user to view the Markdown
# code for the current page.
# https://zensical.org/docs/setup/repository/#content-actions
#"content.action.view",
# Code annotations allow you to add an icon with a tooltip to your
# code blocks to provide explanations at crucial points.
# https://zensical.org/docs/authoring/code-blocks/#code-annotations
"content.code.annotate",
# This feature turns on a button in code blocks that allow users to
# copy the content to their clipboard without first selecting it.
# https://zensical.org/docs/authoring/code-blocks/#code-copy-button
"content.code.copy",
# Code blocks can include a button to allow for the selection of line
# ranges by the user.
# https://zensical.org/docs/authoring/code-blocks/#code-selection-button
"content.code.select",
# Zensical can render footnotes as inline tooltips, so the user can read
# the footnote without leaving the context of the document.
# https://zensical.org/docs/authoring/footnotes/#footnote-tooltips
"content.footnote.tooltips",
# If you have many content tabs that have the same titles (e.g., "Python",
# "JavaScript", "Cobol"), this feature causes all of them to switch to
# at the same time when the user chooses their language in one.
# https://zensical.org/docs/authoring/content-tabs/#linked-content-tabs
"content.tabs.link",
# With this feature enabled users can add tooltips to links that will be
# displayed when the mouse pointer hovers the link.
# https://zensical.org/docs/authoring/tooltips/#improved-tooltips
"content.tooltips",
# With this feature enabled, Zensical will automatically hide parts
# of the header when the user scrolls past a certain point.
# https://zensical.org/docs/setup/header/#automatic-hiding
# "header.autohide",
# Turn on this feature to expand all collapsible sections in the
# navigation sidebar by default.
# https://zensical.org/docs/setup/navigation/#navigation-expansion
# "navigation.expand",
# This feature turns on navigation elements in the footer that allow the
# user to navigate to a next or previous page.
# https://zensical.org/docs/setup/footer/#navigation
"navigation.footer",
# When section index pages are enabled, documents can be directly attached
# to sections, which is particularly useful for providing overview pages.
# https://zensical.org/docs/setup/navigation/#section-index-pages
"navigation.indexes",
# When instant navigation is enabled, clicks on all internal links will be
# intercepted and dispatched via XHR without fully reloading the page.
# https://zensical.org/docs/setup/navigation/#instant-navigation
"navigation.instant",
# With instant prefetching, your site will start to fetch a page once the
# user hovers over a link. This will reduce the perceived loading time
# for the user.
# https://zensical.org/docs/setup/navigation/#instant-prefetching
"navigation.instant.prefetch",
# In order to provide a better user experience on slow connections when
# using instant navigation, a progress indicator can be enabled.
# https://zensical.org/docs/setup/navigation/#progress-indicator
"navigation.instant.progress",
# When navigation paths are activated, a breadcrumb navigation is rendered
# above the title of each page
# https://zensical.org/docs/setup/navigation/#navigation-path
"navigation.path",
# When pruning is enabled, only the visible navigation items are included
# in the rendered HTML, reducing the size of the built site by 33% or more.
# https://zensical.org/docs/setup/navigation/#navigation-pruning
#"navigation.prune",
# When sections are enabled, top-level sections are rendered as groups in
# the sidebar for viewports above 1220px, but remain as-is on mobile.
# https://zensical.org/docs/setup/navigation/#navigation-sections
"navigation.sections",
# When tabs are enabled, top-level sections are rendered in a menu layer
# below the header for viewports above 1220px, but remain as-is on mobile.
# https://zensical.org/docs/setup/navigation/#navigation-tabs
#"navigation.tabs",
# When sticky tabs are enabled, navigation tabs will lock below the header
# and always remain visible when scrolling down.
# https://zensical.org/docs/setup/navigation/#sticky-navigation-tabs
#"navigation.tabs.sticky",
# A back-to-top button can be shown when the user, after scrolling down,
# starts to scroll up again.
# https://zensical.org/docs/setup/navigation/#back-to-top-button
"navigation.top",
# When anchor tracking is enabled, the URL in the address bar is
# automatically updated with the active anchor as highlighted in the table
# of contents.
# https://zensical.org/docs/setup/navigation/#anchor-tracking
"navigation.tracking",
# When search highlighting is enabled and a user clicks on a search result,
# Zensical will highlight all occurrences after following the link.
# https://zensical.org/docs/setup/search/#search-highlighting
"search.highlight",
# When anchor following for the table of contents is enabled, the sidebar
# is automatically scrolled so that the active anchor is always visible.
# https://zensical.org/docs/setup/navigation/#anchor-following
# "toc.follow",
# When navigation integration for the table of contents is enabled, it is
# always rendered as part of the navigation sidebar on the left.
# https://zensical.org/docs/setup/navigation/#navigation-integration
#"toc.integrate",
]
# ----------------------------------------------------------------------------
# You can configure your own logo to be shown in the header using the "logo"
# option in the "theme" subsection. The logo must be a relative path to a file
# in your "docs_dir", e.g., to use `docs/assets/logo.png` you would set:
# ----------------------------------------------------------------------------
#logo = "assets/logo.png"
# ----------------------------------------------------------------------------
# If you don't have a dedicated project logo, you can use a built-in icon from
# the icon sets shipped in Zensical. Please note that the setting lives in a
# different subsection, and that the above take precedence over the icon.
#
# Read more:
# - https://zensical.org/docs/setup/logo-and-icons
# - https://github.com/zensical/ui/tree/master/dist/.icons
# ----------------------------------------------------------------------------
[project.theme.icon]
logo = "material/alpha-c-box"
repo = "fontawesome/brands/github"
# ----------------------------------------------------------------------------
# In the "font" subsection you can configure the fonts used. By default, fonts
# are loaded from Google Fonts, giving you a wide range of choices from a set
# of suitably licensed fonts. There are options for a normal text font and for
# a monospaced font used in code blocks.
# ----------------------------------------------------------------------------
#[project.theme.font]
#text = "Inter"
#code = "Jetbrains Mono"
# ----------------------------------------------------------------------------
# In the "palette" subsection you can configure options for the color scheme.
# You can configure different color schemes, e.g., to turn on dark mode,
# that the user can switch between. Each color scheme can be further
# customized.
#
# Read more:
# - https://zensical.org/docs/setup/colors/
# ----------------------------------------------------------------------------
# Palette toggle for automatic mode
[[project.theme.palette]]
media = "(prefers-color-scheme)"
toggle.icon = "lucide/sun-moon"
toggle.name = "Switch to light mode"
# Palette toggle for light mode
[[project.theme.palette]]
media = "(prefers-color-scheme: light)"
scheme = "default"
toggle.icon = "lucide/sun"
toggle.name = "Switch to dark mode"
# Palette toggle for dark mode
[[project.theme.palette]]
media = "(prefers-color-scheme: dark)"
scheme = "slate"
toggle.icon = "lucide/moon"
toggle.name = "Switch to system preference"
# ----------------------------------------------------------------------------
# The "extra" section contains miscellaneous settings.
# ----------------------------------------------------------------------------
[[project.extra.social]]
icon = "fontawesome/brands/github"
link = "https://github.com/nicbarker/clay"
[[project.extra.social]]
icon = "fontawesome/brands/discord"
link = "https://discord.gg/b4FTWkxdvT"
# ----------------------------------------------------------------------------
# In this section you can configure the Markdown extensions that are used when
# rendering your documentation. We enable the most useful extensions by default,
# but you can customize this list to your needs.
#
# Read more:
# - https://zensical.org/docs/setup/extensions/
# ----------------------------------------------------------------------------
[project.markdown_extensions.abbr]
[project.markdown_extensions.admonition]
[project.markdown_extensions.attr_list]
[project.markdown_extensions.def_list]
[project.markdown_extensions.footnotes]
[project.markdown_extensions.md_in_html]
[project.markdown_extensions.toc]
permalink = true
[project.markdown_extensions.pymdownx.arithmatex]
generic = true
[project.markdown_extensions.pymdownx.betterem]
[project.markdown_extensions.pymdownx.caret]
[project.markdown_extensions.pymdownx.details]
[project.markdown_extensions.pymdownx.emoji]
emoji_generator = "zensical.extensions.emoji.to_svg"
emoji_index = "zensical.extensions.emoji.twemoji"
[project.markdown_extensions.pymdownx.highlight]
anchor_linenums = true
line_spans = "__span"
pygments_lang_class = true
[project.markdown_extensions.pymdownx.inlinehilite]
[project.markdown_extensions.pymdownx.keys]
[project.markdown_extensions.pymdownx.magiclink]
[project.markdown_extensions.pymdownx.mark]
[project.markdown_extensions.pymdownx.smartsymbols]
[project.markdown_extensions.pymdownx.superfences]
custom_fences = [
{ name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" }
]
[project.markdown_extensions.pymdownx.tabbed]
alternate_style = true
combine_header_slug = true
[project.markdown_extensions.pymdownx.tasklist]
custom_checkbox = true
[project.markdown_extensions.pymdownx.tilde]