ApricityUI
Build UIs with HTML + CSS + JS. The syntax follows Web standards as closely as possible.

Overview
ApricityUI was built around one goal: a UI framework that is low-friction, convenient, and broadly capable. That is why it takes the classic HTML + CSS + JS trio as its core.
The JS side currently depends on KubeJS (optional), which means modpack authors can draw whatever UI they like with an extremely low learning curve.
How low? You can have AI generate everything ApricityUI-related for you. Web frameworks are popular enough that AI knows them well — the result may even look better than what you would draw yourself, and you can still read it.
Mods that need custom UI can also depend on ApricityUI to build highly extensible interfaces; the JS and Java APIs are broadly equivalent.
Where does that extensibility show? Nested masks, smooth scrolling, rounded borders, frosted-glass backgrounds, custom animations, custom fonts, GIF playback — even filters, masks, and shadows nested inside one another. Features that take hundreds or thousands of lines elsewhere are a few lines here.
ApricityUI's syntax is essentially fixed and stays as close to Web standards as possible, so you can upgrade freely and use it across game versions without worrying about compatibility breaks.
From 1.2.5 onward, ApricityUI can also call the WebView built into your operating system, letting you embed a real website or web page inside the game that accepts only hardware input (video playback included).
If you are not sure what a Web framework is, at least remember its three well-known advantages:
- Rich functionality, from simple shape drawing to nested rendering effects — ApricityUI can even render onto a block in the world.
- Easy to use: most crash courses I have seen are under three hours (the shortest is ten minutes), and AI knows the stack inside out.
- Easy to debug: almost everything hot-reloads instantly, and convenient developer tools provide visual debugging. ApricityUI has both.
Note: ApricityUI does not bundle a Chrome runtime. It is built entirely from scratch in Java; as of 1.2.4 the jar is only 2.5 MB, it downloads nothing extra, and it is safe to use.
Showcase
Built-in pure-CSS themes
The Ore and AE themes are built in, and more MC-flavored (or deliberately un-MC) general-purpose themes will follow. They all share the same CSS naming scheme, so switching between them is seamless.
-
Limitless Vehicle 3D printer

-
Auto Seam Blend editor

Draw whenever you like
It integrates fully with Minecraft's rendering — draw wherever you want. KubeJS alone is enough to customize item tooltips and radial menus.
-
东方足道屿 item tooltip

-
Simple radial menu (by SurpTalent)

Canvas
Canvas support that is good enough for charts, SVG drawing, complex nested effects, doodle boards, and similar everyday work.
- Building Shop stock market

- Auto Seam Blend drawing board

World windows
HTML can render at a position inside the world, with configurable angle, block pass-through, interaction distance, view distance, camera follow, LOD, and other details.
-
东方足道屿 maid foot massage

-
Mob health bars and item display (by ୧⍤⃝无月)

Native Minecraft elements
Manage container slots, items, fluids, textures, models, sprite animations, translation keys, and other native elements the HTML way.
-
Building Shop building materials shop

-
Cobblemon Skin Manager (by 卡杨巴)

Rich text editor
Rich text editors can be built in a data-driven way.
- Built-in rich text editor example

WebView
So that different mods' UIs can stay independent while still interacting with each other, ApricityUI uses a multi-document architecture. That removes most of the point of the iframe tag, and Minecraft rarely needs nested interfaces anyway — so today ApricityUI's iframe tag is used entirely to embed WebView.
Put simply, creating a document with an iframe tag actually opens a WebView and draws it off-screen into the iframe's area.
WebView's drawbacks are obvious: from either Java or KubeJS it is hard to find an elegant way to interact with it logically, so ApricityUI currently only forwards hardware input to it.
Its high memory use also makes WebView a poor fit for overlays and world windows.
A small number of environments do not ship a WebView at all (Linux, for example), but this mod will never embed one.
The upside is that WebView partly compensates for ApricityUI's incomplete coverage of browser standards. It can draw complex pages that have little to do with the game, or open external sites directly — live documentation, changelogs, mod tutorial videos — and it supports building an in-world cinema.

For developers
Note: All documents and skills are built into the module file, and AI is ready to use.
Using ApricityUI requires no knowledge of the source code. If you need it, the official Maven is available:
repositories {
maven {
url "https://maven.sighs.cc/repository/maven-public/"
}
}
dependencies {
implementation 'com.sighs:ApricityUI-forge-1.20.1:1.2.7'
}
Developing Web apps with AI is very easy — you do not even need a SKILL, and the same goes for ApricityUI.
A simple example: one sentence generates an interface.

Generating random designs until you like one, then adapting the static template, is a high-efficiency workflow.
A freshly generated static template can be imported and previewed directly in the resource manager; images, audio, fonts, and other static assets can be previewed too, and the context menu offers quick reference.
To avoid key conflicts, starting with 1.2.4 the resource manager shortcuts are unbound by default — bind them yourself before debugging.

Since 1.2.4 the jar bundles the complete documentation, and the config file has options for automatic screenshots (up to 20 kept) and for reloading the matching interface as soon as a file changes. All of these are designed for AI.
Inside an AI coding agent you can phrase it this way to keep iterating on a design:
The task is to create or modify a UI; only HTML/CSS/JS may be changed.
Follow the guidance in the bundled documentation.
Known: the folder run/screenshots/aui outputs a game screenshot every second; static assets are watched automatically and trigger a reload on change.
Test loop: edit the html/css/js files -> watch the log for the reload message -> wait three seconds, then check the newest screenshot in the screenshots folder -> judge whether it matches the requirement; if not, keep editing, if yes, stop.
Target HTML file: run/apricity/test/quest.html
Required result: an in-game quest list, modern flat style, red and white palette
Reference image: run/screenshots/image_614748742442633.png
The full SKILL is documented in the official docs and is bundled in the jar with the full docs.
Complex UI needs are rare in Minecraft. In general, letting AI read the bundled documentation is enough to finish most design work in one pass, and with a built-in theme the result already looks good.
Once the interface is roughly done, you can bring up debugging tools similar to a browser's — also unbound by default since 1.2.4.
In DevTools you can locate and pick elements on the page and edit styles and content directly, with undo and redo, then save the styles or the full DOM content.
Logs about unsupported features, syntax errors, internal exceptions, and similar problems are mirrored into the DevTools console.

You can also borrow ready-made styles from Codepen. If you hit a CSS property you need that is missing, open an issue on Github.
Asset distribution
ApricityUI supports HTML, CSS, Javascript, TTF/OTF fonts, and most image formats including GIF as static assets; audio and video are planned.
Assets live in the apricity folder of the version instance and in resource packs. Resource packs have lower priority, but the default global styles and built-in fonts ship in the mod's own resources.
For modpack developers, the apricity folder is the recommended location. Hot reload is bound to END by default and usually takes under a second.
See the Resource Manager chapter of the official docs for details.
If you want to package and share, either a resource pack or a plain archive works. Since a resource pack also requires reloading the game, a simple archive is recommended.
For mods that use ApricityUI as a dependency, the resource-pack form is better: the default load paths include the development resource-pack path with the highest priority, which is very convenient.
Another option is network assets, such as image hosts or open-source static hosting sites. ApricityUI supports loading network assets asynchronously — just do not do anything bad with it.
ApricityUI also offers a retro "server sends HTML to the client to render" workflow, which lets you manage the client UI entirely from the server.
Want to know more?
- ApricityUI also implements browser-style zoom: hold CTRL and scroll on a page to zoom in and out, so players can adapt to any window size.
- KubeJS is an optional dependency. Without it you cannot run the JavaScript written inside HTML, and the console is unavailable — but for mod developers that rarely matters, since DOM work can be done in Java.
- Hot reload is fast: twenty different QQ avatars plus three custom fonts, including inline JS, reload a whole document in about one second. If you write KubeJS client scripts you might even use it to cut corners — though the resource paths offer no PJS completion. Note that this reload does not reload resource packs.
- ApricityUI has no obvious performance bottleneck in normal use, and on 26.1 and above there is almost nothing to worry about. But the author cannot test every case — if you happen to hit a sudden stutter, please report it immediately.
- Lightweight ports of modern front-end frameworks such as Vue and Svelte are in active development. A simple Vue already has a PR from kltyton, and Svelte will be built in later. Join the group if you are interested!
Gallery
-
Machine-Max vehicle settings

-
FindMe companion management

All Versions
+ Add Version| Name | Game version | Platforms | Published | Downloads | Actions |
|---|---|---|---|---|---|
| 6d ago | 1 | ||||
| 6d ago | 1 | ||||
| 6d ago | 1 | ||||
| 6d ago | 1 | ||||
| 6d ago | 1 | ||||
| 6d ago | 1 | ||||
| 6d ago | 1 | ||||
| 6d ago | 1 | ||||
| 20d ago | 1 | ||||
| 20d ago | 1 | ||||
| 20d ago | 1 | ||||
| 20d ago | 1 | ||||
| 20d ago | 1 | ||||
| 20d ago | 1 | ||||
| 20d ago | 1 | ||||
| 20d ago | 1 | ||||
| 23d ago | 1 | ||||
| 23d ago | 1 | ||||
| 23d ago | 1 | ||||
| 23d ago | 1 | ||||
| 23d ago | 1 | ||||
| 23d ago | 1 | ||||
| 23d ago | 1 | ||||
| 23d ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 5mo ago | 1 | ||||
| 5mo ago | 1 | ||||
| 5mo ago | 1 | ||||
| 5mo ago | 1 | ||||
| 5mo ago | 1 | ||||
| 6mo ago | 1 | ||||
| 6mo ago | 1 | ||||
| 6mo ago | 1 | ||||
| 6mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 8mo ago | 1 | ||||
| 8mo ago | 1 | ||||
| 8mo ago | 1 | ||||
| 8mo ago | 1 | ||||
| 8mo ago | 1 |
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
- Added 14 standardized, switchable CSS themes: Ore, Claude Square/Round, CurseForge, Modrinth Light/Dark, Arwes and Arwes Transparent, Neobrutalist, Hand-Drawn Doodle, Retro Vintage, Vaporwave, Impressionist Oil, and Mecha.
apricityui/theme/t00-example.htmlpreviews the same component markup across themes and loads theme stylesheets on demand. - Added the self-contained
<theme-switcher>floating control, with runtime stylesheet loading, theme scope management, andaui-theme-changenotifications. - Added
IntersectionObserversupport and documentation. - Added CSS
outline, its width/style/color/offset longhands, browser-style paint ordering, rounded geometry, visual bounds, and clipping behavior. Outlines do not affect layout; unsupported line patterns still fall back to solid and outline transitions are not yet supported. - Added
perspective()transform parsing and projective geometry for perspective backgrounds, with matching visual bounds and pointer-coordinate mapping.
### Changed
- Standardized theme tokens and component conventions, expanded the theme documentation, and replaced individual demo pages with the shared theme preview.
- Removed the old bundled McUI theme. Pages using its resource paths need to migrate; the new Ore theme is available at
apricityui/theme/t01-ore/t01-ore.css. The existing legacy Ore resources are separate from the new standardized theme. - Reduced packaged resource overhead and redundant bundled assets, and updated the embedded English documentation.
### Performance
- Reworked custom-font rendering around reusable glyph caches instead of whole-line bitmaps, reducing repeated rasterization and texture work.
- Reduced redundant layout invalidation: ancestor changes no longer repeatedly clear overlapping descendant trees, position caches validate their geometry dependencies, and refresh-time full-tree clearing handles each node locally.
- Reused compiled selector token patterns, removed exception-based handling of unsupported CSS properties, and avoided repeated font-name-table reads when looking up derived fonts.
- Skipped the redundant final style pass for documents with no applicable NeoForge 1.21.1 expansion work; unknown or changing expanders retain the complete style pass.
- Improved DevTools DOM-tree expansion and scrolling for large child lists, including cached subtree counts and reduced rebuilding.
- Improved repeated-gradient geometry generation and bounded repeat work while retaining color-stop coverage. Added physical-pixel coverage for thin perspective grid bands.
### Fixed
- Corrected browser-parity issues across theme layouts: flex wrapping and anonymous text, positioned flex sizing, absolute static positions and shrink-to-fit text, max-width inline wrapping, input stretching, text transforms, border rendering, rounded zero-blur shadows, and variable-backed background shorthands.
- Fixed repeating-gradient image sizing and phase, clipped gradient backgrounds, shadow falloff and knockout, SVG image handling, and the Vaporwave perspective floor grid.
- Fixed HTML/tag extraction that could truncate documents, and improved runtime stylesheet refresh and link lifecycle handling.
- Fixed stale or incorrectly ordered incremental hit-test geometry, root inline-transform reentrancy, and redundant multi-document layer ordering.
- Fixed item tooltips hidden behind UI documents, native items incorrectly showing through overlays, and item lighting changing with GUI scale.
- Fixed native Minecraft-font rendering at custom UI batch boundaries.
- Fixed unbound/unknown key codes opening tools and Alt handling consuming unrelated native clicks; corrected Forge 1.18.2 mouse interception to use the cancellable raw-input event.
- Fixed 26.1 client/render integration and filter/backdrop-filter state, viewport scaling, and pipeline handling.
- Fixed Windows WebView native-library compatibility with older MSVC runtimes already loaded by JBR 21.0.8, and added native smoke-test tooling.
- Removed the custom JavaScript
consolethat conflicted with KubeJS.
### Added
- Layout metrics are typed values now, not strings.
layout/CssLengthparses a CSS length once (leading-number scan,calc()/min()/max()/clamp(), unit conversion) and resolves it on demand, so the layout pass no longer re-parseswidth/height/min-*/max-*/flex-basisor thebox-sizingkeyword on every visit;Stylegains lazy accessors (widthLength(),isBorderBox(), …) that compile at computed-style time and are invalidated onclone()/copyFrom()so animation buffers cannot read the previous frame's geometry.Size's public API now delegates toCssLength, which keeps the two implementations from drifting apart. overflowandvisibilitynormalize to theInteraction.Overflow/Interaction.Visibilityenums, anddisplay/overflowquestions are answered by memoizedStyleaccessors (isFlexDisplay(),isGridDisplay(),isInFlow(),clipsOverflow(), …) instead oftrim()+toLowerCase()on every call. Only the memoized form changed:Layout.isInFlowandInteraction.isDisplayedkeep their exact-match semantics, whileisFlexDisplay/isGridDisplaystay case-insensitive, as before.- The first full geometry commit of a document can be sliced across frames. A page whose initial commit would block one frame for hundreds of milliseconds now pays a per-frame budget instead (
initialCommitSliceEnabled,initialCommitSliceMs, default 16 ms), and the document is not painted until its geometry is complete.-Dapricityui.layout.sliceInitialCommit=falserestores the single-shot commit. Small documents and non-render callers (the tick path, hit testing) keep committing synchronously, because they rely oncommit()returning with complete geometry. ApricityUI.ensureDocument(path)returns the existing document for a path or creates one, so callers no longer have to testDocument.get(path)themselves.
### Changed
ApricityUI.createDocument(path)now marks the document reload-persistent, which is what lets a document created from a screen survive a resource reload and open as an overlay.Document.create(path)is unchanged.- Debug log volume is down substantially. The per-draw font warnings ("blank text draw", which fired once per line that had not been rasterized yet) are capped at 8 per process instead of 1200, and no longer embed the full raster cache key.
CanvasImageSupport's per-draw warnings are de-duplicated per resource, so a broken image logs once rather than every frame. Leftover commented-out log blocks, a deadImage.loadTextureoverload, the unreachableQwenHtmlOverlayDebugclass and one repeated failure log in each ofStyleAsyncHandlerandNetworkAsyncHandlerare gone; a number of per-eventINFOlines ([AUI Screen],[AUI Viewport],NetworkUtilchunking, Fabric network registration) moved toDEBUG.
### Fixed
- Buttons in the Ore and McUI themes no longer grow taller while pressed.
.button:activecarried hardcodedpadding-top: 9px; padding-bottom: 11px— correct for the base button's7px/13px, but.button-normal(8px/8px) and.button-small(5px/9px) have no fixed height, so pressing them added 4 px and 6 px of height respectively and pushed the surrounding content down. Both variants now keep their own vertical padding sum on:active, so only the 2 px "pressed" shift remains. - A grid could lay some of its items out against a different track list than the rest, leaving a card outside its container. Two independent causes, both fixed. First, a grid container with
width: autoderived its available track space from an ancestor width whenever its own used size was not cached yet — and it never was on the first measurement, becauseSize.ofshort-circuits on that very cache. Second,RenderElement.dependency(), the stamp that decides whether a committed rect is still valid, mixed in only thelayoutVersioncounters along the route and not the ancestors' used sizes, while the layout caches' own stamp (usedSizeDependency()) did include them. When an ancestor's width changed without anyone bumping a counter, the layout was recomputed but the committed rect was not.dependency()now mixes in ancestor sizes likeusedSizeDependency()does. - A stale
positioncache could survive its container's resize permanently.RenderElement.size's cache invalidates itself when its dependency goes stale, but it did not tell its children:positionhas no dependency stamp of its own and is only cleared explicitly, and a grid's track change does not travel the style-invalidation path.size.get()now drops its children'spositionand committed geometry when it finds itself stale. This is what made a mispositioned card stick until something forced a full relayout. - Switching
:checkedrecomputed the whole document.Element.setCheckedrequested a style recalc on the document element, so toggling any checkbox or radio re-ran style resolution over every element on the page. The affected set for a:checkedchange is the element's own subtree plus its following siblings' subtrees — the combinators (>,+,~, descendant) only look forwards and the selector engine has no:has— which is exactlysubtree(parent). The recalc is now scoped to that, andenforceRadioGroupCheckedrequests the same scope for each radio it unchecks, since those may live anywhere in the document and previously relied on the whole-document pass to refresh their own following siblings. - A grid's
gapwas rounded to whole pixels.Grid.Gapsheldints and parsed the values withSize.parse, which rounds, so a non-integer gap (1.84pxfrom a scrollbar inset,3.5pxfrom avar()or a shorthand expansion) was silently changed and the tracks no longer added up to the container's width. Gaps aredoubles parsed throughCssLengthnow. - A scroll container's scrollbar could appear one frame late.
ScrollModel.commitLayoutMetrics()both measures the scroll area and flips the scrollbar's visibility — a layout input, since the gutter comes out of the content box — but it only marked the layout dirty; whether that became an invalidation depended on where it was called from. The geometry-commit path calls it *after* committing geometry, so the invalidation landed on the next 20 Hz tick and the frame that had just been committed stayed laid out at the old width. The commit path now invalidates immediately (commitScrollMetricsAfterLayoutCommit).
### Added
- Layout metrics are typed values now, not strings.
layout/CssLengthparses a CSS length once (leading-number scan,calc()/min()/max()/clamp(), unit conversion) and resolves it on demand, so the layout pass no longer re-parseswidth/height/min-*/max-*/flex-basisor thebox-sizingkeyword on every visit;Stylegains lazy accessors (widthLength(),isBorderBox(), …) that compile at computed-style time and are invalidated onclone()/copyFrom()so animation buffers cannot read the previous frame's geometry.Size's public API now delegates toCssLength, which keeps the two implementations from drifting apart. overflowandvisibilitynormalize to theInteraction.Overflow/Interaction.Visibilityenums, anddisplay/overflowquestions are answered by memoizedStyleaccessors (isFlexDisplay(),isGridDisplay(),isInFlow(),clipsOverflow(), …) instead oftrim()+toLowerCase()on every call. Only the memoized form changed:Layout.isInFlowandInteraction.isDisplayedkeep their exact-match semantics, whileisFlexDisplay/isGridDisplaystay case-insensitive, as before.- The first full geometry commit of a document can be sliced across frames. A page whose initial commit would block one frame for hundreds of milliseconds now pays a per-frame budget instead (
initialCommitSliceEnabled,initialCommitSliceMs, default 16 ms), and the document is not painted until its geometry is complete.-Dapricityui.layout.sliceInitialCommit=falserestores the single-shot commit. Small documents and non-render callers (the tick path, hit testing) keep committing synchronously, because they rely oncommit()returning with complete geometry. ApricityUI.ensureDocument(path)returns the existing document for a path or creates one, so callers no longer have to testDocument.get(path)themselves.
### Changed
ApricityUI.createDocument(path)now marks the document reload-persistent, which is what lets a document created from a screen survive a resource reload and open as an overlay.Document.create(path)is unchanged.- Debug log volume is down substantially. The per-draw font warnings ("blank text draw", which fired once per line that had not been rasterized yet) are capped at 8 per process instead of 1200, and no longer embed the full raster cache key.
CanvasImageSupport's per-draw warnings are de-duplicated per resource, so a broken image logs once rather than every frame. Leftover commented-out log blocks, a deadImage.loadTextureoverload, the unreachableQwenHtmlOverlayDebugclass and one repeated failure log in each ofStyleAsyncHandlerandNetworkAsyncHandlerare gone; a number of per-eventINFOlines ([AUI Screen],[AUI Viewport],NetworkUtilchunking, Fabric network registration) moved toDEBUG.
### Fixed
- Buttons in the Ore and McUI themes no longer grow taller while pressed.
.button:activecarried hardcodedpadding-top: 9px; padding-bottom: 11px— correct for the base button's7px/13px, but.button-normal(8px/8px) and.button-small(5px/9px) have no fixed height, so pressing them added 4 px and 6 px of height respectively and pushed the surrounding content down. Both variants now keep their own vertical padding sum on:active, so only the 2 px "pressed" shift remains. - A grid could lay some of its items out against a different track list than the rest, leaving a card outside its container. Two independent causes, both fixed. First, a grid container with
width: autoderived its available track space from an ancestor width whenever its own used size was not cached yet — and it never was on the first measurement, becauseSize.ofshort-circuits on that very cache. Second,RenderElement.dependency(), the stamp that decides whether a committed rect is still valid, mixed in only thelayoutVersioncounters along the route and not the ancestors' used sizes, while the layout caches' own stamp (usedSizeDependency()) did include them. When an ancestor's width changed without anyone bumping a counter, the layout was recomputed but the committed rect was not.dependency()now mixes in ancestor sizes likeusedSizeDependency()does. - A stale
positioncache could survive its container's resize permanently.RenderElement.size's cache invalidates itself when its dependency goes stale, but it did not tell its children:positionhas no dependency stamp of its own and is only cleared explicitly, and a grid's track change does not travel the style-invalidation path.size.get()now drops its children'spositionand committed geometry when it finds itself stale. This is what made a mispositioned card stick until something forced a full relayout. - Switching
:checkedrecomputed the whole document.Element.setCheckedrequested a style recalc on the document element, so toggling any checkbox or radio re-ran style resolution over every element on the page. The affected set for a:checkedchange is the element's own subtree plus its following siblings' subtrees — the combinators (>,+,~, descendant) only look forwards and the selector engine has no:has— which is exactlysubtree(parent). The recalc is now scoped to that, andenforceRadioGroupCheckedrequests the same scope for each radio it unchecks, since those may live anywhere in the document and previously relied on the whole-document pass to refresh their own following siblings. - A grid's
gapwas rounded to whole pixels.Grid.Gapsheldints and parsed the values withSize.parse, which rounds, so a non-integer gap (1.84pxfrom a scrollbar inset,3.5pxfrom avar()or a shorthand expansion) was silently changed and the tracks no longer added up to the container's width. Gaps aredoubles parsed throughCssLengthnow. - A scroll container's scrollbar could appear one frame late.
ScrollModel.commitLayoutMetrics()both measures the scroll area and flips the scrollbar's visibility — a layout input, since the gutter comes out of the content box — but it only marked the layout dirty; whether that became an invalidation depended on where it was called from. The geometry-commit path calls it *after* committing geometry, so the invalidation landed on the next 20 Hz tick and the frame that had just been committed stayed laid out at the old width. The commit path now invalidates immediately (commitScrollMetricsAfterLayoutCommit).
### Added
- Layout metrics are typed values now, not strings.
layout/CssLengthparses a CSS length once (leading-number scan,calc()/min()/max()/clamp(), unit conversion) and resolves it on demand, so the layout pass no longer re-parseswidth/height/min-*/max-*/flex-basisor thebox-sizingkeyword on every visit;Stylegains lazy accessors (widthLength(),isBorderBox(), …) that compile at computed-style time and are invalidated onclone()/copyFrom()so animation buffers cannot read the previous frame's geometry.Size's public API now delegates toCssLength, which keeps the two implementations from drifting apart. overflowandvisibilitynormalize to theInteraction.Overflow/Interaction.Visibilityenums, anddisplay/overflowquestions are answered by memoizedStyleaccessors (isFlexDisplay(),isGridDisplay(),isInFlow(),clipsOverflow(), …) instead oftrim()+toLowerCase()on every call. Only the memoized form changed:Layout.isInFlowandInteraction.isDisplayedkeep their exact-match semantics, whileisFlexDisplay/isGridDisplaystay case-insensitive, as before.- The first full geometry commit of a document can be sliced across frames. A page whose initial commit would block one frame for hundreds of milliseconds now pays a per-frame budget instead (
initialCommitSliceEnabled,initialCommitSliceMs, default 16 ms), and the document is not painted until its geometry is complete.-Dapricityui.layout.sliceInitialCommit=falserestores the single-shot commit. Small documents and non-render callers (the tick path, hit testing) keep committing synchronously, because they rely oncommit()returning with complete geometry. ApricityUI.ensureDocument(path)returns the existing document for a path or creates one, so callers no longer have to testDocument.get(path)themselves.
### Changed
ApricityUI.createDocument(path)now marks the document reload-persistent, which is what lets a document created from a screen survive a resource reload and open as an overlay.Document.create(path)is unchanged.- Debug log volume is down substantially. The per-draw font warnings ("blank text draw", which fired once per line that had not been rasterized yet) are capped at 8 per process instead of 1200, and no longer embed the full raster cache key.
CanvasImageSupport's per-draw warnings are de-duplicated per resource, so a broken image logs once rather than every frame. Leftover commented-out log blocks, a deadImage.loadTextureoverload, the unreachableQwenHtmlOverlayDebugclass and one repeated failure log in each ofStyleAsyncHandlerandNetworkAsyncHandlerare gone; a number of per-eventINFOlines ([AUI Screen],[AUI Viewport],NetworkUtilchunking, Fabric network registration) moved toDEBUG.
### Fixed
- Buttons in the Ore and McUI themes no longer grow taller while pressed.
.button:activecarried hardcodedpadding-top: 9px; padding-bottom: 11px— correct for the base button's7px/13px, but.button-normal(8px/8px) and.button-small(5px/9px) have no fixed height, so pressing them added 4 px and 6 px of height respectively and pushed the surrounding content down. Both variants now keep their own vertical padding sum on:active, so only the 2 px "pressed" shift remains. - A grid could lay some of its items out against a different track list than the rest, leaving a card outside its container. Two independent causes, both fixed. First, a grid container with
width: autoderived its available track space from an ancestor width whenever its own used size was not cached yet — and it never was on the first measurement, becauseSize.ofshort-circuits on that very cache. Second,RenderElement.dependency(), the stamp that decides whether a committed rect is still valid, mixed in only thelayoutVersioncounters along the route and not the ancestors' used sizes, while the layout caches' own stamp (usedSizeDependency()) did include them. When an ancestor's width changed without anyone bumping a counter, the layout was recomputed but the committed rect was not.dependency()now mixes in ancestor sizes likeusedSizeDependency()does. - A stale
positioncache could survive its container's resize permanently.RenderElement.size's cache invalidates itself when its dependency goes stale, but it did not tell its children:positionhas no dependency stamp of its own and is only cleared explicitly, and a grid's track change does not travel the style-invalidation path.size.get()now drops its children'spositionand committed geometry when it finds itself stale. This is what made a mispositioned card stick until something forced a full relayout. - Switching
:checkedrecomputed the whole document.Element.setCheckedrequested a style recalc on the document element, so toggling any checkbox or radio re-ran style resolution over every element on the page. The affected set for a:checkedchange is the element's own subtree plus its following siblings' subtrees — the combinators (>,+,~, descendant) only look forwards and the selector engine has no:has— which is exactlysubtree(parent). The recalc is now scoped to that, andenforceRadioGroupCheckedrequests the same scope for each radio it unchecks, since those may live anywhere in the document and previously relied on the whole-document pass to refresh their own following siblings. - A grid's
gapwas rounded to whole pixels.Grid.Gapsheldints and parsed the values withSize.parse, which rounds, so a non-integer gap (1.84pxfrom a scrollbar inset,3.5pxfrom avar()or a shorthand expansion) was silently changed and the tracks no longer added up to the container's width. Gaps aredoubles parsed throughCssLengthnow. - A scroll container's scrollbar could appear one frame late.
ScrollModel.commitLayoutMetrics()both measures the scroll area and flips the scrollbar's visibility — a layout input, since the gutter comes out of the content box — but it only marked the layout dirty; whether that became an invalidation depended on where it was called from. The geometry-commit path calls it *after* committing geometry, so the invalidation landed on the next 20 Hz tick and the frame that had just been committed stayed laid out at the old width. The commit path now invalidates immediately (commitScrollMetricsAfterLayoutCommit).
### Added
- Layout metrics are typed values now, not strings.
layout/CssLengthparses a CSS length once (leading-number scan,calc()/min()/max()/clamp(), unit conversion) and resolves it on demand, so the layout pass no longer re-parseswidth/height/min-*/max-*/flex-basisor thebox-sizingkeyword on every visit;Stylegains lazy accessors (widthLength(),isBorderBox(), …) that compile at computed-style time and are invalidated onclone()/copyFrom()so animation buffers cannot read the previous frame's geometry.Size's public API now delegates toCssLength, which keeps the two implementations from drifting apart. overflowandvisibilitynormalize to theInteraction.Overflow/Interaction.Visibilityenums, anddisplay/overflowquestions are answered by memoizedStyleaccessors (isFlexDisplay(),isGridDisplay(),isInFlow(),clipsOverflow(), …) instead oftrim()+toLowerCase()on every call. Only the memoized form changed:Layout.isInFlowandInteraction.isDisplayedkeep their exact-match semantics, whileisFlexDisplay/isGridDisplaystay case-insensitive, as before.- The first full geometry commit of a document can be sliced across frames. A page whose initial commit would block one frame for hundreds of milliseconds now pays a per-frame budget instead (
initialCommitSliceEnabled,initialCommitSliceMs, default 16 ms), and the document is not painted until its geometry is complete.-Dapricityui.layout.sliceInitialCommit=falserestores the single-shot commit. Small documents and non-render callers (the tick path, hit testing) keep committing synchronously, because they rely oncommit()returning with complete geometry. ApricityUI.ensureDocument(path)returns the existing document for a path or creates one, so callers no longer have to testDocument.get(path)themselves.
### Changed
ApricityUI.createDocument(path)now marks the document reload-persistent, which is what lets a document created from a screen survive a resource reload and open as an overlay.Document.create(path)is unchanged.- Debug log volume is down substantially. The per-draw font warnings ("blank text draw", which fired once per line that had not been rasterized yet) are capped at 8 per process instead of 1200, and no longer embed the full raster cache key.
CanvasImageSupport's per-draw warnings are de-duplicated per resource, so a broken image logs once rather than every frame. Leftover commented-out log blocks, a deadImage.loadTextureoverload, the unreachableQwenHtmlOverlayDebugclass and one repeated failure log in each ofStyleAsyncHandlerandNetworkAsyncHandlerare gone; a number of per-eventINFOlines ([AUI Screen],[AUI Viewport],NetworkUtilchunking, Fabric network registration) moved toDEBUG.
### Fixed
- Buttons in the Ore and McUI themes no longer grow taller while pressed.
.button:activecarried hardcodedpadding-top: 9px; padding-bottom: 11px— correct for the base button's7px/13px, but.button-normal(8px/8px) and.button-small(5px/9px) have no fixed height, so pressing them added 4 px and 6 px of height respectively and pushed the surrounding content down. Both variants now keep their own vertical padding sum on:active, so only the 2 px "pressed" shift remains. - A grid could lay some of its items out against a different track list than the rest, leaving a card outside its container. Two independent causes, both fixed. First, a grid container with
width: autoderived its available track space from an ancestor width whenever its own used size was not cached yet — and it never was on the first measurement, becauseSize.ofshort-circuits on that very cache. Second,RenderElement.dependency(), the stamp that decides whether a committed rect is still valid, mixed in only thelayoutVersioncounters along the route and not the ancestors' used sizes, while the layout caches' own stamp (usedSizeDependency()) did include them. When an ancestor's width changed without anyone bumping a counter, the layout was recomputed but the committed rect was not.dependency()now mixes in ancestor sizes likeusedSizeDependency()does. - A stale
positioncache could survive its container's resize permanently.RenderElement.size's cache invalidates itself when its dependency goes stale, but it did not tell its children:positionhas no dependency stamp of its own and is only cleared explicitly, and a grid's track change does not travel the style-invalidation path.size.get()now drops its children'spositionand committed geometry when it finds itself stale. This is what made a mispositioned card stick until something forced a full relayout. - Switching
:checkedrecomputed the whole document.Element.setCheckedrequested a style recalc on the document element, so toggling any checkbox or radio re-ran style resolution over every element on the page. The affected set for a:checkedchange is the element's own subtree plus its following siblings' subtrees — the combinators (>,+,~, descendant) only look forwards and the selector engine has no:has— which is exactlysubtree(parent). The recalc is now scoped to that, andenforceRadioGroupCheckedrequests the same scope for each radio it unchecks, since those may live anywhere in the document and previously relied on the whole-document pass to refresh their own following siblings. - A grid's
gapwas rounded to whole pixels.Grid.Gapsheldints and parsed the values withSize.parse, which rounds, so a non-integer gap (1.84pxfrom a scrollbar inset,3.5pxfrom avar()or a shorthand expansion) was silently changed and the tracks no longer added up to the container's width. Gaps aredoubles parsed throughCssLengthnow. - A scroll container's scrollbar could appear one frame late.
ScrollModel.commitLayoutMetrics()both measures the scroll area and flips the scrollbar's visibility — a layout input, since the gutter comes out of the content box — but it only marked the layout dirty; whether that became an invalidation depended on where it was called from. The geometry-commit path calls it *after* committing geometry, so the invalidation landed on the next 20 Hz tick and the frame that had just been committed stayed laid out at the old width. The commit path now invalidates immediately (commitScrollMetricsAfterLayoutCommit).
### Added
- Layout metrics are typed values now, not strings.
layout/CssLengthparses a CSS length once (leading-number scan,calc()/min()/max()/clamp(), unit conversion) and resolves it on demand, so the layout pass no longer re-parseswidth/height/min-*/max-*/flex-basisor thebox-sizingkeyword on every visit;Stylegains lazy accessors (widthLength(),isBorderBox(), …) that compile at computed-style time and are invalidated onclone()/copyFrom()so animation buffers cannot read the previous frame's geometry.Size's public API now delegates toCssLength, which keeps the two implementations from drifting apart. overflowandvisibilitynormalize to theInteraction.Overflow/Interaction.Visibilityenums, anddisplay/overflowquestions are answered by memoizedStyleaccessors (isFlexDisplay(),isGridDisplay(),isInFlow(),clipsOverflow(), …) instead oftrim()+toLowerCase()on every call. Only the memoized form changed:Layout.isInFlowandInteraction.isDisplayedkeep their exact-match semantics, whileisFlexDisplay/isGridDisplaystay case-insensitive, as before.- The first full geometry commit of a document can be sliced across frames. A page whose initial commit would block one frame for hundreds of milliseconds now pays a per-frame budget instead (
initialCommitSliceEnabled,initialCommitSliceMs, default 16 ms), and the document is not painted until its geometry is complete.-Dapricityui.layout.sliceInitialCommit=falserestores the single-shot commit. Small documents and non-render callers (the tick path, hit testing) keep committing synchronously, because they rely oncommit()returning with complete geometry. ApricityUI.ensureDocument(path)returns the existing document for a path or creates one, so callers no longer have to testDocument.get(path)themselves.
### Changed
ApricityUI.createDocument(path)now marks the document reload-persistent, which is what lets a document created from a screen survive a resource reload and open as an overlay.Document.create(path)is unchanged.- Debug log volume is down substantially. The per-draw font warnings ("blank text draw", which fired once per line that had not been rasterized yet) are capped at 8 per process instead of 1200, and no longer embed the full raster cache key.
CanvasImageSupport's per-draw warnings are de-duplicated per resource, so a broken image logs once rather than every frame. Leftover commented-out log blocks, a deadImage.loadTextureoverload, the unreachableQwenHtmlOverlayDebugclass and one repeated failure log in each ofStyleAsyncHandlerandNetworkAsyncHandlerare gone; a number of per-eventINFOlines ([AUI Screen],[AUI Viewport],NetworkUtilchunking, Fabric network registration) moved toDEBUG.
### Fixed
- Buttons in the Ore and McUI themes no longer grow taller while pressed.
.button:activecarried hardcodedpadding-top: 9px; padding-bottom: 11px— correct for the base button's7px/13px, but.button-normal(8px/8px) and.button-small(5px/9px) have no fixed height, so pressing them added 4 px and 6 px of height respectively and pushed the surrounding content down. Both variants now keep their own vertical padding sum on:active, so only the 2 px "pressed" shift remains. - A grid could lay some of its items out against a different track list than the rest, leaving a card outside its container. Two independent causes, both fixed. First, a grid container with
width: autoderived its available track space from an ancestor width whenever its own used size was not cached yet — and it never was on the first measurement, becauseSize.ofshort-circuits on that very cache. Second,RenderElement.dependency(), the stamp that decides whether a committed rect is still valid, mixed in only thelayoutVersioncounters along the route and not the ancestors' used sizes, while the layout caches' own stamp (usedSizeDependency()) did include them. When an ancestor's width changed without anyone bumping a counter, the layout was recomputed but the committed rect was not.dependency()now mixes in ancestor sizes likeusedSizeDependency()does. - A stale
positioncache could survive its container's resize permanently.RenderElement.size's cache invalidates itself when its dependency goes stale, but it did not tell its children:positionhas no dependency stamp of its own and is only cleared explicitly, and a grid's track change does not travel the style-invalidation path.size.get()now drops its children'spositionand committed geometry when it finds itself stale. This is what made a mispositioned card stick until something forced a full relayout. - Switching
:checkedrecomputed the whole document.Element.setCheckedrequested a style recalc on the document element, so toggling any checkbox or radio re-ran style resolution over every element on the page. The affected set for a:checkedchange is the element's own subtree plus its following siblings' subtrees — the combinators (>,+,~, descendant) only look forwards and the selector engine has no:has— which is exactlysubtree(parent). The recalc is now scoped to that, andenforceRadioGroupCheckedrequests the same scope for each radio it unchecks, since those may live anywhere in the document and previously relied on the whole-document pass to refresh their own following siblings. - A grid's
gapwas rounded to whole pixels.Grid.Gapsheldints and parsed the values withSize.parse, which rounds, so a non-integer gap (1.84pxfrom a scrollbar inset,3.5pxfrom avar()or a shorthand expansion) was silently changed and the tracks no longer added up to the container's width. Gaps aredoubles parsed throughCssLengthnow. - A scroll container's scrollbar could appear one frame late.
ScrollModel.commitLayoutMetrics()both measures the scroll area and flips the scrollbar's visibility — a layout input, since the gutter comes out of the content box — but it only marked the layout dirty; whether that became an invalidation depended on where it was called from. The geometry-commit path calls it *after* committing geometry, so the invalidation landed on the next 20 Hz tick and the frame that had just been committed stayed laid out at the old width. The commit path now invalidates immediately (commitScrollMetricsAfterLayoutCommit).
### Added
- Layout metrics are typed values now, not strings.
layout/CssLengthparses a CSS length once (leading-number scan,calc()/min()/max()/clamp(), unit conversion) and resolves it on demand, so the layout pass no longer re-parseswidth/height/min-*/max-*/flex-basisor thebox-sizingkeyword on every visit;Stylegains lazy accessors (widthLength(),isBorderBox(), …) that compile at computed-style time and are invalidated onclone()/copyFrom()so animation buffers cannot read the previous frame's geometry.Size's public API now delegates toCssLength, which keeps the two implementations from drifting apart. overflowandvisibilitynormalize to theInteraction.Overflow/Interaction.Visibilityenums, anddisplay/overflowquestions are answered by memoizedStyleaccessors (isFlexDisplay(),isGridDisplay(),isInFlow(),clipsOverflow(), …) instead oftrim()+toLowerCase()on every call. Only the memoized form changed:Layout.isInFlowandInteraction.isDisplayedkeep their exact-match semantics, whileisFlexDisplay/isGridDisplaystay case-insensitive, as before.- The first full geometry commit of a document can be sliced across frames. A page whose initial commit would block one frame for hundreds of milliseconds now pays a per-frame budget instead (
initialCommitSliceEnabled,initialCommitSliceMs, default 16 ms), and the document is not painted until its geometry is complete.-Dapricityui.layout.sliceInitialCommit=falserestores the single-shot commit. Small documents and non-render callers (the tick path, hit testing) keep committing synchronously, because they rely oncommit()returning with complete geometry. ApricityUI.ensureDocument(path)returns the existing document for a path or creates one, so callers no longer have to testDocument.get(path)themselves.
### Changed
ApricityUI.createDocument(path)now marks the document reload-persistent, which is what lets a document created from a screen survive a resource reload and open as an overlay.Document.create(path)is unchanged.- Debug log volume is down substantially. The per-draw font warnings ("blank text draw", which fired once per line that had not been rasterized yet) are capped at 8 per process instead of 1200, and no longer embed the full raster cache key.
CanvasImageSupport's per-draw warnings are de-duplicated per resource, so a broken image logs once rather than every frame. Leftover commented-out log blocks, a deadImage.loadTextureoverload, the unreachableQwenHtmlOverlayDebugclass and one repeated failure log in each ofStyleAsyncHandlerandNetworkAsyncHandlerare gone; a number of per-eventINFOlines ([AUI Screen],[AUI Viewport],NetworkUtilchunking, Fabric network registration) moved toDEBUG.
### Fixed
- Buttons in the Ore and McUI themes no longer grow taller while pressed.
.button:activecarried hardcodedpadding-top: 9px; padding-bottom: 11px— correct for the base button's7px/13px, but.button-normal(8px/8px) and.button-small(5px/9px) have no fixed height, so pressing them added 4 px and 6 px of height respectively and pushed the surrounding content down. Both variants now keep their own vertical padding sum on:active, so only the 2 px "pressed" shift remains. - A grid could lay some of its items out against a different track list than the rest, leaving a card outside its container. Two independent causes, both fixed. First, a grid container with
width: autoderived its available track space from an ancestor width whenever its own used size was not cached yet — and it never was on the first measurement, becauseSize.ofshort-circuits on that very cache. Second,RenderElement.dependency(), the stamp that decides whether a committed rect is still valid, mixed in only thelayoutVersioncounters along the route and not the ancestors' used sizes, while the layout caches' own stamp (usedSizeDependency()) did include them. When an ancestor's width changed without anyone bumping a counter, the layout was recomputed but the committed rect was not.dependency()now mixes in ancestor sizes likeusedSizeDependency()does. - A stale
positioncache could survive its container's resize permanently.RenderElement.size's cache invalidates itself when its dependency goes stale, but it did not tell its children:positionhas no dependency stamp of its own and is only cleared explicitly, and a grid's track change does not travel the style-invalidation path.size.get()now drops its children'spositionand committed geometry when it finds itself stale. This is what made a mispositioned card stick until something forced a full relayout. - Switching
:checkedrecomputed the whole document.Element.setCheckedrequested a style recalc on the document element, so toggling any checkbox or radio re-ran style resolution over every element on the page. The affected set for a:checkedchange is the element's own subtree plus its following siblings' subtrees — the combinators (>,+,~, descendant) only look forwards and the selector engine has no:has— which is exactlysubtree(parent). The recalc is now scoped to that, andenforceRadioGroupCheckedrequests the same scope for each radio it unchecks, since those may live anywhere in the document and previously relied on the whole-document pass to refresh their own following siblings. - A grid's
gapwas rounded to whole pixels.Grid.Gapsheldints and parsed the values withSize.parse, which rounds, so a non-integer gap (1.84pxfrom a scrollbar inset,3.5pxfrom avar()or a shorthand expansion) was silently changed and the tracks no longer added up to the container's width. Gaps aredoubles parsed throughCssLengthnow. - A scroll container's scrollbar could appear one frame late.
ScrollModel.commitLayoutMetrics()both measures the scroll area and flips the scrollbar's visibility — a layout input, since the gutter comes out of the content box — but it only marked the layout dirty; whether that became an invalidation depended on where it was called from. The geometry-commit path calls it *after* committing geometry, so the invalidation landed on the next 20 Hz tick and the frame that had just been committed stayed laid out at the old width. The commit path now invalidates immediately (commitScrollMetricsAfterLayoutCommit).
### Added
- Layout metrics are typed values now, not strings.
layout/CssLengthparses a CSS length once (leading-number scan,calc()/min()/max()/clamp(), unit conversion) and resolves it on demand, so the layout pass no longer re-parseswidth/height/min-*/max-*/flex-basisor thebox-sizingkeyword on every visit;Stylegains lazy accessors (widthLength(),isBorderBox(), …) that compile at computed-style time and are invalidated onclone()/copyFrom()so animation buffers cannot read the previous frame's geometry.Size's public API now delegates toCssLength, which keeps the two implementations from drifting apart. overflowandvisibilitynormalize to theInteraction.Overflow/Interaction.Visibilityenums, anddisplay/overflowquestions are answered by memoizedStyleaccessors (isFlexDisplay(),isGridDisplay(),isInFlow(),clipsOverflow(), …) instead oftrim()+toLowerCase()on every call. Only the memoized form changed:Layout.isInFlowandInteraction.isDisplayedkeep their exact-match semantics, whileisFlexDisplay/isGridDisplaystay case-insensitive, as before.- The first full geometry commit of a document can be sliced across frames. A page whose initial commit would block one frame for hundreds of milliseconds now pays a per-frame budget instead (
initialCommitSliceEnabled,initialCommitSliceMs, default 16 ms), and the document is not painted until its geometry is complete.-Dapricityui.layout.sliceInitialCommit=falserestores the single-shot commit. Small documents and non-render callers (the tick path, hit testing) keep committing synchronously, because they rely oncommit()returning with complete geometry. ApricityUI.ensureDocument(path)returns the existing document for a path or creates one, so callers no longer have to testDocument.get(path)themselves.
### Changed
ApricityUI.createDocument(path)now marks the document reload-persistent, which is what lets a document created from a screen survive a resource reload and open as an overlay.Document.create(path)is unchanged.- Debug log volume is down substantially. The per-draw font warnings ("blank text draw", which fired once per line that had not been rasterized yet) are capped at 8 per process instead of 1200, and no longer embed the full raster cache key.
CanvasImageSupport's per-draw warnings are de-duplicated per resource, so a broken image logs once rather than every frame. Leftover commented-out log blocks, a deadImage.loadTextureoverload, the unreachableQwenHtmlOverlayDebugclass and one repeated failure log in each ofStyleAsyncHandlerandNetworkAsyncHandlerare gone; a number of per-eventINFOlines ([AUI Screen],[AUI Viewport],NetworkUtilchunking, Fabric network registration) moved toDEBUG.
### Fixed
- Buttons in the Ore and McUI themes no longer grow taller while pressed.
.button:activecarried hardcodedpadding-top: 9px; padding-bottom: 11px— correct for the base button's7px/13px, but.button-normal(8px/8px) and.button-small(5px/9px) have no fixed height, so pressing them added 4 px and 6 px of height respectively and pushed the surrounding content down. Both variants now keep their own vertical padding sum on:active, so only the 2 px "pressed" shift remains. - A grid could lay some of its items out against a different track list than the rest, leaving a card outside its container. Two independent causes, both fixed. First, a grid container with
width: autoderived its available track space from an ancestor width whenever its own used size was not cached yet — and it never was on the first measurement, becauseSize.ofshort-circuits on that very cache. Second,RenderElement.dependency(), the stamp that decides whether a committed rect is still valid, mixed in only thelayoutVersioncounters along the route and not the ancestors' used sizes, while the layout caches' own stamp (usedSizeDependency()) did include them. When an ancestor's width changed without anyone bumping a counter, the layout was recomputed but the committed rect was not.dependency()now mixes in ancestor sizes likeusedSizeDependency()does. - A stale
positioncache could survive its container's resize permanently.RenderElement.size's cache invalidates itself when its dependency goes stale, but it did not tell its children:positionhas no dependency stamp of its own and is only cleared explicitly, and a grid's track change does not travel the style-invalidation path.size.get()now drops its children'spositionand committed geometry when it finds itself stale. This is what made a mispositioned card stick until something forced a full relayout. - Switching
:checkedrecomputed the whole document.Element.setCheckedrequested a style recalc on the document element, so toggling any checkbox or radio re-ran style resolution over every element on the page. The affected set for a:checkedchange is the element's own subtree plus its following siblings' subtrees — the combinators (>,+,~, descendant) only look forwards and the selector engine has no:has— which is exactlysubtree(parent). The recalc is now scoped to that, andenforceRadioGroupCheckedrequests the same scope for each radio it unchecks, since those may live anywhere in the document and previously relied on the whole-document pass to refresh their own following siblings. - A grid's
gapwas rounded to whole pixels.Grid.Gapsheldints and parsed the values withSize.parse, which rounds, so a non-integer gap (1.84pxfrom a scrollbar inset,3.5pxfrom avar()or a shorthand expansion) was silently changed and the tracks no longer added up to the container's width. Gaps aredoubles parsed throughCssLengthnow. - A scroll container's scrollbar could appear one frame late.
ScrollModel.commitLayoutMetrics()both measures the scroll area and flips the scrollbar's visibility — a layout input, since the gutter comes out of the content box — but it only marked the layout dirty; whether that became an invalidation depended on where it was called from. The geometry-commit path calls it *after* committing geometry, so the invalidation landed on the next 20 Hz tick and the frame that had just been committed stayed laid out at the old width. The commit path now invalidates immediately (commitScrollMetricsAfterLayoutCommit).
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Changed
- The Resource Manager no longer has a world-window mode: it always opens as a GUI document. The
debug.resourceManagerWorldWindowoption, its DevTools toggle and the tick that switched an open instance between modes are gone. aui-viewport:modenow defaults tobrowserinstead ofgui. A page that omits the meta tag lays out against the browser-style CSS viewport and follows the window;mode=gui(aliasmc) opts back in. Pages that relied on the old implicit GUI viewport must now saymode=gui, and WorldWindow pages must declaremode=fixed, since the undeclared default is now the 1920-wide browser viewport.<iframe>: the WebView2 user data directory moved toapricity/.cache/webview, and the resource scan, static resource list and dev auto-reload now skip dot-prefixed directories. An existingapricity/webviewis orphaned: delete it, or move it toapricity/.cache/webviewto keep its cookies.
## Fixed
- WorldWindow frustum culling and hit testing were built without the camera view matrix, so a panel that was on screen could be culled away the moment the camera turned, and its pointer/crosshair mapping only lined up while the camera faced the world axes. The view matrix is now composed with the pose for both.
- Root
overflowis carried by the viewport instead of the root element's own box (CSS 2.1 §11.1.1), so a page using the ordinaryhtml,body{overflow:hidden}idiom paints again. With no in-flow content the root box is zero-height, which used to collapse the clip to an empty rectangle and cull every node: the whole page disappeared with no error at all. - Fixed the
GL_INVALID_OPERATION ("Depth formats do not match")flood when another mod blits depth out of the stencil-enabled main render target (KubeJS'sHighlightRendererwas the reported case). The destination's depth format is now matched at the blit call site, and the copy succeeds again. <iframe>: Enter did nothing in a single-line<input>, so a page's own search box could never be submitted from the keyboard; it now submits the enclosing form.
## Added
<iframe>— real web pages in game.** Rendered by the system WebView (WebView2 / Edge Runtime on Windows) in a hidden offscreen window and streamed back into a texture, so layout, clipping, transforms and hit testing treat it like any other texture-backed element. Absolutesrconly; pointer and keyboard input is forwarded. Windows x64 + WebView2 Runtime only.<iframe>capture(auto / lossless / fast / stream) andcapture-scalecontrols, plusIframe.status()diagnostics and a frame-timing HUD stream line.- CSS scrollbar styling:
scrollbar-width,scrollbar-color, and the::-webkit-scrollbar/-track/-thumb/-thumb:hover/-cornerpseudo-elements.
## Performance
<iframe>frames now travel as an incremental image stream (32x32 tile diff over shared memory); only changed regions are re-uploaded, and a static page publishes nothing.- Up to four captures in flight; the capture rate follows the element's draw rate; decoding moved off the input thread (
cmd=now 0-1 ms); pointer moves coalesced; raster capped at 1.2M pixels. - Faster item rendering, transform-only style changes no longer force a full relayout, and text measurement no longer allocates per lookup (7.45 -> 0.14 MB/frame).
## Fixed
- Links opened in a new window now navigate in the same iframe instead of spawning a desktop popup, and closing a page shuts down its web view.
clip-pathpolygon fill rules, concave polygons, andcircle()/ellipse()default radii.- WorldWindow visibility hysteresis band no longer renders at full cost.
- DevTools no longer answers to a hardcoded Ctrl+Shift+I; it is now only the rebindable "Toggle DevTools" keybind.
## Added
<iframe>— real web pages in game.** Rendered by the system WebView (WebView2 / Edge Runtime on Windows) in a hidden offscreen window and streamed back into a texture, so layout, clipping, transforms and hit testing treat it like any other texture-backed element. Absolutesrconly; pointer and keyboard input is forwarded. Windows x64 + WebView2 Runtime only.<iframe>capture(auto / lossless / fast / stream) andcapture-scalecontrols, plusIframe.status()diagnostics and a frame-timing HUD stream line.- CSS scrollbar styling:
scrollbar-width,scrollbar-color, and the::-webkit-scrollbar/-track/-thumb/-thumb:hover/-cornerpseudo-elements.
## Performance
<iframe>frames now travel as an incremental image stream (32x32 tile diff over shared memory); only changed regions are re-uploaded, and a static page publishes nothing.- Up to four captures in flight; the capture rate follows the element's draw rate; decoding moved off the input thread (
cmd=now 0-1 ms); pointer moves coalesced; raster capped at 1.2M pixels. - Faster item rendering, transform-only style changes no longer force a full relayout, and text measurement no longer allocates per lookup (7.45 -> 0.14 MB/frame).
## Fixed
- Links opened in a new window now navigate in the same iframe instead of spawning a desktop popup, and closing a page shuts down its web view.
clip-pathpolygon fill rules, concave polygons, andcircle()/ellipse()default radii.- WorldWindow visibility hysteresis band no longer renders at full cost.
- DevTools no longer answers to a hardcoded Ctrl+Shift+I; it is now only the rebindable "Toggle DevTools" keybind.
## Added
<iframe>— real web pages in game.** Rendered by the system WebView (WebView2 / Edge Runtime on Windows) in a hidden offscreen window and streamed back into a texture, so layout, clipping, transforms and hit testing treat it like any other texture-backed element. Absolutesrconly; pointer and keyboard input is forwarded. Windows x64 + WebView2 Runtime only.<iframe>capture(auto / lossless / fast / stream) andcapture-scalecontrols, plusIframe.status()diagnostics and a frame-timing HUD stream line.- CSS scrollbar styling:
scrollbar-width,scrollbar-color, and the::-webkit-scrollbar/-track/-thumb/-thumb:hover/-cornerpseudo-elements.
## Performance
<iframe>frames now travel as an incremental image stream (32x32 tile diff over shared memory); only changed regions are re-uploaded, and a static page publishes nothing.- Up to four captures in flight; the capture rate follows the element's draw rate; decoding moved off the input thread (
cmd=now 0-1 ms); pointer moves coalesced; raster capped at 1.2M pixels. - Faster item rendering, transform-only style changes no longer force a full relayout, and text measurement no longer allocates per lookup (7.45 -> 0.14 MB/frame).
## Fixed
- Links opened in a new window now navigate in the same iframe instead of spawning a desktop popup, and closing a page shuts down its web view.
clip-pathpolygon fill rules, concave polygons, andcircle()/ellipse()default radii.- WorldWindow visibility hysteresis band no longer renders at full cost.
- DevTools no longer answers to a hardcoded Ctrl+Shift+I; it is now only the rebindable "Toggle DevTools" keybind.
## Added
<iframe>— real web pages in game.** Rendered by the system WebView (WebView2 / Edge Runtime on Windows) in a hidden offscreen window and streamed back into a texture, so layout, clipping, transforms and hit testing treat it like any other texture-backed element. Absolutesrconly; pointer and keyboard input is forwarded. Windows x64 + WebView2 Runtime only.<iframe>capture(auto / lossless / fast / stream) andcapture-scalecontrols, plusIframe.status()diagnostics and a frame-timing HUD stream line.- CSS scrollbar styling:
scrollbar-width,scrollbar-color, and the::-webkit-scrollbar/-track/-thumb/-thumb:hover/-cornerpseudo-elements.
## Performance
<iframe>frames now travel as an incremental image stream (32x32 tile diff over shared memory); only changed regions are re-uploaded, and a static page publishes nothing.- Up to four captures in flight; the capture rate follows the element's draw rate; decoding moved off the input thread (
cmd=now 0-1 ms); pointer moves coalesced; raster capped at 1.2M pixels. - Faster item rendering, transform-only style changes no longer force a full relayout, and text measurement no longer allocates per lookup (7.45 -> 0.14 MB/frame).
## Fixed
- Links opened in a new window now navigate in the same iframe instead of spawning a desktop popup, and closing a page shuts down its web view.
clip-pathpolygon fill rules, concave polygons, andcircle()/ellipse()default radii.- WorldWindow visibility hysteresis band no longer renders at full cost.
- DevTools no longer answers to a hardcoded Ctrl+Shift+I; it is now only the rebindable "Toggle DevTools" keybind.
## Added
<iframe>— real web pages in game.** Rendered by the system WebView (WebView2 / Edge Runtime on Windows) in a hidden offscreen window and streamed back into a texture, so layout, clipping, transforms and hit testing treat it like any other texture-backed element. Absolutesrconly; pointer and keyboard input is forwarded. Windows x64 + WebView2 Runtime only.<iframe>capture(auto / lossless / fast / stream) andcapture-scalecontrols, plusIframe.status()diagnostics and a frame-timing HUD stream line.- CSS scrollbar styling:
scrollbar-width,scrollbar-color, and the::-webkit-scrollbar/-track/-thumb/-thumb:hover/-cornerpseudo-elements.
## Performance
<iframe>frames now travel as an incremental image stream (32x32 tile diff over shared memory); only changed regions are re-uploaded, and a static page publishes nothing.- Up to four captures in flight; the capture rate follows the element's draw rate; decoding moved off the input thread (
cmd=now 0-1 ms); pointer moves coalesced; raster capped at 1.2M pixels. - Faster item rendering, transform-only style changes no longer force a full relayout, and text measurement no longer allocates per lookup (7.45 -> 0.14 MB/frame).
## Fixed
- Links opened in a new window now navigate in the same iframe instead of spawning a desktop popup, and closing a page shuts down its web view.
clip-pathpolygon fill rules, concave polygons, andcircle()/ellipse()default radii.- WorldWindow visibility hysteresis band no longer renders at full cost.
- DevTools no longer answers to a hardcoded Ctrl+Shift+I; it is now only the rebindable "Toggle DevTools" keybind.
