Repository navigation
Releases: codeatlasdev/ui
Release list
@codeatlasdev/ui@4.6.0
Minor Changes
-
0219812: AI agents get one canonical entry point.
llms.txtopens with a recipe for every screen type (app shell, list, form, settings, auth, dashboard, landing, theming), then decision tables and a "common workarounds" page;get_guideand the new per-page files/llms/<slug>.txtcarry the complete demo source of each page, whilellms-full.txtstays under its old size.ca-ui-mcpaddsget_recipeand reports deprecated components (Row,GlassCard,GlassSurface) with their replacement.initwrites a marked, idempotent block into the app'sAGENTS.md. The agent skill no longer teaches the removedcols/pixel-gaplayout API or deprecated components. -
1b1b993:
AspectRatio(#446, part of #432): a box that holdsratiofor a video, an embed or any child with no size of its own; a directimg,video,iframe,canvasorsvgchild fills it.Imagegainsratio(crop to a width / height box, filling the container) andfit(coverorcontain). Replaces the RadixAspectRatiorow of the React equivalence table. -
23fa9ee: Auth building blocks (#379):
Turnstilein@codeatlasdev/ui/forms(Cloudflare captcha, explicit render, loads the vendor script on demand with no dependency and no server access, follows the colour scheme and the locale,bind:handlewithgetToken()/reset(), removed on unmount, silent no-op without asiteKey),PasswordStrength(3-step meter with a polite text label, purepasswordScore()and ascore/labelsoverride),SplashScreen(role="status",ready+minDuration, skipped under reduced motion) andAvatardecorative(no accessible name next to a visible name). New i18n groupspasswordStrength,splashScreenandturnstile(en, pt-BR). -
00757fe:
AuthLayout:panelTone(auto|brand|flat) and the--ca-auth-panel-bg/--ca-auth-panel-fgtokens. A flat panel is a solid surface with a hairline, themed text and no blobs; the defaultautopicks it under theflatandoutlinedsurface styles, so themes that forbid gradients no longer override.ca-auth__paneland.ca-auth__blobsfrom app CSS (#409). The panel slot's--panel-*variables are now CSS (derived from--ca-auth-panel-fg) instead of an inline style. -
78ddb7f:
AuthLayout:panelSide(left/right),panelBleed(full-bleed, sticky panel with no padding, logo or blobs, and a hairline divider),panelHidden(md900px orlg1200px; the panel snippet is unmounted while hidden, so media inside is never fetched on mobile), aheadersnippet (sticky bar over the form column that replaces the mobile brand strip) andalign="start".AuthCardgetsvariant="plain"(display-size title, no card width cap). New template: Waitlist with map (#347). -
b192b0a:
Badgerenders an inline status pill:<Badge tone="danger" subtle dot size="md">Overdue</Badge>(tonesbrand,success,danger,warning,info,neutral;subtleis a tinted background whose label clears 4.5:1 in light and dark).ButtongainstrailingHint(aKbdintrailingis hidden belowmdand on touch with no reserved gap) andalign="between"(label at the start,trailingat the end; the spinner keeps its place). Defaults are unchanged. -
acdf4f3:
ButtonGroup(#448, part of #432): attachesButton,IconButtonand aDropdownMenutrigger into onerole="group"control, horizontal or vertical (orientation), with square inner corners, a single-line seam and the hovered or keyboard-focused member raised over it. Replaces the shadcn Button Group row in the React equivalents table. -
8c31f6a:
ButtonandIconButtongettone="neutral"(#357):outlinedandghostbuttons drawn with a neutral label, border and hover/pressed fills instead of the brand colour, for secondary actions beside a brandfilledone.IconButtonalso gets theoutlinedvariant. Label and border contrast is checked for every preset intests/contrast.test.ts. -
61912be:
CarouselandCarouselItem(#445, part of #432): native scroll-snap slides that follow the WAI-ARIA carousel pattern (labelled region, slides named "n of m", off-screen slidesinert, labelled dot buttons witharia-current, arrows / Home / End on the focused carousel).perView,loop,bind:index,onchange;autoplay(ms) is opt in, adds a Pause button, stops on focus or hover, is a polite live region only when stopped, and never runs underprefers-reduced-motion. No carousel engine dependency. Replaces shadcnCarousel. New i18n groupcarousel(en, pt-BR). -
82edb9d:
ca-ui-mcpgainsget_release(version): a release as a post for agents, with its highlights, the numbers and the conditions each was measured under, the behaviour changes to check after upgrading, the upgrade command and every merged change grouped by area with its pull request and issue. Without a version it returns the digest of every release. It readsdist/releases.json, built from the release posts in the docs site. -
7b30608: New
ChipGroup: single choice among a few short options as chips that wrap (#355). A realradiogroupof native radios (roving tab stop, arrows/Home/End/Space/Enter, disabled items skipped), three columns belowsmand free wrap fromsm, a check icon on the selected chip,errorasrole="alert"witharia-invalid/aria-describedbyon the group,name/requiredfor native forms, 44px chips. Use it instead ofRadioGroup(too tall) orSegmentedControl(never wraps) for options like team size. -
689d2c5: New token
--ca-code-line-height(20px): an integer px line height for code surfaces.CodeSnippet,.ca-code preand a bare Markdownpreinside.ca-proseuse it instead of the unitless 1.6 / 1.7, which rounded to a different pixel height in a text layer and in an overlay (a caret and an active-line highlight drifting apart). Thecodeinside aCodeSnippetalso pins--ca-font-mononow: it used the UA's baremonospace, resolved per lookup. The docs gain "Code surfaces: integer line-height, one font stack" for apps that build an editor.The stylesheet also drops
--ca-action-focus, a token no component, doc or template ever read, to stay inside its size budget. -
caf82ad: Component tokens for
Button,IconButtonandKbd. Weight, tracking, line-height, gap, per-size padding, height and font size, ghost border width, disabled background and the touch minimum are--ca-btn-*,--ca-icon-btn-*and--ca-kbd-*custom properties; set them on:root, on a wrapper or through the newThemeOptions.components(button,iconButton,kbd, merged bymergeTheme). Unset, every component draws exactly what it drew in 4.5.0. One naming convention for all components (--ca-<component>-<property>,--ca-<component>-<size>-<property>) is documented in the theming guide. No more selectors against.ca-btn--lg,.ca-icon-btnor.ca-kbd. -
3b76318:
ca-ui-mcpgainslist_conceptsandget_concept: the Concepts pages (viewport-complete UX, motion that respects people, targets and focus, appeal is not usability) with every claim's strength label, scope, sources, limits and last-reviewed date. The llms-full concept digest now carries the contract and the prompt only, so the file stays under its size cap. -
e125cc8:
ContextMenu(#455): wraps a region and opens aDropdownMenuat the pointer on right-click (a second one moves it), at the finger on a touch long-press (longPressDelay, default 500 ms, cancelled by lifting early or dragging) and from the keyboard with Shift+F10 or the ContextMenu key. Themenusnippet takes the same items asDropdownMenu(items, checkbox and radio items, submenus, labels, separators). Text fields keep the browser's menu;disabledkeeps it everywhere;bind:open,onOpenChange,focusable,menuClass. -
c19ba2b: Density and appearance tokens for
Avatar,TextArea,TextField,Card,SegmentedControlandEmptyState, on the same--ca-<component>-<property>convention as the Button tokens, also settable throughThemeOptions.components.Avatargetstone="neutral"(surface background, secondary text, hairline ring) and--ca-avatar-fallback-{bg,color,size,weight}with per-size--ca-avatar-<size>-fallback-*.TextFieldandTextArearead--ca-field-gapand--ca-field-label-leading(or their own--ca-tf-*and--ca-ta-*);--ca-ta-min-heightis the height of the visible box, border included.Cardreads--ca-card-{bg,radius,border,shadow}.SegmentedControlreads--ca-seg-item-{padding-x,font-size,line-height}(and per size).EmptyStategetsbare,actionsFilland--ca-empty-{padding,title-size,title-weight}. Unset, every component draws what it drew in 4.5.0. -
ab42958:
ca-ui-mcpgainssearch_terms,get_termandget_reference: the 269-term design atlas and the verified sources behind it, offline fromdist/atlas.json. Scores are labelled heuristic, every term carries its limits and review date, and a reference returns what it does not support and the wording that was read. -
73f38cd: Design style pages (#583):
/styles/<id>/forswiss,editorial,brutalism,sketchbook,glassmorphismandterminal, each rendered in its own style, with the origin, the traits (type, colour, shape, depth, motion), do and don't, accessibility pitfalls with their sources, before and after on real components, thedesignStyle()recipe and a prompt. Theca-ui-mcpserver gainsget_style(a style as markdown, or the digest of all of them) andllms.txtlinks the digest at/llms/styles.txt.designDefaults()is exported: the stylesheet default of every knob a theme can write, so a scoped preview can reset what an outer theme set. -
300ac7c:
Dialogsplit is configurable:splitSize={{ width, height }}(any CSS length),splitColumns="2fr 3fr"andfullscreenBelow="sm" | "md"(any size: exactly the viewport under the breakpoint, square corners, safe-area padding kept)...
@codeatlasdev/ui@4.5.0
Minor Changes
- c23cff4: i18n: the message catalog is split per group and each component bundles only its own strings.
root: Button onlydrops from 27.89 to 23.91 kB gzip andAppShellLayoutfrom 39.88 to 36.07 kB.Messages,getMessages,useT,I18nProvider,setLocaleandregisterLocalebehave as before;useT()andgetMessages()still return the full catalog, which is bundled only when you import them.
Install
bun add @codeatlasdev/ui@4.5.0Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛
@codeatlasdev/ui@4.4.0
Minor Changes
-
1c6653e:
NavItemin the AppShell panel getscolor(a decorative dot),leading(a snippet that replaces the icon),count(a neutral total, announced as a bare number instead of "N unread",1.2kabove 999) andactions(a snippet ofDropdownMenuItems behind a "…" button named "Actions for ") (#275).NavSection.onReorderandNavModule.onReordermake the rows reorderable with theuseDndprimitive: a grip per row, mouse, touch press-and-hold and keyboard, with announcements; the shell reports{ id, from, to }(newNavReorderEventtype) and never reordersitemsitself.PanelNavItemnow renders a row that contains the<a class="ca-nav-item__link">(a link cannot hold the actions button or the grip), so.ca-nav-itemis the row, not the anchor. New messagesappShell.itemActionsandappShell.moveItem. -
1c6653e:
AppShellLayouttakespanelWidth(px, held between 160 and 480, default 200) andresizablePanel(#292). The content offset and the open and close animation follow the width, so a panel for long labels no longer misaligns the page.resizablePaneladds a keyboard-operable drag handle on the panel's trailing edge (Arrow keys 8px, Shift 32px, Home and End for the bounds); withpersistPanelthe chosen width is stored inca-shelland restored by the inline script before the first paint. NewappShell.resizePanelmessage and.ca-panel__resizeclass. -
ca80cfd:
Combobox:multipleturns it into a searchable multi-select (valueisstring[],aria-multiselectablelistbox, list stays open, removable chips,Backspaceon an empty query removes the last chip) andcreatableworks in both modes.CommandPalette:search(query, signal)adds debounced (searchDebounce), abortable server-side results with loading, empty and failed states announced throughannounce(), plusonSelectand the exportedCommandResulttype. -
49116d1:
DataTablegains row groups, column resize, row reorder and safer interactive cells (#283).groupByrenders one<tbody>per key with a collapsible header (aria-expanded,groupLabelto customize).meta.resizableon a column adds a window-splitter handle (mouse, touch, keyboard arrows, Shift, Home, End, Enter) driven bysize,minSizeandmaxSize; the newDataTableColumnResizeis exported for hand-built headers.onReorderadds a drag grip per row on top of the newuseDndprimitive, moves rows between groups and reports{ id, from, to }(DataTableReorderEvent);rowIdgives rows stable ids andpageSizesets the page size in simple mode. A click on a link, button, input or other control inside a cell no longer firesonRowClick(adddata-no-row-clickto opt other elements out). The new labels (Reorder, Move, Resize) follow the active locale (en, pt-BR). Needs the drag and drop primitive from #284. -
d138bd8: New drag and drop primitive,
useDnd(),applyMoveand<DndLayer>, for sortable lists, moves between lists and drop zones (#284). It works with mouse and pen (4px threshold), touch (press and hold, so the page still scrolls) and keyboard (Space lifts, arrows move, Home/End jump, Enter drops, Escape cancels), speaks every step throughannounce()(English and Portuguese built in,useDnd({ messages })for others), auto-scrolls near the edges of a scrolling container and drops its animations underprefers-reduced-motion. The engine measures the boxes once per drag and only moves a clone, a drop line anddata-dnd-*attributes, so the list does not re-render untilonDrop.<DndLayer {dnd} />renders the keyboard instructions and the forced-colors aware styles (.ca-dnd-overlay,.ca-dnd-indicator), so apps that never drag add no bytes tocore.cssor to the i18n catalog. -
946d69d:
Drawercan be named without its default title: passaria-label,aria-labelledby(your own header) orhideTitle, andid,data-*and other attributes now reach the<dialog>(#285).AlertDialoggainsconfirmPropsandcancelProps(attributes for the two buttons) andcancelVariant(#287), plus achildrenbody, adescriptionthat accepts a snippet andconfirmDisabledfor type-to-confirm (#278). -
e983caa:
DropdownMenuis controllable and can open at a point.bind:openandonOpenChangeread and set the open state;anchor={{ x, y }}opens the menu at a viewport point (a right-click menu, notriggerneeded) and changing it while open moves the menu instead of toggling it. A menu opened from code starts on its first item and returns focus to the element that had it. On the built-in trigger Shift+F10 and the ContextMenu key open the menu like ArrowDown. -
e983caa:
DropdownMenugains selection items.DropdownMenuCheckboxItem(role="menuitemcheckbox",bind:checked,onCheckedChange) andDropdownMenuRadioGroup+DropdownMenuRadioItem(role="menuitemradio",bind:value,onValueChange) renderaria-checkedand a check indicator, stay open after a pick (closeOnSelectcloses), and join arrow, Home, End and typeahead navigation (the menu now also recognizesmenuitemradio). -
e983caa:
DropdownMenugains submenus.DropdownMenuSubtakes atriggersnippet holding aDropdownMenuSubTriggeritem (aria-haspopup="menu",aria-expanded, chevron) and the submenu items as children; they nest to any depth. ArrowRight, Enter or Space opens the submenu on its first item, ArrowLeft or Escape closes only that level, hovering the trigger opens it after a short intent delay and the pointer can cross into it. Selecting an item closes every level. A popover layer that is closing no longer owns Escape or outside presses, so a closing submenu hands them to its parent. -
ff460ad: Form controls forward native attributes and expose their element, with one vocabulary (
ref,hideLabel, rest props on the native element):Checkbox,TextField,TextAreaandFileUploadspread every other attribute and handler (data-*,maxlength,inputmode,onclick,onkeydown,name,capture...) on the native<input>/<textarea>, and take a bindablerefwith that element.Checkboxonchangenow receives the native event as a second argument, and its forwardedonclickmakes Shift range selection possible.TextField/TextAreagainonfocusandhideLabel(label kept for screen readers with.ca-sr-only;aria-labelalso works without a label).Selectacceptsaria-labelandaria-labelledbyfor a compact select without a visible label. The trigger is named by that text followed by the current value (visible text stays in the name); without either, the placeholder names it as before.FileUploadgainsref, anopen()instance method for a button outside the dropzone, andlist={false}: a plain picker whoseonchangereceives only the files just picked, with no built-in list and nothing kept infiles(the native input is cleared, so the same file can be picked again).
-
8c24c31: Theme option
motion: { enter: "none" }(token--ca-enter-scale: 0) turns off entrance and exit animations only:Dialog,AlertDialog,Drawer,Sheet,Lightbox,CommandPalette,DocsSearch, date pickers,DropdownMenu,Popover,TooltipandToastappear and leave in place, while hover and focus color transitions keep--ca-duration-*. New derived tokens--ca-enter-fast,--ca-enter-baseand--ca-enter-slow;MotionStagedemos render their final state when the theme turns entrances off, as underprefers-reduced-motion(#268). -
16f89dd:
Popoveris controllable.bind:openandonOpenChangeread and set the open state, and thechildrensnippet receivesclose, which closes the popover and returns focus to the trigger, so a choice made inside it (aCalendarday, an option) can dismiss it. Existing popovers are unchanged. -
8c24c31: New
--ca-radius-roundtoken (999px × --ca-radius-scale): "fully rounded at the current scale".SwatchGroupswatches, the AppShell active pips (.ca-nav-item__pip,.ca-rail__pip) and the new-item dot, and.ca-nav-item__badgeuse it, so a theme withradius: 0squares them with no app CSS (#267).--ca-radius-chipnow aliases it.--ca-radius-pillstays a fixed 999px for functional shapes (Switch, Slider, Progress, scrollbars) and forcontrolRadius: "pill". -
946d69d:
Tabsgainsvariant="line"(bare text tabs with an underline on the active one, for page headers), andTabsListandTabsTriggerforward rest attributes:aria-labelnames the tablist,data-*,idand event handlers reach the trigger button (#286). ATabsTriggernow pointsaria-controlsat its panel only while aTabsContentwith the samevalueis mounted, so tabs used as view switchers no longer reference missing elements. -
2ab0d93:
ToggleGroupgetsrequired: pressing the active item no longer clears the selection (radio-like,onchangestays silent;type="multiple"keeps its last pressed item). An empty group still selects on the first press. Closes the gap withSegmentedControlfor filters that must look like contiguous buttons.
Patch Changes
- 05cfbfc:
AppShellLayout: an item atpath: "/"is active on/only, no longer on every page, and a module atbasePath: "/"only gets the pages no other module claims. Module prefixes (basePath,pathPrefixes) match whole path segments and the longest one wins, whatever the module order (/settingsfoono longer matches/settings). - 98d2ace: Astro: the theme now survives
<ClientRouter />(View Transitions) navigations.caUIregisters anastro:before-swaphandler that carries#ca-theme,#ca-runtime-tokens, thetheme-colormeta and the theme attributes onto the incoming document (#265).codeatlas-ui initon Astro also addssvelte()from@astrojs/sveltetointegrations, and notesnpx astro add sveltewhen the dependency is missing (#273). - 3ae1f41:
Buttonwithhrefnow types the anchor's own attri...
@codeatlasdev/ui@4.3.0
Minor Changes
- a1c0d04: Demo sidecars accept
names(motion vocabulary) andmotion(exact property, duration, easing, delay, stagger, spring). They are printed ine/NNN.txtandp/NNN.txt, carried incatalog.jsonandcatalog.tsv, andca-ui-mcpsearch_demosmatches names and aliases before summary and tags. - a1c0d04:
MotionStageand theMotionCtxtype: the frame of a motion demo. It autoplays only while at least half of it is in view and the tab is visible, hands control to the user on pointer, key or focus (autoplay stops, Replay appears), always shows a Pause button witharia-pressed, and underprefers-reduced-motionnever autoplays and tells the demo to render its final state (ctx.reduced).ctx.cycleincrements each loop so a demo can re-run its animation. Labels come from the i18n catalog (motionStage.pause,motionStage.replay) and can be overridden withpauseLabel/replayLabel.
Install
bun add @codeatlasdev/ui@4.3.0Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛
@codeatlasdev/ui@4.2.0
Minor Changes
- b65d56d: AI feedback loop. The
ca-ui-mcpserver has a newreport_issuetool: an agent that hits a library problem (bug, missing prop or component, migration gap, wrong docs) gets a sanitized issue draft (paths, foreign URLs, emails, IPs, tokens, env assignments and foreign package scopes are redacted, and the redactions are listed) with a prefilled link to the new "AI report" issue form and agh issue createcommand. It never posts.bunx @codeatlasdev/ui workaroundslists the// ca-ui-workaround(#<n>): <why>markers an agent left in your code and which issues are already closed (read-only, offline-safe, exit 0). - 81910d1: Avatar renders the photo as a real
<img>(no detachedImage()probe, so no double download) and passesloading,referrerpolicyandcrossoriginthrough, soloading="lazy"keeps a 200-row list from firing 200 requests andreferrerpolicy="no-referrer"works with CDNs that reject a cross-originReferer.fallbacknow also takes a snippet (an icon for groups, channels and bots), andonerrorfires when the image fails so you can refetch a signed URL. The fallback is always rendered beneath the image, so the box never changes size. Thedata-statusattribute on.ca-avataris gone. - 80c0336:
Cardhas apaddingprop ('none' | 'sm' | 'md' | 'lg', any CSS length, or a number of pixels) so a one-off density no longer needs an inner wrapper;flushstays as the alias of'none'. The docs now say that Card's content is already padded, and in development a[ca-ui]warning (card-double-padding) fires once when the first child adds its own padding on top of the card's (the 20px + 20px = 40px "oversized card"). - cd655bb: Chat primitives:
MessageBubble(direction, grouped tails, sender and avatar slot, quote, reactions, delivery ticks),ConversationItem(inbox row with unread, pinned, muted, typing, active),AudioPlayer(lazy src, waveform frompeaks, slider semantics, playback rate),MediaGrid(image, video and document tiles opening aLightboxgallery) andVirtualList(windowing plus load-older at the top that keeps the scroll position). Newchat,audioPlayer,mediaGridandvirtualListmessages in en and pt-BR. - 80c0336:
CopyButtontakesaria-labelfor the icon-only form (label=""), so each row of a list or table can announce its own name ("Copy client_id of Acme") instead of the same localized "Copy". The default stays the i18ncopyButton.label. The label is ignored whenlabelis visible, so the accessible name keeps containing the visible text. Remaining attributes (data-*,id,aria-describedby, …) are now forwarded to the underlying button. - 6e08799: Every numbered demo now has a reviewed
<Variant>.meta.jsonsidecar (summary,use,spec,tags,shape), soget_demo,search_demosand/api/v1/en/{e,p}/NNN.txtreturn what the demo shows, when to use it and a Spec list of its literal values (props, snippets, state, counts). The catalog check (manifest --check, run by every build) now fails on a demo without a sidecar, a sidecar without a demo, an unknown key, a badshape, or a spec line that quotes a token that is not in the demo source. Newcodeatlas-ui pick [ids...]opens the gallery, listens on127.0.0.1with a random per-run token and prints the ids you send with "Send to AI" (#005 #012). - d99cead:
codeatlas-ui initscaffolds SEO and PWA on SvelteKit (#205): prerenderedmanifest.webmanifest,robots.txtandsitemap.xmlroutes built with theseohelpers, a service worker (src/service-worker/index.tson$app/service-worker,$app/manifestand$app/envfor Kit 3;src/service-worker.tson$service-workerfor Kit 2) with a prerendered/offlinepage, and a default<Seo>with canonical and manifest link in the root layout. Existing files are never overwritten; after creating theminitreminds the app to setpaths.origin(otherwise Kit's prerender placeholder origin reaches the sitemap and canonical URLs), add the icons and excludesrc/service-workerfromtsconfig.jsonon Kit 3. - d99cead: New
InstallPrompt(#208): a dismissible banner that offers to install the app. On Chromium it keepsbeforeinstallprompt(no browser mini-infobar) and shows an Install button that opens the native dialog and reports the choice withonchoice; on iOS Safari and iPadOS, which have no install API, it shows the Share, Add to Home Screen steps. It renders nothing when the app already runs standalone, and stays hidden fordismissDays(default 14) after the user closes it. Strings are in theinstallPrompti18n group (en,pt-BR); theroot: Button onlysize budget rises by 240 B gzip because both catalogs ship with every root import. - 81910d1: Lightbox opens videos and galleries.
items({ type?: 'image' | 'video', src, alt?, caption?, meta?, poster?, download? }, exported asLightboxItem) plus a bindableindexadd previous/next buttons, ArrowLeft/ArrowRight and swipe (no wrap; the end buttons arearia-disabledso focus stays put), a counter, caption and sender/time line, video with controls (paused when you move on), and a Download button (ondownloador a plain<a download>).src/altstill work as a single-item shortcut. Newlightbox.previous,next,downloadandcounterlabels in en and pt-BR; the close button moved into a.ca-lightbox__toolsbar. - 3180023: Leading icon slot for
DropdownMenuItemandTabsTrigger, and a documented pattern for brand logos (#194).iconis a snippet rendered in a fixed 14px box with the standard gap andaria-hidden(the label stays the accessible name), so icon size and alignment no longer drift between menus.brandIconmarks the icon as a fixed-color brand mark: no highlighted, disabled, active or destructive color reaches it (trademark guidelines forbid recoloring). NewSchemeAssettakeslightanddarksnippets and shows the one for the kit's color scheme (.darkon<html>or adata-theme-scopeisland) with CSS only, so it is right in SSR and on first paint and apps no longer read the theme store for logo variants. New classes:.ca-dropdown-item__icon,.ca-tabs-trigger__icon,.ca-scheme-asset,.ca-scheme-asset__light,.ca-scheme-asset__dark. - 7a112eb: Navigation:
LoadingBoundarywraps<svelte:boundary pending>with a 200 ms delay and 500 ms minimum so a loading UI never flashes (default placeholder isSkeletonrows,aria-busy, named region).@codeatlasdev/ui/kitgainsNavigationProgress(top bar driven bynavigatingfrom$app/state) and thependingLink()attachment that setsdata-pendingon the link being navigated to. A test now proves AppShell stays mounted across 10 client-side navigations. - df50d86: New
RecordList,RecordRowandRecordCell: records as dense rows (identity, a few columns, a row menu) that re-flow to a compact two-line row on a phone. The list owns the flush container, the separators, onecolumnstemplate shared by the header and every row, the re-flow (a container query on the list's own width, 36rem), the 44px actions cell and anaria-hiddenheader whose labels become per-celldtwhen stacked. It replaces the hand-rolled flushCard+ grid +:globalrow CSS, and is much shorter on a phone thanTable responsive="cards". - d99cead:
<Seo>completes the social and language tags (#207):og.defaultImageis the site-wide card used when a page has noog.image(also feedstwitter:imageand thesummary_large_imagecard),twitter:titleandtwitter:descriptionmirror the page title and description, and the newlangprop setsog:localeand keeps<html lang>in sync on the client. - 81910d1:
skeletonprop onAvatar,KpiCard,TextField,Table,CardandGlassCard: the component keeps its real DOM, so the placeholder has exactly the loaded size (no layout shift). Content is laid out but hidden, the host paints the same shimmer as<Skeleton>, and it is announced witharia-busy="true"; a skeletonTextFieldis disabled and anAvatarmakes no image request. Reduced motion stops the sweep and forced colors draw an outline. New global[data-skeleton]rule andca-shimmerkeyframes inutilities.css. - f889334: Every backdrop blur now comes from one place. Components no longer declare
backdrop-filter: they setdata-ca-blur="<role>"(or are a.ca-surface) andstyles/surface.csspaints it from the tokens<html data-style>rewrites, so one attribute turns every blur off or down together. The look is unchanged (guarded by a computed-style test over 9 styles, light and dark).FloatingLayergains ablurprop (overlayortooltip).lint:tokensrejectsbackdrop-filteroutsidesurface.cssandmaterials.css. The MCP server gainslist_surfaces, generated from the[data-style]selectors inmaterials.css, anddist/surfaces.jsonships with the package. - 3180023:
TabsListhandles its own overflow (#190). It keeps the width of its tabs insideTabs(no more full-column pill on desktop), and when the tabs do not fit it scrolls sideways on its own with the scrollbar hidden and scroll snapping, instead of wrapping or widening the page on a phone. The selected tab is scrolled into view on mount and when it changes (only the strip scrolls, never the page). New optionalfadeprop masks the edge that still hides tabs, like theAppSettingsnav. The wrapperdivwithoverflow-x: autoand the:global(.ca-tabs-list) { width: max-content }workaround can be deleted.
Patch Changes
- 526fee2: A menu, Select list, popover or tooltip that is already open when a Dialog, Sheet or Drawer opens (a shortcut or an async action opening the modal over it) now moves into that modal and stays clickable and above it. Before, it stayed outside the modal, so it went inert and could not be used. When a modal closes before the popovers hosted in it, they are handed to the next moda...
@codeatlasdev/ui@4.1.0
Minor Changes
- ccae97c: Numbered demo catalog (
ca-prompt): every docs demo has a stable#NNNid. The package shipsdemos.json(export@codeatlasdev/ui/demos.json) anddist/api/v1reference texts, andca-ui-mcpgainssearch_demosandget_demo. The agent skill documents picking demos by number.
Patch Changes
- 6ab52ee: Checkbox, Switch and RadioGroup: on touch screens (
pointer: coarse) a tap on the box toggles the control again. The 44px hit-area overlay painted over the native input and swallowed the tap; the native input now is the 44px hit area.
Install
bun add @codeatlasdev/ui@4.1.0Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛
@codeatlasdev/ui@4.0.0
Major Changes
- b99075a:
AppShellLayoutrenders its content region as<main id={mainId} tabindex="-1">by default, so a page built on it has a<main>landmark (axelandmark-one-main,region) and the skip link lands on it. It was a plaindiv. PassmainId={null}when your page renders its own<main id="main" tabindex="-1">: the region is then a plaindiv, never a second landmark. If you wrapped your content in<main>inside the shell, remove it or passmainId={null}. - fdb6798:
nativeFeel()now has one opt-out class,.ca-selectable, for both the context menu and text selection (#136). The context-menu filter used.selectablewhile the selection CSS used.ca-selectable, so neither class restored both. The filter and its docs now say.ca-selectable, matching theca-*class contract. Clean cutover:.selectablewas never documented outside a JSDoc line, and no stylesheet ever targeted it, so apps using it must rename it toca-selectable(right-click on a.selectableregion is now blocked again until they do).
Minor Changes
- 6e39edc:
caBrandPluginwritessrc/generated/brand.tsas soon as the Vite config resolves (the URLs depend only on a hash of the inputs, so no rendering is needed), not only at build start. Newcodeatlas-ui brandcommand andbrandUrls()/runBrand(): render the icons and the module without a Vite run, so a job that only runssvelte-checkon a fresh clone type-checks (svelte-kit syncdoes not load Vite plugins). - c2d255d:
FormSectionacceptsheadingLevel(2 | 3 | 4 | 5 | 6) andAppSettings/AppFormprovide the section heading level likePageShell, soFormSectionandEmptyStatetitles inside them render as<h2>under the page<h1>(axeheading-order) instead of<h5>. Outside a page layout the default stays<h5>. Only the tag changes, never the look. - af02ce2:
RadioGroup,Checkbox,SwitchandSelectacceptname, so their value is part ofFormDataon a native form submit (progressive enhancement, SvelteKit form actions without a hidden-input workaround).Selectrenders a hidden input carrying the selected value whennameis set. - e7b85e7:
ogCardacceptslang(BCP 47, default"en", escaped) for the card's<html lang>, so non-English cards get the right hyphenation and locale font selection. - 55ecc1a:
Box,Flex,StackandGridare generic onas:<Stack as="form" novalidate onsubmit={...}>accepts the form's attributes, and the handler'scurrentTargetis anHTMLFormElement(same foras="a",as="label",as="button"...). An attribute that does not belong to the tag is a type error. ExportsPolymorphicPropsandElementTag.
Patch Changes
- 153be87:
centsToDisplaykeeps the no-break spaceIntlputs after the currency symbol ("R$\u00a0123,45"), so a price no longer wraps between the symbol and the number.MoneyInputstill shows and parses a plain space in its editable text. - a517102:
toCsv,toJson,ExportColumnandExportMenuaccept any object row type (T extends object), so rows typed with aninterfaceno longer need a cast.keyis still checked againstkeyof T. - 8d25c2f:
caErrorPlugin({ scaffold: true })writes a validsrc/error.html: the theme init script now sits in<head>instead of inside<style>, the page uses the app theme (themeoption, same object ascaThemePlugin) instead of hard-coded amber, its copy andlangfollow the newlocaleoption (endefault, warns when unset), and thejavascript:history.back()link is gone (CSP-safe). The scaffolded+error.svelteuses tabs and double quotes like the other generated files.initnow writescaErrorPlugin({ scaffold: true, theme }). - 9e5655f:
initwithout--writeis now a true dry run: it printswould create <file>instead of writing new files. It also stops creatingsrc/lib/theme.ts(src/theme.tson Astro) whenvite.config.ts/hooks.server.tsalready importthemefrom elsewhere, and wires the files it adds to that import. - a7f7e96: Docs and agent skill: apps must install
@lucide/sveltethemselves. Under Bun's isolated linker the package's own dependency is not importable from the app. - 3ef5b11:
migratekeeps the default thickness ofGlassCardandGlassSurface: a usage withoutthickness(defaultregular) becomeselevation={3}instead of dropping it, so the migrated card keeps its fill, blur, shadow and rim (CardandSurfacedefault toelevation={1}). - 33ad505:
migratelooks uptypescriptin the project directory first (then next to the CLI) and printsmigrate needs typescript (bun add -d typescript)with exit code 1 when it is missing, instead of crashing withCannot find package 'typescript'. This also fixesbunx @codeatlasdev/ui migratein layouts where the CLI cache directory cannot see the project'stypescript. - b7cdd79:
migratenow ends with a list of the usages it left for you (file:line Component: reason (recipe)), and--strictexits with code 1 when any remain. AGridorStackwhosecols/aligncould not be mapped still gets itsgapconverted:gap={20}next tocols="2fr 1fr"becomesgap="5"instead of silently turning into step 20 (80px). Dynamic or off-scale gaps are reported, since their meaning changed from pixels to a step. - 98ea2ed: Nested modals: only the newest focus trap owns focus, so opening a
Dialog/AlertDialog/Sheet/Drawerover another no longer loops onfocusin(Maximum call stack size exceeded); closing it hands focus back to the one below. - daccdc8:
.ca-proseheading anchors stay visible on touch screens: a stray second@media (hover: none)block hid them withdisplay: none, contradicting the rule above it. New "Release lists" docs page documents theca-release*markup and the version-id anchor convention. - c0bc624:
--ca-scrim-blurnow drives the modal scrim (#135). It was declared asnonebut never read: the::backdropof Dialog, AlertDialog, Sheet, Drawer, CommandPalette and DocsSearch hardcodedblur(4px) saturate(180%). The scrim reads the token and the token defaults to that same value, so nothing changes visually;:root { --ca-scrim-blur: none }now removes the blur from all of them. The CommandPalette scrim shares it (it used a plainblur(4px), now with the sharedsaturate(180%)). - 5a3ede3: Agent skill and AGENTS.md import
ThemeOptionsfrom@codeatlasdev/ui/vite(/astroin Astro), like the docs andinit. The package root pulls.sveltefiles that Node cannot load fromvite.config. - 8185835: New
--ca-tooltip-blurtoken for the Tooltip surface blur (#137). It defaults tovar(--ca-blur-ultra-thin), so the look is unchanged;--ca-tooltip-blur: nonegives an opaque tooltip without re-tiering the shared ultra-thin blur (.glass-ultra-thin,.glass-btn-clear,--ca-upload-blur). - 3dda885:
@codeatlasdev/ui/viteexportsdesignStyle,DESIGN_STYLES,mergeThemeandDesignStyleId, so atheme.tsshared withvite.configcan use a design recipe (including the 3.0 migrationdesignStyle("liquid", { ... })) in plain Node. The theming guide and theinittheme comment now point to this entry instead of/astro.
Install
bun add @codeatlasdev/ui@4.0.0Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛
@codeatlasdev/ui@3.1.0
Minor Changes
- 0579bb3:
.ca-prosestyles release lists, so a changelog page needs no custom CSS (#126). Wrap one<section class="ca-release">per version (heading with its channel.ca-chip, then a.ca-release__metarow of date and links, then the rendered notes), add a.ca-release-jumppill index and close with.ca-release-thanks. Anchored releases clear a stickyDocsShellheader (scroll-margin-top), and the meta links and pills keep 44px touch targets. Classes only, no JS or component: the markup can come from Markdown rendered at build time or at the edge. - 973fe00: Add
SwatchGroup: a single-choice color swatch picker (role="radiogroup", roving focus, preset or customcolor). Its coarse-pointer hit area is the swatch itself, so neighbouring swatches never overlap and every click lands on the swatch the user aimed at.
Patch Changes
- b27c73a:
generateBrandand the brand Vite plugin no longer load every system font for each PNG they render. resvg did that by default on every construction (~450 ms per render on Windows, ~2.7 s per icon set, at every build and dev start); fonts now load only when the mark has<text>(#120). - 5c79b24:
FilterGroupdraws its label with--ca-text-secondaryinstead of--ca-text-hint. The hint role can be set low by a custom preset, and the label failed the 4.5:1 axe check on a raised surface in dark mode. - 5c79b24:
KpiCardis built onCardinstead of the deprecatedGlassCard, so rendering it no longer logs the[ca-ui]deprecation warning in dev. The look is unchanged (elevation 3).
Install
bun add @codeatlasdev/ui@3.1.0Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛
@codeatlasdev/ui@3.0.0
Major Changes
-
a1d14f5: Sober default theme, Liquid Glass is now opt-in (#111). The package used to look like a UI-kit demo: a glass material over a three-hue mesh, glow on selected states, a white rim on every surface, hue-tinted 24-40px shadows, 10px controls, 2px icons and 700 headings. The default is now solid and neutral:
defaultStyle: "solid",paper: "plain",radius: 0.8(--ca-radius-mdis 8px),iconStroke: 1.75, heading weight 600 (and no extra h1 tracking).- Surfaces, text and outlines carry almost no chroma (the brand hue stays on fills, focus, links and charts); shadows are neutral black (
--ca-shadow-*,--ca-glass-shadow,--ca-mat-*-shadow). [data-style="solid"]and[data-style="flat"]set--ca-glow-primary(-strong)and--ca-glass-rimto transparent and draw--ca-mat-edgeas a hairline, so.glass-tinted,.glass-prominent,.glass-glow,.glass-active,.glass-btn-prominentand the AppShell nav pills lose their halo and rim without a component change. The page mesh (--ca-page-mesh) is drawn only byglassandfrosted.- New recipe
designStyle("liquid")reproduces the former look exactly (glass, mesh, glow, round corners, bold headings, tinted shadows).designStyle("default")now means the sober default.
Migration: to keep the old look, use
designStyle("liquid", { ...yourTheme }), or setdefaultStyle: "glass",paper: "mesh",radius: 1,iconStroke: 2andtypography: { weights: { heading: 700 } }.colorScheme.setStyle("glass")still switches at runtime. Apps that override--ca-*tokens are unaffected; apps that read--ca-radius-*or the shadow tokens get the new values.defineTheme()with no options now resolves the new defaults (designTokensemits nothing for them).
Minor Changes
-
8b36a1e: AppShellLayout: the shell keeps its state across page loads (#109). On an MPA (Astro, a full reload) every navigation is a new document, and the panel, the selected module, the expanded sections and the panel scroll all reset.
persistPanel(defaulttrue) now keeps the panel open/closed, the selected module and the section open map inlocalStorage["ca-shell"](the oldca-panel-openkey is migrated), and the panel scroll per module insessionStorage. An inline script emitted right after the sidebar restores them before the first paint and setsdata-ca-panel/data-ca-moduleon<html>, so a stored-closed panel is laid out closed with no flash, on the same model as the theme script. Stored values are validated and discarded when malformed. The user's last choice for a section wins overdefaultOpen; the section holding the current page always opens. The shell now starts at its target width instead of animating open on load.persistPanel={false}keeps the old always-default behavior for demos and embeds. -
3476d9a: Brand assets from one mark.
caBrandPluginandgenerateBrand(@codeatlasdev/ui/vite) render a content-hashed favicon (SVG that followsprefers-color-scheme, ICO, PNG),apple-touch-icon, 192/512/maskable manifest icons and a web app manifest from one SVG with{{ink}}/{{spark}}placeholders and your theme;resolveBrandandrenderMarkexpose the resolved sRGB colors.iconLinksandogCard(@codeatlasdev/ui/seo) emit the head links and the 1200x630 social-card HTML forrenderOgImage.<Seo>now also writesog:image:typeandtwitter:image.@resvg/resvg-jsandpng-to-icoare optional peer dependencies. -
1eeca1c: Surface engine and layout primitives (#112).
New components:
Box,Flex,Stack,Grid,Container,Section(layout),Surface,CardandSurfacePanel.- Layout primitives take token props (
gap,p/px/py,align,justify,direction,columns,wrap) and a polymorphicas. A prop renders as adata-*attribute (data-gap="4"), a responsive object ({ base: "2", md: "4" }) asdata-md-gap="4", styled bystyles/layout.cssin@layer ca.utilities. No inlinestyle. Surfacehas orthogonal axes:elevation0-5,materialauto | solid | frosted | glass | liquid,tone,variantfilled | outlined | ghost,radius,interactive,glow,as. One.ca-surfacerule reads--ca-surface-*variables.material="auto"follows<html data-style>; the explicit materials look the same under every style. It never setswill-changeorisolation, and it hasforced-colorsand reduced-transparency entries.Card(title,headingLevel,href,flush,full,headerActions) is built onSurface;SurfacePanelis a raised<section>(the AppShell sidebarPanelkeeps its name).- Docs: Surface, Card, Layout primitives;
docs/css-api.mdlists the newca-*classes and the data attributes.
Behavior changes to review in
Stack,GridandRow(they were the only layout components with inlinestyle):StackandGridgapis now a spacing step string (gap="4"is 16px), not pixels.align/justifyusestart | center | end | between | ...instead of raw CSS values.Gridcolsis nowcolumns(1-12, or a responsive object);rowGap,colGapand CSS template strings ("2fr 1fr",repeat(auto-fill, ...)) are gone: writegrid-template-columnsin a class.Rowis deprecated in favor ofFlex. It still renders.ca-rowand maps a pixelgapto the matching step.GlassCardandGlassSurfaceare deprecated in favor ofCardandSurface, and removed in the next major. They render the new components, keep theirca-glass-card*andglass*classes and look the same under everydata-style, and log a[ca-ui]warning in development.
Size:
styles/core.cssgrows from 9.6 to 13.0 kB gzip (limit 9.6 to 13.6 kB),global.cssfrom 12.6 to 15.9 kB:layout.cssis ~2.2 kB (every token prop and step at the base and four breakpoints, generated byscripts/build-layout.ts) andsurface.css~1.2 kB (the five materials by six elevations). JS entries are unchanged.Migration:
bunx @codeatlasdev/ui migrate --write srcrewritesGlassCardtoCard material="glass"(thicknesstoelevation,tintedtotone="brand",glowingtoglow, staticpaddingtop),GlassSurfacetoSurface(refractiontomaterial="liquid"),RowtoFlex, andStack/Gridpixel gaps,colsandalign/justifyvalues. Dynamic or off-scale usages are left as they are, with their import, for you to finish. - Layout primitives take token props (
-
49b2bf4: Unified layer engine. Dialog, AlertDialog, Sheet, Drawer, Lightbox, CommandPalette, DocsSearch, DropdownMenu, Popover, Tooltip, Select, Combobox, NotificationBell and the date pickers now share one layer stack (
primitives/layer.ts).- Modals are native
<dialog>+showModal(); menus, popovers and tooltips arepopoverelements. Both live in the top layer, so stacking no longer depends onz-index: a menu inside a dialog is always on top. The--ca-z-dropdownand--ca-z-tooltiptokens remain for your own in-flow UI but are no longer used by overlays. - Anchored layers use CSS anchor positioning when supported and Floating UI otherwise.
- One Escape / outside-press / focus stack: only the topmost layer reacts, so nested overlays dismiss in order. Focus returns to the trigger on close.
- New
onLayerChange(listener)(and alayerchangeCustomEventondocument) fires on every layer open and close with the open layers and their rectangles, so hosts can park or snapshot native views. - The
.ca-floatingwrapperdivis gone:.ca-floatingis now on the layer element itself (the one carryingdata-state/data-side), and.ca-date__floatwas removed.
- Modals are native
Install
bun add @codeatlasdev/ui@3.0.0Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛
@codeatlasdev/ui@2.3.2
Patch Changes
- 9b7015f: DocsShell: the sidebar keeps its place between pages. On a static docs site every sidebar link loads a new document, and the sidebar used to jump back to the top, hiding the page just picked (#99). The scroll position is now saved per section in
sessionStorage(on a sidebar click and onpagehide) and restored by an inline script emitted right after the sidebar, before the first paint; the current page is scrolled into the sidebar's view when it is outside it. Only the sidebar scrolls: the window and focus are untouched. Client-only mounts and pages whose CSP blocks inline scripts get the same restore on hydration.
Install
bun add @codeatlasdev/ui@2.3.2Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛