Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .tegami/2026-09-25-flushsync.md

This file was deleted.

17 changes: 0 additions & 17 deletions .tegami/2026-09-25-quickjs-0-17.md

This file was deleted.

12 changes: 0 additions & 12 deletions .tegami/2026-09-25-unmount-quadratic.md

This file was deleted.

33 changes: 0 additions & 33 deletions .tegami/2026-09-26-element-pooling.md

This file was deleted.

17 changes: 0 additions & 17 deletions .tegami/2026-09-26-json-commands.md

This file was deleted.

15 changes: 0 additions & 15 deletions .tegami/2026-09-26-ordinal-css-lookups.md

This file was deleted.

34 changes: 3 additions & 31 deletions .tegami/publish-lock.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,6 @@
core:changelogs:
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: minor\n upm:com.reactunity.core:\n type: minor\n---\n\n### Logical borders and sizing\n\nThe painted border properties have their logical spellings — `border-inline-color`,\n`border-start-start-radius` and the rest — so `direction: rtl` turns a coloured edge or a rounded corner\naround, and Tailwind's `border-s-*` and `rounded-s-*` do something. `inline-size`, `block-size` and their\n`min-`/`max-` forms are aliases for the width and height properties.\n"
filename: 2026-09-12-logical-borders-and-sizing.md
v: 0.0.0
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: minor\n upm:com.reactunity.core:\n type: minor\n---\n\n### `scroll-padding`, `scroll-margin` and `scroll-snap-stop`\n\nThe three scroll-snap properties that were missing are in: `scroll-padding` insets the box a snap target\nlines up against, `scroll-margin` outsets the target itself, and `scroll-snap-stop: always` keeps a fling\nfrom carrying past a target on its way to one further along.\n"
filename: 2026-09-12-scroll-padding-margin-stop.md
v: 0.0.0
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: minor\n upm:com.reactunity.core:\n type: minor\n---\n\n### `background-clip`\n\n`background-clip` takes one value per background layer, and all four of CSS's values now do what they say.\n`text` cuts a layer down to the element's own glyphs — a gradient or an image painted through the text,\nwhich with `color: transparent` is the gradient-text effect. The glyphs are rasterised into a coverage\ntexture by a command buffer and only when they move, so an idle element costs nothing.\n\nThe three boxes used to be one box. Every background was masked at the padding edge, so a background never\nreached under its own border: a dashed, dotted or semi-transparent border showed the page through its gaps\nrather than the element, and a rounded one took the border's inner radius instead of its own. The mask is the border box now, which is both\nwhat `border-box` means and what CSS makes the default; `padding-box` and `content-box` are cut per layer\nin the fragment shader, with the radii that box has left, and cost nothing when there is no border or\npadding to separate them.\n\nMoving that mask corrected an inset `box-shadow` with it. One is cast by the padding edge, which is where\nit is now drawn and rounded — it used to be cast by the border box and then trimmed back to the padding\nbox by the mask, so on a bordered element it sat a border-width out from where CSS puts it.\n"
filename: 2026-09-13-background-clip.md
v: 0.0.0
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: patch\n upm:com.reactunity.core:\n type: patch\n---\n\n### `calc()` keeps a percentage\n\nA `calc()` that works out to a percentage is one now, on every property Yoga can hold a percentage for:\n`width: calc(1/2 * 100%)` is `50%`, and so `w-1/2`, `left-1/2`, `-translate-x-1/2` and `basis-1/3` — which\nis how a CSS framework spells every fraction it has — lay out instead of dropping. The percentage is\ncarried through the arithmetic rather than resolved on sight, which is what used to throw the answer away:\nthe parent it was resolved against had not been laid out yet, so the declaration came out as nothing at\nall.\n\nIt survives a sub-expression too, parenthesised or a nested `calc()` — `calc(calc(1 / 2 * 100%) * -1)`,\nwhich is what a framework emits for every *negative* fraction utility. A group is worked out before the\ncalculation around it, and the length or percentage it came out as is an operand again exactly as it\nstands, rather than something the arithmetic around it could not read.\n\nOn a property Yoga does not hold as a percentage -- `border-left-width`, `scroll-margin-left` -- a\ncalculated percentage now resolves to the same number the percentage written out gets, rather than\nreaching the property as a value of the wrong type.\n\nA calculation mixing a percentage with a length, `calc(100% - 2rem)`, is still dropped. Yoga has one unit\nper value and no calc of its own, so there is nothing to hand it.\n\n`infinity` is a number as CSS Values 4 asks — the largest length the implementation supports, not a real\ninfinity. `border-radius: calc(infinity * 1px)`, which is what a `rounded-full` utility compiles to, used\nto come out of the corner reduction as NaN and leave the element painting nothing.\n\nUnder `REACT_UNITY_DEVELOPER`, a declaration that resolves to nothing now says so once in the console,\nnaming the property and the value. A dropped declaration was silent before, which is the part of this that\ncost the most to find.\n"
filename: 2026-09-13-calc-percentage.md
v: 0.0.0
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: minor\n upm:com.reactunity.core:\n type: minor\n---\n\n### `overscroll-behavior`\n\n**Scrolling now chains.** A `<scroll>` that has reached its end hands the rest of the gesture to the\nnearest scroll box above it, as a page does on the web. Until now the innermost box swallowed every\ntick and every drag it was given, so a short list inside a long page was a dead spot: reaching its\nend stopped the scroll rather than carrying on with the page behind it.\n\n`overscroll-behavior` is how a box keeps a scroll to itself instead. `contain` stops the handover,\n`none` stops it and drops the local overscroll effect with it -- which here is `<scroll>`'s\n`elasticity` prop, so `none` on an elastic box takes its bounce away where `contain` leaves it. Both\naxes are separate: the shorthand takes one value or two with `x` first, `overscroll-behavior-x` and\n`-y` set them individually, and `overscroll-behavior-inline` and `-block` are the same two under\ntheir logical names. The axis a gesture runs along is the one that decides, so a contained `y` does\nnot trap a sideways scroll.\n\nA gesture stays with whichever box took it, rather than being handed over the moment that one\nreaches its own end: dragging a list down to its end and on past it does not then start dragging the\npage. That is the latching a browser does, and it is measured from where the drag began, so a slow\none is not mistaken for a stuck one.\n\nChaining walks the element tree rather than the transform hierarchy, so a `filter` or a\n`perspective` in between -- both of which reparent their subtree onto an offscreen canvas -- does\nnot break it.\n"
filename: 2026-09-13-overscroll-behavior.md
v: 0.0.0
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: minor\n upm:com.reactunity.core:\n type: minor\n---\n\n### `perspective`, `perspective-origin` and `backface-visibility`\n\n3D transforms have always composed here — `rotateX` and `rotateY` set a real rotation, and children keep\ntheir own depth the way `transform-style: preserve-3d` describes — but nothing projected them, so a\nrotation only ever foreshortened linearly and never converged anywhere.\n\n`perspective` on a parent now does. Its subtree is captured through a camera of its own, the same offscreen\nmachinery `filter` uses, with an off-axis frustum standing the viewer wherever `perspective-origin` puts\nthem; the capture grows to hold whatever the projection throws outside the element's box, so a card leaning\nout of its stage is not clipped. `backface-visibility: hidden` is separate and needs no perspective: it\nwatches the element's winding — which catches a mirror as well as a rotation, as CSS does — and takes the\nelement out of both drawing and hit testing while its back is turned.\n\nThere is still no `transform-style`. Its `preserve-3d` is what everything already does, and its default\n`flat` would need a shear the transform model has nowhere to put.\n\nA `transform` whose functions run together with no space between them -- `rotateX(35deg)rotateY(-30deg)`,\nwhich is what every CSS minifier emits -- now parses. It was being read as one token naming no function,\nso a built stylesheet silently lost the whole declaration while the same rule worked in source.\n\n**`translate-z` has changed sign.** A positive value now comes *towards* the viewer, as CSS says and as\n`rotateX`/`rotateY` already did. Nothing could show the difference under an orthographic screen-space\ncanvas, which is why it went unnoticed; a `perspective` shows it immediately. Code that leaned on the old\ndirection — most likely ordering elements in a world-space canvas — wants the opposite sign now.\n"
filename: 2026-09-13-perspective-and-backface.md
v: 0.0.0
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: patch\n---\n\n### Logical border radii survive the Vite build\n\nThe preset names an old CSS target so that Lightning CSS lowers what ReactUnity's CSS subset has no\nparser for. `border-start-start-radius` and its three siblings were caught by that too, and their\nlowered form is a pair of rules selected by `:-webkit-any(:lang(ae),:lang(ar),...)` naming every\nright-to-left language -- pseudo-classes ReactUnity reads as custom states, which never match, so the\nradius was dropped and the console carried a warning for each. Lowering is excluded for the logical\nproperties now, the way it already was for `light-dark()`: both are things the renderer resolves\nitself, and against the element's own direction rather than the document's language.\n"
filename: 2026-09-14-lightningcss-logical-properties.md
v: 0.0.0
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: minor\n upm:com.reactunity.core:\n type: minor\n---\n\n### Relative color syntax\n\nEvery color function takes a `from <color>` origin, which is CSS Color 5's relative color syntax:\n`rgb()`, `hsl()`, `hsv()`, `lab()`, `lch()`, `oklab()` and `oklch()`. The origin is converted into the\nfunction's own space and its channels become keywords the channel slots can use, on their own or inside\n`calc()` — so `rgb(from var(--brand) r g b / 50%)` is a brand color at half opacity without repeating it,\nand `oklch(from currentcolor calc(l * 0.8) c h)` is a hover shade of whatever the element's own color\nturns out to be. A keyword may appear in any slot, so `rgb(from red b g r)` is blue.\n\nEach keyword carries the range CSS gives it — `r` is `0`–`255`, `hsl()`'s `s` is `0`–`100`, `oklch()`'s\n`l` is `0`–`1`, a hue is degrees, `alpha` is `0`–`1` — so a literal mixes with one exactly as it would in\na browser. An omitted alpha is the origin's own rather than `1`, so `rgb(from <color> r g b)` is that\ncolor unchanged.\n\nThe origin stays lazy, so it may be a `var()`, `currentColor`, a `color-mix()` or another relative color,\nand one parsed rule resolves against each element it lands on. A literal origin is decomposed while\nparsing instead, so the whole function still folds to a constant.\n\nTwo things the color functions have always got wrong were in the way, and are fixed:\n\n`hsl()` and `hsv()` read saturation and lightness on CSS's `0`–`100` scale rather than `0`–`1`, so the\n`%` may be left off as every framework that emits `hsl(152 37 59)` expects. Written with percentages\nthey are unchanged; written as bare fractions — `hsl(152, 0.37, 0.59)`, which was the only spelling that\nused to work — they now mean what CSS says they mean, which is very nearly black.\n\nOut-of-range components are clamped rather than carried through the conversion, which is what CSS\nColor 4 asks of a specified color and what this arithmetic reaches constantly: `rgb(300 0 0)` is red,\n`hsl(0 150 50)` is a saturation of `100%`, and `calc(l * 4)` is a lightness of `1` rather than a color\nthat paints nothing. A saturation past `100%` used to come out of the conversion as a different hue\nentirely. Alpha is clamped everywhere, hue wraps, and a channel written as `none` is `0` in `rgb()`,\n`hsl()` and `hsv()` as it already was in the Lab family.\n"
filename: 2026-09-14-relative-color-syntax.md
- content: "---\npackages:\n npm:@reactunity/renderer:\n type: patch\n---\n\n### `flushSync` works again\n\n`flushSync` from `@reactunity/renderer` threw `TypeError: not a function` on every call. It called\nthe reconciler's `flushSync`, which react-reconciler 0.33 renamed to `flushSyncFromReconciler`, and\nthe reconciler's typings still declare the old name, so nothing caught it. It now commits the\nupdates its callback schedules before it returns, including on roots rendered with\n`disableBatchRendering`.\n"
filename: 2026-09-25-flushsync.md
v: 0.0.0
core:packages:
- id: npm:@reactunity/monorepo
Expand All @@ -31,14 +10,7 @@ core:packages:
- id: npm:@reactunity/material
updated: true
- changelogIds:
- 2026-09-12-logical-borders-and-sizing.md
- 2026-09-12-scroll-padding-margin-stop.md
- 2026-09-13-background-clip.md
- 2026-09-13-calc-percentage.md
- 2026-09-13-overscroll-behavior.md
- 2026-09-13-perspective-and-backface.md
- 2026-09-14-lightningcss-logical-properties.md
- 2026-09-14-relative-color-syntax.md
- 2026-09-25-flushsync.md
id: npm:@reactunity/renderer
updated: true
- id: npm:@reactunity/scripts
Expand Down
Loading