Changelog
What changed in each release of the Halo and Pro kits, the shared core, the CLI and the custom elements. All five packages are versioned together.
0.6.0
Minor Changes
- #42
caae958Thanks @robintrepte! - Forms, media, maps and fonts.
TextField:type="password"gets a show/hide button (reveal={false}turns it off), an interactiveactionslot that stays next to error marks,onKeyDown.Checkbox: a 24px hit area around the same box (WCAG 2.2 target size).SearchField: forwardsid,name,defaultValue,onKeyDown,onFocus,onBlur,autoFocusandinputProps(combobox ARIA);bindShortcutfocuses it with ⌘/Ctrl + the hint's key; the hint is hidden on touch screens.FileDrop:disabled,name(the listed files take part in a form),onFilescan returnfalseor the files to list.OtpInput: empties its cells whenerrorturns on (clearOnError),ref.current.reset().- New
QrCode(encoded on the device, no service);TwoFactorSetupdraws it fromqrValueand offerstrustDevice. TilePicker layout="list".CookieConsent:equalChoice,id,aria-describedbyon its text, category texts may hold links.Gallery: controlledindex/onIndexChange,label, arrow keys on the stage and the thumbnails, focus moves into the lightbox and back.BeforeAfter: the page scrolls vertically over it on touch screens, drags end on pointer cancel.CountUp: renders its final value on the server and counts up on the client;animate={false}.PlanCard:pricetakes any node,pricePrefix,priceNote,href/linkAsfor the action, a "Current plan" badge.FloatingLayout:controlsandattributionin the free corner,startWidth/endWidth,--floating-safe-*variables with the space its slots take, theendslot as a bottom sheet below 720px (endSheet).- Tokens
ramp-1…ramp-5in both kits: a calm sequential ramp for maps and heatmaps. - Static WOFF fonts under
fonts/og/for share images (next/og) and PDF tools.
- #42
caae958Thanks @robintrepte! - Fluid layouts and root props.
- Every component now puts
classNameandstyleon its root element (about 160 ignored one or both); a test renders every reference preview and checks it. - Nothing pushes past its container: segmented controls, tabs and toggle groups shrink with ellipsis labels (
ToggleGroup blockis new);MobileTabBarshares its width evenly; theTopBarnav switches to icons as soon as its labels no longer fit, the account button cuts name and address with an ellipsis (the full text intitle) and the search field shrinks;CheckoutStepsstay on one row and show numbers only when narrow (compactforces it); anAlert's actions move under the text when narrow; chat source chips grow to two lines and take adetailtooltip;RangeSliderfields fit narrow columns. TopBarandAppShelltake a node asbrand(a logo mark with the name).SideRail:expanded/defaultExpanded/onExpandedChangeshow names next to the icons,toggleadds the button, every button has a real tooltip while collapsed.Breadcrumb(andPageHeader crumbs): items withhref(withlinkAs) oronClick,onNavigate,label; plain labels are no longer fake#links; 24px targets.Pagination: link mode (hrefper page,linkAs),label, previous/next disabled at the ends.ListRow:href/linkAs,actionsoutside the clickable area (no nested buttons), a non-interactive row withoutonClick/href,titleLines/subtitleLines.SelectMenu size="sm";Table stackshows rows as labelled cards when the table is narrow.
- #41
ce11eddThanks @robintrepte! - Sidebar overhaul and an app shell with a sidebar.
Sidebar:variant(floating,attached,shell); links withhref,linkAsandexternal;badge("dot"or a word),disabled, itemactions("…" menu); collapsible groups with anaction; tooltips and flyouts when collapsed;storageKeyandshortcut(⌘B / Ctrl+B); an off-canvas drawer on phones (mobileOpen,onMobileOpenChange);header/footeras a function of{ collapsed }.- Fixed: more room between a group heading and a selected first item; counts no longer jump to the corner in narrow shells.
- Filters:
as="aside"makes the sidebar a complementary region; items withcheckedare checkbox options (radios in a group withselect: "single"), a group'scontentholds any control,collapsibleturns the icons-only mode off. - New
UserMenu;WorkspaceSwitchertakescompact. AppShell navigation="sidebar"withsidebarGroups,workspaces,userMenuandsidebar.
0.5.0
Minor Changes
- #39
90fd777Thanks @robintrepte! - The icon library grows to 690 icons.
- Everything product apps on the kit use, among them
panelLeftClose,circleCheckBig,messageSquarePlus,replyAll,forward,lifeBuoy,gavel,octagonX,arrowDownUpandgrid3x3. - New categories Games (
swords,castle,pickaxe,dices,skull, chess pieces …) and Maps and 3D (draftingCompass,satellite,rotate3d,axis3d,lasso,spline…); more AI icons (brainCog,circuitBoard,scanText,textSearch,orbit,infinity…). - More brand logos: AI (
brandOpenai,brandGooglegemini,brandMistralai,brandPerplexity,brandDeepseek,brandOllama,brandCursor,brandGithubcopilot,brandModelcontextprotocol…), business and marketing (brandLinkedin,brandMicrosoft,brandSlack,brandMeta,brandGoogleads,brandGoogleanalytics,brandTrustpilot), shops, infrastructure and automation (brandWoocommerce,brandShopify,brandWordpress,brandCloudflare,brandHetzner,brandSupabase,brandN8n,brandZapier…) and payments and crypto (brandRevolut,brandCoinbase,brandBinance,brandBitcoin,brandEthereum). - LinkedIn, Microsoft, Windows, Slack, Skype, Amazon and OpenAI come from Font Awesome Free (CC BY 4.0); the attribution is in
ICONS-LICENSE.
0.4.0
Minor Changes
- #37
c40834aThanks @robintrepte! - An icon library: 567 more icons in the kit's style, from passkeys and security to social and brand logos, files, media, commerce, places, home and health.
@twentyfirstmedia/halo/icons(and/pro/icons,@twentyfirstmedia/ui-core/icons) exports each icon by name:fingerprint,scanFace,keyRound,shieldCheck,brandX,brandInstagram,brandGithub… Register what you use once withregisterIcons({ fingerprint, brandGithub })(orregisterIcons(ICON_LIBRARY)), then use the names inIconand everyiconprop. Only registered icons are bundled.- Line icons are drawn on the kit's 24px grid with 2px round strokes (from Lucide, ISC); brand logos are filled (from Simple Icons, CC0). Licence notices ship in
ICONS-LICENSE. - Your own icons:
registerIcons({ acme: "M…" }), typed through theCustomIconsinterface. The 93 built-in icons work as before and cannot be replaced. - An unknown icon name now logs a warning once instead of silently drawing a placeholder.
@twentyfirstmedia/elementsregisters the whole library, so<tf-button icon="fingerprint">works in plain HTML; it also exportsregisterIcons.- New docs page "Icons": every icon with search, categories and copy on click. The MCP server's
list_icons,search_docsandcheck_usageknow the library.
0.3.1
Patch Changes
- #35
3a25cd6Thanks @robintrepte! -FloatingLayouton a phone keeps the height it is given: the stacked slot grid counts its padding into the inherited height (box-sizing: border-box). Before, the layout grew by twice the padding, and with a fixed height the bottom slot was cut off at the edge. Apps that worked around it with.tf-floating-ui { box-sizing: border-box; }can drop that rule.
0.3.0
Minor Changes
- #30
bef588dThanks @robintrepte! -ButtonandIconButtonpass native attributes on to their<button>(or, withasChild, to the child):id,name,value,form,title,tabIndex,autoFocus,aria-label,aria-labelledby,aria-describedby,aria-expanded,aria-controls,aria-haspopup,aria-pressed,aria-current(documented asNativeButtonProps), plus any other<button>attribute,data-*, event handlers and, in React 19,ref.typealso takes"reset", andIconButtontakestype. OnIconButton,aria-labelortitleoverride the oneslabelsets. WithasChild,onClickand the other attributes now reach the child as well.
In @twentyfirstmedia/elements, <tf-button> and <tf-icon-button> take these as attributes; aria-* attributes move from the host to the inner button. The docs and the MCP server list them with both components.
- #28
ef1090fThanks @robintrepte! -FileDrophands over the real files.
- New
onFiles(files: File[])with theFileobjects the person picked or dropped, afteracceptandmultiple. Added list entries carry theirfiletoo, soonChangegets them as well. - Progress only when the app reports it: with
onFiles, nothing is simulated; setprogresson controlledfilesto show a bar (now a labelledprogressbar). An uncontrolled list withonFilesshows each file with its size only. acceptis checked on drop as well;onRejectreceives what did not match.onRemovereports removed or cancelled files.simulateProgresskeeps the old demo animation; it stays the default withoutonFiles, so existing code behaves as before. Running simulations stop on unmount.
- #31
ece54b1Thanks @robintrepte! -GalleryandBeforeAfterload images lazily and takeloadingandreferrerPolicy.
Gallery: the main image loads lazily by default (loading="eager"for a gallery at the top of the page), thumbnails always lazily, all images decode asynchronously;referrerPolicyapplies to every image (new typeImageReferrerPolicy).BeforeAfter: the slider has anaria-valuetext("Before 40%, After 60%", withbeforeLabel/afterLabel), overridable withvalueText(percent); its accessible name islabel. Page Up/Down, Home and End move the slider; arrow keys no longer scroll the page while it has focus. Both images takeloading(default lazy) andreferrerPolicy.
- #29
4cf034aThanks @robintrepte! -Paginationcan be controlled:pagewithonChange(page)(1-based), ordefaultPagewith an optionalonChangefor an uncontrolled pager.pagewithoutonChangestays the start page, as before. Clicking the current page or past either end no longer reports a change.
- #33
d408a82Thanks @robintrepte! - Every built-in string is overridable, and numbers follow a locale.
UIProvider messages: replace any text a component shows on its own (accessible names such as "Close", "Send", "Previous image", "Select row", button texts, placeholders, status words, validation messages). Partial and nested; props such astitleorlabelstill win. English stays the default and no translations ship.useUIStrings(),DEFAULT_STRINGS, theUIStringsandUIMessagestypes, and one exported group per component file (MEDIA_STRINGS…).UIProvider locale(BCP 47, defaulten-US): every number, amount, percentage and date in components, calendar month and weekday names.useFormat()returns the helpers bound to it;formatNumber,formatMoney,formatPercentandformatDatetake an optional locale as their last argument.CountUpandStatTileread string values in the provider locale ("13,05 €/m²" in de-DE counts to 13.05 instead of showing "1,305"). New helpersparseFormatted,formatLike,numberSeparators.SearchFieldtakeslabel(accessible name).- Fixed:
VoiceModewithstateLabelshowed "Listening…".
- #32
3b0992dThanks @robintrepte! -TextFieldpasses native constraints to its input:min,max,step,minLength,maxLength,pattern,inputMode,readOnlyandautoFocus(next to the existingname,requiredandid), all typed and documented.PlanCardtakesdisabledfor its button.
0.2.1
Patch Changes
- #26
863179bThanks @robintrepte! - Published through npm Trusted Publishing: the release workflow no longer uses an npm token, and every package carries a provenance statement linking it to the GitHub Actions run that built it. No code changes.
0.2.0
Minor Changes
- #24
4529ab5Thanks @robintrepte! - Whole dashboards in glass, in both kits.
- New
Ambient: a calm, living background of soft colour fields from the kit (pastels in Halo, a faint aurora in Pro) that drift slowly, or a photo or video under a scrim, or your own layer. Still with motion off, paused off screen. New tokens--ambient-base,--ambient-1…4,--ambient-scrim. - In a glass region,
AppShellturns into glass (top bar, rail, sidebar; the sheet clears), andStatTileand tinted cards lay their tint over the glass (--glass-tint). - On glass, the faintest text steps up one shade so secondary text stays readable.
AppShellappliesclassName.- New pattern: a wellbeing dashboard in glass.
- #22
beb3043Thanks @robintrepte! - Glass surfaces for UI that floats over maps, 3D scenes, video or photos, in both kits: translucent, blurred and colour-lifted, soft and milky in Halo, dark with a hairline edge in Pro.
surface="glass"onPanel,Card,DockandMapView(legend, controls, popup), and<UIProvider surface="glass">for everything floating in a subtree: top-level surfaces, loose controls, menus, popovers, dialogs and drawers. What sits on glass (inputs, tab tracks, badges, highlights) turns translucent too;surface="solid"opts one out. Without React:class="tf-glass"ordata-surface="glass".- New
FloatingLayout: four slots (top,start,end,bottom) over a full-bleedbackground, glass by default; on phones the slots stack and the layout grows with them. - New glass tokens in both kits (
--glass,--glass-strong,--glass-control,--glass-well,--glass-hover,--glass-border,--glass-highlight,--glass-sheen,--shadow-glass,--glass-filter), derived from the theme. Body text on glass keeps 4.5:1 over any backdrop; reduced transparency and forced colours switch to solid surfaces, more contrast to the stronger fill. MapViewtakesheight="100%"(any CSS length).Docknow appliesclassNameandstyle.- A
UIProvidersubtree with its owntokenskeeps the outer theme, so derived colours follow the tokens even without athemeprop. - New pattern: a city map with glass controls.
- #22
beb3043Thanks @robintrepte! - Motion with one switch, in both kits.
<UIProvider motion="off" | "standard" | "expressive">(ordata-motion):expressivegives orchestrated page entrances (Halo: longer, more travel, a short focus-in blur; Pro: crisp and staggered),offstops everything, e.g. for a setting in your app. The reader's reduced-motion setting always turns motion off.- Choreography: charts, progress bars and
CountUpwait for the card they sit in; aStaggerbelow the fold waits until it scrolls into view (reveal={false}plays at once). - Layers animate out as well as in: popovers, tooltips, hover cards, comboboxes, dialogs, drawers, the command palette and toasts.
Modal,DrawerandCommandPalettetake an optionalopenfor that. startViewTransition(update)for page transitions (the kits style::view-transition),useMotion()for the level in effect,usePresence(open)andleaving()for your own layers.- New motion tokens:
--dur-exit,--motion-time,--motion-travel,--motion-blur. - Fixed: with
themes.cssorscoped.css, keyframes defined in another file of the kit never ran (spinners, skeleton shimmer, pulses, pop-ins).
Patch Changes
- #25
688f141Thanks @robintrepte! - Glass app shell: it keeps the layout of the solid shell and turns its frame into glass, with the top bar and rail sitting on it as they sit on the solid frame (they no longer float as separate pieces). AToggleGroupis only as wide as its items.
0.1.1
No changes in this release.
0.1.0
Minor Changes
- #12
2cf6196Thanks @robintrepte! - Consistency pass over every component in both kits and themes. The app shell responds to its own width (container queries); kit boxes no longer depend on the host's box-sizing; controls and code use the kit fonts; bold stays on the type scale; status shapes, list icons, stat values, pagination and invoice numbers line up; segmented controls and tabs no longer stretch or break mid-word. Gauge, Funnel and ProgressRing now fill when reduced motion is on. Pro follows its reference more closely: decorative tints and chart series are monochrome, bars are flat, in-flow cards use hairlines, badges sit on the corner. New:formatPercent.
- #8
2db7b83Thanks @robintrepte! - First release: every component of the Halo reference inui-core, the Halo and Pro kits with the same components, props and markup (switchable at runtime viadata-kit), tokens and a Tailwind v4 preset per kit, self-hosted fonts and the CLI (init,add,list,diff).
- #10
532f835Thanks @robintrepte! - Mobile first: every component works from 320px up in both kits (desktop from 1181px unchanged). The shell, rail and page nav reflow on phones, tables scroll in their own box, dialogs open as bottom sheets, popovers and tooltips stay on screen, text fields use 16px on touch screens and Pro switches to comfortable hit targets on touch unlessdata-densityis set. New:tf-gridcolumns via--cols/--cols-md/--cols-sm,tf-sheet-nav/tf-sheet-aside,tf-table-scroll,useKeepInView,useScrollFocus,TooltipdefaultOpen.
- #16
350ca53Thanks @robintrepte! - Open source under the MIT license and published on npmjs.com: install withnpm i @twentyfirstmedia/halo(orpro), no registry setup or token.twentyfirst-ui initno longer writes an.npmrc.
- #13
559cb2dThanks @robintrepte! - English only: built-in text is plain English and overridable through props; numbers, money, percentages and dates format with Intl in en-US (formatNumber,formatMoney,formatPercent,formatDate).
New components in both kits: Accordion, Separator, AspectRatio, Label, Toggle, ToggleGroup, ButtonGroup, ScrollArea, HoverCard, Carousel, Menubar, NavigationMenu, Sidebar, SelectMenu, Form and ResizablePanels. Menu gains keyboard navigation, typeahead, submenus, checkbox/radio and disabled items; Tabs render panels; Drawer takes side; Popover flips when there is no room; CommandPalette takes loading, empty and onQueryChange; Button and IconButton take asChild; inputs take id, name and required.
- #18
0f19447Thanks @robintrepte! - shadcn-style theming in both kits. The kits use shadcn/ui's theme variables (background,foreground,card,popover,primary,secondary,muted,accent,destructive,border,input,ring,radius,chart-1…chart-5,sidebar-*) and every component reads them, so a pasted shadcn theme restyles everything, light and dark. The old--accent/--on-accent/--accent-hoverare now--primary/--primary-foreground/--primary-hover;--accentis the highlight under the pointer, as in shadcn. Token blocks have zero specificity and dark mode also follows the.darkclass. New:themeCss,parseThemeCss,brandTheme,THEME_PRESETSand preset CSS files (themes/blue.css…),UIProvider tokens,twentyfirst-ui theme, Tailwind utilities with shadcn's names.
Patch Changes
- #15
2c5ddb6Thanks @robintrepte! - The CLI ships an MCP server for coding agents:twentyfirst-ui mcp(stdio, offline, the docs are bundled) with the toolssearch_docs,list_components,get_component,get_pattern,get_guide,get_tokens,list_iconsandcheck_usage, every page as a resource, and the promptsbuild_screenandreview_ui.twentyfirst-ui mcp initregisters it for Claude Code, Cursor and VS Code and adds the kit rules toAGENTS.md.
- #11
acec782Thanks @robintrepte! - Icons are centred: 36 icon drawings were off the middle of their 24px grid (up to 1.75 units), visible in round icon buttons and tinted icons. They are now centred on their bounding box; shapes are unchanged, andplaystays optically centred.