Repository navigation
Releases: once-ui-system/core
Release list
v2.0.0
[2.0.0] — 2026-09-25
Once UI 2.0 is on latest:
npm i @once-ui-system/core2.0 is the release where Once UI becomes a system an AI can build with
correctly. One spec drives the docs, the ai/ harness, the validator and the
codemod, so what an agent reads is what the package accepts. Utilities and
responsive classes are generated, and breakpoints are fixed, so CSS and
JavaScript agree on every width and a responsive Flex can be a server
component.
Upgrading from 1.8.x. MIGRATING.md is the checklist, in the
order that keeps the app building: run the codemod, install the peers for the
subpaths you use, fix what the codemod reports, import LayoutProvider from
@once-ui-system/core/next in a Next app, check the browser floor (Chrome 111,
Safari 15.4, Firefox 113), drop custom breakpoints, and re-shape any
ScrollContainer. Most apps finish in the first two steps.
What is in it. 2.0.0 is 2.0.0-alpha.3 plus the entry below. The four
alpha entries that follow are the full record of what changed from 1.8.x and
why; read them together, oldest first. Two things are by design rather than
open questions:
- A bundler is required. Components import their own
.module.scssfiles,
so Once UI runs in apps built by a bundler that compiles Sass modules
(Next.js, Vite and the like), as it always has. Plain Noderequire/import
of the package is not supported. @once-ui-system/foundationsstays private. Core inlines its SCSS and CSS
at build time, so there is nothing extra to install.
Added
sizestakes theMediashorthand everywhere it reachesMedia.
<Media sizes={1200}>stands for(max-width: 1200px) 100vw, 1200px, but
Carousel(its slides andthumbnail.sizes),Swiper,OgCard,Bookand
MediaUploadtyped theirsizesasstringwhile passing it straight to
Media— so the number worked at runtime and failed to type-check. They take
string | numbernow, resolved byMediaas before. Theai/harness lists
the wider type, so an agent validating against the alpha.3 harness would
reject the shorthand on these five.
v2.0.0-alpha.0
The first preview of Once UI 2.0.
This is a pre-release. It goes to the alpha dist-tag, not latest. npm install @once-ui-system/core keeps resolving 1.8.4, and every ^1.8.x range in the wild is unaffected — a prerelease doesn't satisfy a stable range, not even ^2.0.0 or *. You have to ask for it by name:
npm i @once-ui-system/core@alphaIt's a preview of 2.0, not 2.0. The API here is what we intend to ship, but the point of an alpha is to find out where that's wrong — please report what breaks.
Migrating from 1.8.x
Five things, in the order that makes sense. MIGRATING.md walks each one.
- Run the codemod. It applies every prop rename below, component-scoped, and is a no-op on its own output — so it's safe to re-run.
curl -O https://raw.githubusercontent.com/once-ui-system/core/main/scripts/codemod-2.0.mjs node codemod-2.0.mjs src --dry # read the report first node codemod-2.0.mjs src - Move chart,
CodeBlockandMediaUploadimports to the/data,/codeand/mediasubpaths, and install the peer each one names. The codemod does this too. - Rewrite
ColorInput'sonChangebody by hand — the signature changed, and a rename can't express that. - Next apps: import
LayoutProviderfrom@once-ui-system/core/nextto keep 1.8.x link and image behaviour. One line. - Check the browser floor. Scheme tokens are
oklch()now.
Nothing else is required to build.
Breaking
- Prop API standardisation — a batch of renames, all codemoddable.
- Timing props are milliseconds, consistently.
fillmeans layout everywhere, andSkeletondrops its second size scale.- Charts,
CodeBlockandMediaUploadmove to their own subpaths (/data,/code,/media). IconNameis a union, notstring. A name that used to render a blank space is now a type error — that surfaces bugs an app already had rather than creating work.Textareagrows with its content by default.LayoutProviderno longer takesbreakpoints— the five steps are fixed.- Colour props that paint into SVG accept tokens again.
- Scheme tokens are
oklch(), which sets a browser floor: Chrome 111, Safari 15.4, Firefox 113.
Next.js is now optional
Core no longer imports next/* at runtime. ElementType (behind SmartLink, and any Button / Card / ToggleButton with href), Media, Logo, MegaMenu and Kbar render through an adapter layer whose defaults are plain DOM — <a>, <img>, window.location.assign.
Next apps keep 1.8.x behaviour with a one-line import change:
- import { LayoutProvider } from "@once-ui-system/core";
+ import { LayoutProvider } from "@once-ui-system/core/next";Verified by installing the packed tarball into a React app with no next in node_modules and server-rendering SmartLink, Button, Media, Row, Column, Text and Schema.
Also in this release
Added — ThemeSwitcher gains collapsed; focusRing on Input and Textarea; StylePanel is composable with host-owned state; ThemeProvider takes persistence; body text scales without touching headings; generateColorScheme and schemeAlphaVariants.
Fixed — the mega menu's dropdown shadow is no longer clipped; background="transparent" and solid="transparent" actually do something; ThemeSwitcher exposes which theme is active; a failed emoji fetch no longer fails the build or the publish; StylePanel no longer writes settings straight to localStorage; Carousel and Swiper stop blurring the edges of their own images; a larger icon registry.
Changed — spacing utilities are generated rather than hand-written; .align-between, .align-around and .align-even are gone; agent guidance (ai/rules.md, ai/recipes.md) gains rules on proportion and reveal-on-hover.
Full detail in the changelog.
Known and deliberate
- The published package is bundler-only.
distis ESM syntax in.jsfiles without"type": "module", so Next.js, Vite and friends load it and plain Noderequire/importdoes not. This predates 2.0; fixing the module format is a decision that hasn't been made yet. @once-ui-system/foundationsis not published. Core inlines its SCSS and CSS at build time, so you install nothing new.- Breakpoints are fixed. See MIGRATING.md §7.
Docs
The documentation site now describes 2.0: https://docs.once-ui.com
Still on 1.8.x? The 1.x docs are frozen at https://v1.docs.once-ui.com
v.1.8.5
A patch on the stable line. npm install @once-ui-system/core gets it; nothing to migrate.
Fixed
-
CountFxno longer freezes whenvaluereverts mid-animation. A run started from the last completed value, so changingvalueback while one was still in flight hit an early return after the frame had already been cancelled — the number stayed stuck on an arbitrary intermediate value. A monthly/annual pricing toggle clicked twice in quick succession was enough. Runs now start from whatever is on screen, so an interrupted transition reverses from where it got to and always settles exactly on the target. Reported and diagnosed by @chanderlud in #134. -
The published package now carries its license text.
package.jsonhas declared"license": "MIT"all along, but the grant itself never shipped — it lived only at the monorepo root, and npm includes a license only from the package directory. No tarball up to and including 1.8.4 had one.
Full changelog: CHANGELOG.md
v2.0.0-alpha.3
[2.0.0-alpha.3] — 2026-09-24
The fourth alpha preview, on the same alpha dist-tag. npm install @once-ui-system/core still resolves the 1.8 line; asking for this one stays
deliberate:
npm i @once-ui-system/core@alphaClassified as a pre-release of the 2.0 major. No prop, export or token is added,
removed or renamed; everything here is a fix, plus an internal import refactor
that leaves the public entry points as they were. Two fixes change rendered
output for code that already works around the old behaviour, which is worth
checking on upgrade: a titled BarChart no longer grows past its height (its
card is height tall, like the other charts, with the plot fitting under the
header), and a caller that padded a chart's height to make room for the
header will now get a taller plot rather than a clipped one.
The ai/ harness is regenerated for the version; no component's props changed.
Changed
- Core's own files import from the file that defines a symbol, never from a
barrel. 195 imports across 160 files went through a directoryindex.ts
(".","../components","../../contexts"…), so importing any one
component pulled in every module that barrel re-exports. In a Next.js app
that put all 130 client components on every page, the 150 KB emoji dataset
among them. The public barrels are unchanged, and
import { Button } from "@once-ui-system/core"works exactly as before. What
this unlocks is per-component imports
(@once-ui-system/core/components/Button), which now ship only what they
name: on the Once UI site they cut the JavaScript every page loads from
542 KB to 472 KB gzipped. A test fails if an internal barrel import comes
back.
Fixed
-
A chart's plot fits inside its card again.
LineChart,BarChart,
LineBarChartandPieChartusedheighttwice: as the card's height and
as amin-heighton the plot row under the header (added in 1.8.0). A chart
with a title therefore asked for header +heightinside a card that is
heighttall, and the plot hung past the card by the header's height —
measured at 45px with a title and 65px with a description — clipped by its
ownoverflow: hidden, with the axis labels and legend spilling onto
whatever came next.BarChartgrew instead, because its card used
minHeight.heightnow sizes the card once, on all four, and the plot
takes the height the header leaves. -
Numeric
paddingandmarginno longer vanish on the client.Flex,
Grid,TextandHeadingwrote a number as the CSS shorthand next to the
four longhands, leaving the unset longhandsundefined. The server skips
those, so server-rendered pages were fine. On the client React writes an
undefined longhand as''after the shorthand, which clears it:
<Column padding={1}>mounted in the browser hadstyle="", and a box that
went from four numeric sides to onepaddinglost all of it on the update.
Found in Aveiro's editor, where a block's spacing collapsed as soon as all
four sides matched. Numbers are now written as the four sides, resolved
side → axis → all, the same precedence as before. Tokens are unchanged:
they are classes, not inline styles. -
A clickable
Cardruns its handler once, and its label names the link.
onClickwas set on both the card's button and the surface inside it, so
a click on the content bubbled and ran the handler twice; a card that
toggled its ownselectedstate flipped and flipped back. The handler now
sits on the focusable element only (throughonLinkClickfor anhref
card).aria-label,aria-labelledbyandaria-describedbyalso went to
the inner surface, leaving the link or button named by whatever text the
card contained; they now land on the element that takes focus. -
FadingLettersFxno longer stalls while the page is busy. It animated
filter: blur(), which browsers cannot hand to the compositor (a changing
blur radius moves pixels), so the letters ran on the main thread and froze
mid-blur whenever script ran during their entrance: hydration, a chunk
loading, a WebGL context starting. The blur is now fixed, on two copies of
each letter drawn with::beforeand::afterfrom adata-letter
attribute, and onlyopacityandtransformanimate, so the whole effect
runs on the compositor. The look is the old curve, sampled: a heavy blur,
a light one, then the sharp letter, on the same timings and delays. Each
letter now wraps its character in an inner span; the copies are generated
content, so the text is still in the DOM once. -
A collapsed
ThemeSwitcherno longer loads open and then snaps shut.
The server cannot know the visitor's theme, so the group used to render
with every option showing and collapse on hydration. It now renders at its
one-option size, invisible, and appears once the active option is in place,
with the swap unanimated. Touch devices, which always show every option,
are unchanged. -
ThemeSwitcher direction="column"reserves the space it draws. The
anchor was a hard-coded 40px square, but the options are 32pxIconButtons
in a 1px border: 34px. The extra 6px pushed the control off-centre in its
row. The anchor, and the caps the options open and close to, now follow
the button size. -
MegaMenuworks from the keyboard. A group with a dropdown and no
hrefrendered its trigger as adiv, so it was not in the tab order at
all, and every panel opened on hover only. Those triggers are now buttons
witharia-expandedandaria-controlsthat toggle on Enter or Space; a
trigger that is also a link opens its panel on keyboard focus. Tab moves
from an open trigger into its panel and on to the next trigger, Shift+Tab
comes back, ArrowDown opens a panel and the arrows move within it, and
Escape closes it and returns focus to the trigger. Pointer behaviour is
unchanged.
v2.0.0-alpha.2
[2.0.0-alpha.2] — 2026-09-23
The third alpha preview, on the same alpha dist-tag. npm install @once-ui-system/core still resolves the 1.8 line; asking for this one stays
deliberate:
npm i @once-ui-system/core@alphaClassified as a pre-release of the 2.0 major. The ScrollContainer tile
defaults are removed rather than deprecated, which is a major-criteria change
("removing or renaming components, props, tokens, or CSS classes", and
"changing default behavior in ways that alter rendered output for existing
code") — it rides the 2.0 line rather than a minor, and existing carousels need
their shape passed in explicitly. Everything else here is additive or a fix.
Per the AI-consumer rule, ai/manifest.json, ai/catalog.json and
ai/spec.json are regenerated in this release: ScrollContainer gained six
props since alpha.1, so an agent validating against the previous harness would
have rejected all of them.
Added
-
ScrollContainermoves a track instead of scrolling a box. A scroll box
cannot do any of the three things asked of a carousel: its contents are
clipped at its own edge by definition, so no tile can peek past it; its
scroll range has two hard ends, so there is nowhere for a wrap-around to go;
and the browser owns the easing, so a drag can only setscrollLeftand
hope. The track is now laid out once and moved with a transform, which
removes all three limits at once and makes the motion a CSS transition the
component controls.New with it:
infinitecontinues past the last item into the first in the
same direction, with no rewind and both controls always enabled;markers
(true,"top"or"bottom") shows how many items there are and which is
in front, and jumps to one when pressed;draggable(on by default) has the
track follow a mouse, pen or finger, claiming a gesture for one axis on its
first movement so a vertical swipe still scrolls the page, and swallowing the
click at the end of a drag so it does not also open the tile;stepsets how
many items a control press moves; andclipkeeps everything inside the
component's own box for callers who want the old edges back.This changes existing carousels. Tiles used to arrive with
aspectRatio="3/4",border,radius="xl"and a width range already set,
so anything that was not a tall bordered portrait card had to override four
properties before it could begin. A tile now contributes only what a track
item cannot do without — refusing to shrink, and aminWidthfloor so a
carousel of fill-width content has something to fill. Shape and size come
from the caller, through the same rest props that always landed on the tile.
Because the browser is no longer doing the scrolling, a trackpad's horizontal
gesture no longer moves the track, which is why drag is on by default and the
controls are never hidden. -
ThemeSwitcher collapsedcan open downwards, withdirection="column".
The collapsed group grew sideways, in the flow of the layout, which works
where it has room beside it and not where it has neighbours: the group
widening pushes them along, and the option the pointer is resting on slides
out from under it. In a header that is every time it opens.direction="column"splits the control into an anchor and a panel. The
anchor is what the layout sees and stays the size of one option however many
are showing; the panel is taken out of the flow and hung from the anchor, so
it grows over the page instead of through the header. Measured in a 56px
sticky header with neighbours on both sides: nothing beside it moves and the
header does not change height.It opens downwards rather than centring on the active option. An open panel
is 124px against a 56px header, so anchoring it on the second or third option
would put its top edge 34px and 76px above the viewport.The active option leads. In fixed order the panel's first slot belongs to
whichever theme is listed first, so the icon already under the pointer
changes identity as the panel unrolls — hover the moon and the computer
arrives under your cursor, and a click without looking sets the wrong theme.
Leading with the active option keeps the invariant that the theme in force is
the one in the anchor slot, before opening and after choosing. The reorder is
done in the markup rather than with CSSorder, so tab order and visual
order stay the same thing, and stable keys mean a focused button keeps focus
across the move.directiondefaults to"row"and is ignored when the group is not
collapsed, so nothing that already usesThemeSwitcherchanges. Devices that
cannot hover still get the group always open, and there it takes its space in
the layout rather than hanging over what is underneath. -
ScrollContainertakesactiveandonActiveChange.activebrings an
item to the front by its index, by the shorter way round wheninfinite;
onActiveChangereports the item that arrived there, however it got there.
Together they let something outside the component stand in for the markers —
a row of names, a set of thumbnails, a table of contents — which previously
was not possible at all: the index was internal state, and the forwarded ref
was never attached to anything, so there was no handle on the run from
outside.activeis a command rather than a lock, and the effect that reads it is
keyed on the prop alone. Were it to depend on where the run actually is,
every drag would be undone a frame after it ended by an effect insisting on
the last value the caller passed, and the run would fight the pointer. An
activethat never changes therefore does not pin the run in place;
onActiveChangeis how a caller stays level with it. -
ScrollContainertakesproximity. Tiles scale by their distance from
the one in front, so the run has a focus that moves with it. It is continuous
rather than stepped — the scale is computed from the live drag offset divided
by the tile pitch, so halfway through a drag the outgoing and incoming tiles
are the same size and the emphasis crosses between them as the pointer moves,
instead of snapping when the track settles.trueis a gentle default; a
number is how much smaller each whole step away is, as a fraction. Tiles stop
shrinking at 72% so a long run does not trail off into nothing. Off — the
default — a tile carries no transform at all, notscale(1), so it never
becomes a containing block for anything the caller positioned inside it. -
Form, a layout for a set of fields.densitycarries the gap and the
grouping as one decision, because they are one decision:"stacked"fuses
the fields into a single control, with the borders of neighbours collapsed
onto one hairline and only the outside of the group left round, while
"tight"and"spacious"are ordinary gaps that leave every field its own
border and radius.columnslays the fields out in a grid and takes a
per-breakpoint map; a field can occupy more than one column withspan;
sizesets the size of every field in the group, and a field that sets its
own still wins.The corners are the point. Passing
corners="top" | "none" | "bottom"down
a stack is positional bookkeeping that breaks the moment a field is
conditionally rendered or reordered — hide the third of four and the group
ends on a square edge.Formderives them from the children that actually
rendered and from where each one lands in the grid, so a ragged last row, a
spanning field and a column count that changes with the viewport all come
out right. A focused field is lifted above its neighbours, and nothing in
the component clips, so the focus ring stays whole.InputandTextareagained aspanprop, read byFormand stripped
before it reaches the DOM, and mark their bordered box with
data-surface="field"soFormcan reach the element that actually carries
the radius. Children that do not mark a surface are laid out and spaced but
keep their own corners. -
Docs pages say whether you can install what they describe. A
status
frontmatter field —"alpha"or"unreleased", absent meaning available on
latest— puts a tag in the sidebar and a notice under the page title.
VersionBanneralready said the site documents 2.0, but it says one thing to
every page, and it cannot tell "shipped in the alpha" from "not published
anywhere yet": it told both to install@alpha, which is wrong for the
second. It is also above the sidebar, far from where someone arriving from a
search result starts reading.Deliberately a separate axis from
navTag. That one says when something
changed and decays on a timer — grey at 30 days, gone at 60 — while
availability stops being true only when a release makes it false, and a page
can be both new and unreleased at once. Where both apply the sidebar shows
availability, because in a column that narrow it is the more useful of the
two.Applied to the six pages documenting components that do not exist in 1.8.5:
Book,Effect,InfoTip,NavItem/NavGroup,ScrubberandSetting,
verified against the published dist-tags rather than assumed. The media
players page is left alone on purpose — its video half shipped in 1.8.5 and
only the audio player is new, so the page as a whole is not alpha-only. -
Feedbacktakes anicon. It was locked to the variant's glyph, with
showIcononly able to turn it off.iconaccepts anyIconName, so a
custom name registered throughIconLibraryOverridesworks here too; leaving
it out keeps the variant's icon. -
**
Placement,SideandAlignmentare exported from the pac...
v2.0.0-alpha.1
[2.0.0-alpha.1] — 2026-09-17
The second alpha preview, on the same alpha dist-tag. npm install @once-ui-system/core still resolves the 1.8 line; asking for this one stays
deliberate:
npm i @once-ui-system/core@alphaMostly regressions the first alpha shipped and the first alpha found — which
is what an alpha is for.
Fixed
CountFxno longer freezes whenvaluereverts mid-animation. A run
started from the last completed value, which was recorded only when an
animation reached its end. Changingvalueback while one was still in
flight therefore hit thevalue === previousValueRef.currentearly return
after the effect cleanup had already cancelled the frame: nothing was left
driving the number, and the display stayed on whatever intermediate value it
happened to be showing until some third value came along. A monthly/annual
pricing toggle clicked twice in quick succession was enough to trigger it.
Runs now start from whatever is on screen rather than from a completion
guarded ref, so an interrupted transition reverses from where it got to and
always settles exactly on the target. Thesmootheffect renders its digit
wheels from the same origin, which now tracks the run in flight instead of
the last one that finished. Reported and diagnosed by @chanderlud in
#134.DatePickerandCarouselrendered invisible, but interactive, for four
minutes. The 2.0 pass that movedRevealFx.delayto milliseconds also
multiplied the twospeedvalues in core by a thousand — andspeedwas
already milliseconds, so the calendar's reveal became a 250-second opacity
transition and the carousel slide's a 300-second one. The buttons were
mounted, focusable and clickable underneath; you just could not see them.
Both are back to 250 and 300, and a test fails on any core component that
asksRevealFxfor a reveal slower than five seconds.Table's page-size select was a 2rem sliver,User,UserMenu,OgCard
andAvatarskeletons filled their container. 2.0 renamedheightto
sizeon the field components and replacedSkeleton's ownwidth/
heightscale withsize, and the codemod applied that to consumer trees —
but core's own call sites were never run through it.height="xs"on the
paginationSelectand the table search,height="m"on theUser
skeleton and four onOgCard's all landed on the Flex layout prop of the
same name, wherexsandmare spacing tokens;Avatar's loading circle
passedwidth/heighttokens instead ofsize. All are on the 2.0 names,
the page-size select gets 5rem to show its value, and a test scans every
component for the 1.8 spellings.OgCardshowed a loading skeleton over data it was handed. With both
urlandogDataset, the fetch for the URL still ran andloadingstayed
true until it finished, so the mock-data card drew skeletons over content it
could already render. Provided data is never fetched for now, and never
loading.- The time picker's header showed
13:14 PM. It printed the 24-hour value
and then appended the meridiem; it prints01:14 PMnow. - The published package now carries its license text.
package.jsonhas
declared"license": "MIT"all along, which is what license scanners read,
but the grant itself never shipped: the MIT file lived only at the monorepo
root, and npm includes a license only when it finds one in the package
directory. No tarball up to and including 2.0.0-alpha.0 carried one. The
notice also now names Dopler, the entity, rather than Once UI, the product.
Fixed (docs site, not published code)
- Markdown tables rendered as raw pipes. The docs pipeline had no GFM, so
the harness table on the AI coding page and the clamp table on the Input page
printed their source.remark-gfmis in the pipeline now and a markdown
table renders through the design system'sTable, like every other table in
the docs. Two harness links pointed at the wrong files on the way. - The
IconButtoncustom-content example lost its label on the primary
button. TheTextinside forcedonSolid="neutral-medium", a neutral
on-solid token over a brand solid, which is near-white on the docs' contrast
solids. A button already sets the text colour for its own surface, so the
example inherits it. - The "Copy for LLM" menu's three options carry prefix icons (document,
OpenAI, Claude). The two brand marks come fromreact-icons, registered in
the docs app's own icon library as the harness rules prescribe. - The
Optionprefix example rendered nothing: it passedicontoIcon,
whose prop isname.
Changed
-
Agent guidance: a prop another prop already implies is a default too. The
harness told agents to omitposition="relative"and then shipped examples
writingfillWidthbesidemaxWidth(which already fills the width),
minWidth={0}besidefillWidth(which already setsmin-width: 0), and
border="neutral-alpha-weak"where bareborderdraws the default border —
the surface recipe itself was written that way, in the rules, the compact
rules, a task bundle,AGENTS.mdand 15 example files. All of them now use
the short forms, and the rules gain the principle behind them plus one on
zIndex: it belongs to an isolated stacking group (a decorative layer and
its content sibling, a sticky header over its pane), not to a lone element
or a page-wide ladder.validate-ai-codeenforces the three mechanically, per element rather than
per file, with--fixfor each; it also flags azIndexin a file with
nothing positioned absolute, fixed or sticky. Tag scanning is brace-aware
now, so a>inside an arrow function in a prop no longer ends the tag
early. Covered by tests.
v.1.7.0
Once UI 1.7: Form with Intent
Dependencies
Recharts, PrismJS, CompressorJS and Sharp are now optional dependencies. Please install them according to your needs when migrating to the 1.7 version.
New components
- ScrollContainer: New media display component
- SplitView: New adjustable layout component
- Modal: New fullscreen modal component
- BlobFx: New fluid blob effect
- FadingLettersFx: New fading letters text effect
Component improvements
- Media: Video controls and sizes shorthand
- Timeline: Improved layout and spacing
- Button & IconButton & ToggleButton: New XS and XL sizes
- Input & Textarea: New XS, L, and XL sizes
- Input & Textarea: New Ghost variant
- Flex & Grid: Removed unnecessary class duplication
- Textarea: Fixed "jumping" issue when using auto height
Accessibility improvements
- Button & IconButton & ToggleButton: remove hover effect on touch devices
Once UI 1.7 marks a milestone: our first truly community-driven release. From bug reports to feature contributions, the people using Once UI every day helped shape what it became.
Thank you to everyone who contributed code, filed issues, or shared feedback. This release is better because of you.
shaayar
JExcellence
chanderlud
divyanshudhruv
hitomihiumi
bugsNburgers
v1.6.0
Once UI 1.6
Feature
Flex + Grid
- width, minWidth, maxWidth, height, minHeight, maxHeight props support %, vh, dvh, vw and calc values and apply them as inline style
- translateX and translateY prop (supports number as REM, string as SpacingToken, %, vh, dvh, vw and calc)
- paddings, margins and gaps support numbers as REM
- boolean border shorthand prop (border, borderLeft ... borderX ...): adds 1px solid default border with a new CSS class and CSS variable (aliasing neutral-medium)
ScrollLock
- new utility component
ThemeInit
- new utility component to centralize FAUC logic
Heading, Text
- family prop: override default font family of a variant
Fix, Refact
Flex + Grid
- breakpoint object cascading with inline styles (larger breakpoint styles will cascade to smaller if not set)
- zIndex and borderWidth props accept string
DropdownWrapper, Dialog, ContextMenu
- use new ScrollLock component
Dialog
- fix issue when sometimes Dialog opened as inert, breaking interactivity
UI
Textarea
- styled scrollbar
LineChart
- remove left legend padding when using ReverseY prop
v1.5.3
Once UI 1.5.3
Features
- Bulge + WeatherFx custom triggers
- Zoom cursor on Media enlarge and improved image rendering
- MegaMenu transition refined
- Chart axis reverse positions
- ShineFx inverse prop for inverting effect
- "sharp" shape added to StylePanel
- custom icon in Avatar
New
- CelebrationFx: for confetti and fireworks
- RadialGauge and LinearGauge components for meters
Fixes
- add default minHeight to charts instead of height
v1.5.0
Once UI 1.5 — Curiosity in Code
Accessibility Improvements
DropdownWrapper
- Scroll lock when open
- Better hover + arrow key handling when using simultaneously
EmojiPicker
- Refactored scroll behavior
- Improved performance
Component Improvements
Flex
- Custom breakpoint support (now a client component applying props and styles accordingly)
- Hidden elements are no longer removed from the DOM — only classes are applied
Scrollbar
- Allow default scrollbar
- Improved minimal scrollbar UI
Radius
"full"is now supported for individual border radius (e.g.topLeftRadius="full")
Button
- New shorthand prop
roundedusingdata-border
Background
- Improved line rendering for better visual quality
Card
- More accurate border radius spreading for cleaner tab selection UI
- Removed hover effect on tap for more natural responsive design
Input / Textarea
- Native character count support for
maxLength
MegaMenu
- Improved animations
- Support for custom content via
ReactNode - Reduced re-rendering
Icon / IconButton / HeadingLink
- Refactored with new
Animationcomponent - Added fade-in animation for
Tooltip
HeadingNav
- Added default heading
Heading / Text
- Adjusted line-height for display sizes
- Separate multiplier variables for display
- Added support for
XSandXLlabel sizes
TiltFx
- Added distortion amount support
UserMenu
- Disabled click when loading
New Components
Hover
A minimal component that supports overlaying elements on hover.
Animation
A robust component that can be used to craft more complex interactions.
HoverCard
A portal-based element built on top of Animation for simple hover interactions.
Timeline
A static timeline component that supports various data elements.
Swiper
A new multi-image component that supports mobile gestures and dragging.
TypeFx
A typewriter-style component that supports an array of words.
MatrixFx
A canvas-based animation for dot patterns with style adjustments.
ShineFx
A shining text effect, recently used for AI "Thinking" states.