Repository navigation
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). They set the tokens--ca-dialog-split-width,--ca-dialog-split-heightand--ca-dialog-split-columns. Defaults are unchanged. The insertion-line-free column drop requested in the same issue already ships assortable({ drop: 'container' }); it now has a test for the announced target container on keyboard and touch. -
ca5cc0e: Display text role (#350). New
Headingcomponent (level,size="display" | "display-2" | "display-3" | "h1".."h6") bound to--ca-font-display(the body font untilfonts.displayis set), the new--ca-display-tracking/--ca-display-leadingtokens and the fluid--ca-font-size-display-*sizes.AuthCard,PageShell,EmptyStateandSiteHeadertake adisplayprop.defineTheme({ typography: { displayTracking, displayLeading } })sets the tokens. -
e5c7c0f:
DocsHeaderis the top bar ofDocsShellas its own component, so the pages of a site that are not a docs frame (a landing page, a template gallery) render the same header as the docs: one height, container width, brand position, section tabs with an underline for the current one, search (⌘K), actions and theme toggle, and the same phone menu. The bar is now opaque (it was an 82% translucent fill with a blur), so content scrolled under it no longer shows through. -
4e4bd67:
DrawergetschromelessandfullBleedBelow(#362), mirroringDialog'schromeless.chromelessrenders no header, close button or footer actions and no body padding, so a detail view can bring its own header and scroll area; the panel is still named bytitle(screen readers only),aria-labeloraria-labelledby.fullBleedBelow="sm" | "md" | "lg"makes a left/right panel exactly the viewport width and--ca-vhheight below that breakpoint (no radius, border or page strip, safe-area padding kept)..ca-drawer__bodyis now a flex column withmin-height: 0, so a child can own the scroll. -
2cc7983: Editorial primitives (#353). New tokens
--ca-font-size-display-hero(fluid, 60-216px) and--ca-ease-out-expo/--ca-ease-out-quart/--ca-ease-in-out-quart(also in theeasingexport). NewHighlightandStrikeThroughcomponents withanimated, drawn in their final state underprefers-reduced-motion.Surfacetakesgrain(true | "animated"), a paper-noise overlay tuned by--ca-grain-opacity/--ca-grain-size. Fix:--ca-font-mono(andfontFamily.mono) now starts with the system monospace instead of JetBrains Mono, which the kit does not load; setfonts.monoto pick another. -
446c722:
HoverCard(#454): a rich preview that opens on pointer hover (afteropenDelay) and on keyboard focus (at once), stays open while the pointer crosses into it (closeDelay), closes on Escape and blur, and is reachable with Tab from the trigger. A hint layer: it never takes Escape or presses from the layer below. Touch never opens it, so a tap only runs the trigger's own action (usePopoverfor a tap-to-open preview).triggerChild/trigger,bind:open,onOpenChange,side,align,sideOffset.FloatingLayerforwardsonfocusout. -
c8e9492:
ca-ui-mcpsearches the gallery index.search_uitakes words plus facet filters (kind, subtype, style, motion, interaction, layout, colorMode, density, tech, cost, component),list_facetslists every value with its count for the current selection, andget_relatedreturns the entries related to a demo. They read the same published facets asapi/v1/index.json: a facet still under review is never searchable. -
a5bc3b7:
init --writenow produces a SvelteKit app that passesvite build,svelte-check --fail-on-warningsandcodeatlas-ui lintas written, on Kit 2 and Kit 3.- PWA and SEO are opt-in. The manifest route, service worker, offline page,
robots.txtandsitemap.xmlare written only withinit --with pwa(also combinable:--with lint,e2e,pwa). A plaininitwrites nomanifestlink in<Seo>(the old default failed the prerender with a 404 as soon as the manifest route was removed).--with pwacan be added later to an appinitalready set up. - Lint-clean output. The copy
initwrites (layout title, offline page, manifest name) lives insrc/lib/messages.ts(export const m), socodeatlas-ui lintno longer reportsi18n-literalon a freshly initialised app. - Kit 3
#lib/*map.initadds"imports": { "#lib/*": "./src/lib/*" }topackage.json(never replacing an existing map) and imports the theme and messages through it, instead of relative paths. - Missing config files are created:
vite.config.ts,src/app.html(without the inline-styleddisplay: contentswrapper) andtsconfig.jsoninstead ofskip ... (not found). Kit 2'stsconfig.jsongetsallowImportingTsExtensions,noEmitandskipLibCheck, which its generated base config lacks; an existing file only gains the options it does not set. @sveltejs/adapter-static: the adapter output folder (build, or thepagesoption) goes into the tsconfigexclude, sosvelte-checkno longer scans it, andsrc/routes/+layout.tsgetsexport const prerender = true;.initnotes when the adapter major does not pair with the Kit major (@sveltejs/adapter-static@4for Kit 3,@3for Kit 2).prerender.handleUnseenRoutes: "ignore"is written into thevite.config.tsthat--with pwacreates on Kit 3 (no page links to/robots.txtor/sitemap.xml), and noted otherwise.- Fixed the public-origin note. Kit 2 rejects
kit.paths.origin; the mechanism iskit.prerender.originthere andsveltekit({ paths: { origin } })on Kit 3. - A re-run no longer mistakes the theme import
initwrote intovite.config.tsfor an app-owned theme.
- PWA and SEO are opt-in. The manifest route, service worker, offline page,
-
a73cb68:
inlineonDialog,Sheet,Drawer,Popover,DropdownMenuandToasterrenders the overlay in place instead of the top layer:position: absoluteinside its container, never moved to<body>, noshowModal()/showPopover(), no scroll lock, no focus trap, no inert page, no global Escape or outside-press ownership (Escape closes it from inside). It resolves the tokens of its container, so it follows adata-presetordata-theme-scopeisland.openLayertakes the sameinlineoption. The Theme Studio overlays screen now shows the real components instead of static replicas. -
d101e43:
TextFieldaddons, the InputGroup (#447, part of #432):prefixandsuffixput text or aButton/IconButton/select inside the field border, flush with it and set apart by a divider. A string addon is read as part of the field's description and focuses the input on click; a snippet addon keeps its own name and Tab stop. The focus ring wraps the whole group;error,disabledandsize="sm"reach the addons;--ca-tf-addon-bgsets the fill..ca-tf__addonclasses documented indocs/css-api.md. -
1263da3: New
Kanbanboard (#361) on top ofuseDnd: columns with a fixed header (colour rail, title, count), independently scrolling card lists, a board that pans with the mouse and fades only where content is hidden, per-columnonloadmorewith skeleton cards and a "Load more (N)" fallback, a mobile tab strip (one column at a time) and a "Move to…" menu next to every card. Mouse, pen, touch and keyboard moves are announced. The engine gainssortable({ drop: "container" })(column highlight, no insertion line,to.indexalways 0) anddnd.scroller()(horizontal board auto-scroll, faster the closer the pointer is to the edge); Left/Right move a card to the same index of the neighbouring column, an empty column included. -
46129ad:
Kanbantakes asummarysnippet that renders per-column content (a money total, a weight) after the count while the colour rail, title and count stay; on the mobile tab layout it shows as a row above the active column's cards.headerstill replaces the whole title row. The kanban docs show a per-column money total and a board-height parent without:global. -
62a8d05:
KanbantakesheadingLevel(2–6, likeEmptyStateandFormSection) for its column titles (#501). The default level changes from<h3>to<h2>(inside aPageShell, the page's section level), so a board under the page<h1>no longer skips a level and passes axeheading-order. A board under an<h2>of its own passesheadingLevel={3}to keep the old markup. Only the tag changes, never the look; a customheadersnippet and the mobile tab layout (no column headings) are unaffected. -
f6085f9:
KpiCardtakes achartsnippet (aSparklineunder the number, next to the icon) and ahintline, so an icon + sparkline tile needs noCard+Stat+Sparklinecomposition. The chart is a snippet rather than a data prop to keep the/chartssubpath andechartsout of the core entry. New part:.ca-kpi__chart. -
25bd205: New
layersstore:layers.modalOpenis reactive and true while a modal layer (Dialog, AlertDialog, Drawer, Sheet) is open;layers.blocks(trigger)is the same but false for a trigger inside the topmost modal.Tooltip,HoverCardandNavigationMenuItemno longer open on pointer movement while a modal is open (unless their trigger is inside it), so a hover panel can no longer appear under a dialog. Gate your own hover or edge UI with the store. -
d55e2a5: New
LocaleSwitcher: a compact language selector for public headers. Options are data ({ value, label, short?, lang? }), each named in its own language with alangattribute per item;onselect(locale)may return a promise (the switcher is busy until it settles,valuechanges only on success,onerroron failure); the trigger shows the native name from 600px and a short code below (or always withcompact). Compose it in thetrailingslot ofSiteHeader, now documented as the end of the bar. New i18n grouplocaleSwitcher(en, pt-BR) (#349). -
c172f2c: New optional entry
@codeatlasdev/ui/mapfor map products.MapStagewrapsmaplibre-gl(default) ormapbox-gland keeps the basemap in step with the theme (styles: { light, dark }, swapped withsetStyleon the same map), draws deck.gllayersinterleaved throughMapboxOverlay, fits bounds withpadding, and turnsflyTointojumpTounderprefers-reduced-motion.MapControlsadds glass zoom, compass (and optional geolocate) buttons with 44px touch targets.tokenColor(token, alpha?),mapColors(),useMapTheme()andrgbaCss()resolveoklch()/color-mix()tokens to[r, g, b, a]through a canvas, cached by resolved value so a theme change needs no manual reset. All engines are optional peers loaded insideonMount, so the stage renders in SSR. Pin@deck.gl/*to~9.4. -
8607da1:
MenubarandMenubarMenu(#456): the WAI-ARIA menubar for application menus. One tab stop; Left/Right/Home/End move between bar items (disabled skipped, wraps); Enter, Space or Down open a menu on its first item, Up on its last; inside an open menu Left/Right switch to the adjacent menu, while a submenu keeps its own arrows; Escape returns focus to the bar item; hover switches menus only once one is open. Menu content is theDropdownMenuitems unchanged.DropdownMenugainsonmenukeydown,ontriggerkeydownandopenFocus. -
abb6269: Migration tooling per major (#382).
migrations/<major>.jsonis the machine-readable record of every breaking change from 1.0.0 to 4.0.0 (before, after, why, and whethermigratecovers it:yes,partial,no); it generatesMIGRATION.md,dist/migrations.jsonand a new MCP toolget_migration(from, to).codeatlas-ui migrate --from 2 --to 4 [--write] [--report json|md] [--strict]runs only the codemods of the majors in that range, lists what it could not rewrite with file:line (Gridcolstemplate strings,.ca-overlayselectors,setLocale, abits-uiimport, ...) and the changes left to review by hand, and exits 1 under--strict. It no longer answers a bare "Nothing to migrate": it names the codemods it checked. New codemodselectable-to-ca-selectable(4.0.0:class="selectable",class:selectable,.selectablein a style block). The dev warnings ofGlassCard,GlassSurface,RowandsetLocaleon the server cite their migration id. A major changeset must now carry aMigration: <id>line naming an existing entry, and a test fails otherwise.NavModuleisbasePath/itemsin every release from 1.0.0 on: thepath/sectionsshape in the old skill examples never shipped, so there is nothing to migrate. -
043c32e: Migration tooling for teams moving an existing app onto the library (#383). New commands of
codeatlas-ui:tokens import <css|json>turns the custom properties of an existing design system (hex,rgb(),hsl()oroklch(), with or without alpha, one rule per theme) into a typedtheme.tswith a preset per theme, composites every translucent token over its background and prints each token it flattened and each text or outline pair under 4.5:1 or 3:1 (--strictexits 1);doctorchecks peer versions, optional peers, theme wiring, viewport meta, a rootfont-size,TODO(lib#n)markers and.ca-*selectors, one line per finding with a fix hint, and--fixrenames the markersworkaroundsreads;lintis a UI guard that reads.svelte,.astroand.css(no hex, raw px, inline style,:global(.ca-*)outsideui-guard.config.json, literal text in templates);compare <urlA> <urlB>opens two pages in Chromium and prints the elements whose box, computed styles or pixels differ, sorted by deviation.init --with lint,e2ewrites the guard scripts and a Playwright config with a 390/768/1280 matrix and an axe spec.migraterenames the pre-1.0NavModulekeyspath/sectionstobasePath/items(registry entryv1-navmodule-basepath-items).@codeatlasdev/ui/testinggainswithTheme({ scheme, preset, style }),coarsePointer()andmeasure(el, props). -
330d761:
NavigationMenu,NavigationMenuItemandNavigationMenuLink(#457): disclosure navigation for marketing headers, notrole="menu". Anavwith a list of links andaria-expanded/aria-controlsbuttons that reveal a panel of links; one tab stop with Left/Right/Home/End across the top level, Enter/Space toggle, Down opens into the panel, Tab flows through the panel links and on, Escape returns focus to the button. Hover opens afteropenDelay, switches at once while one is open and closes aftercloseDelay; a click toggles (touch).currentsetsaria-current="page";descriptionadds a second line to a panel link. -
98a7396: PageFrame: page container for map-first apps.
modemap | split | page,side,widthsm | md | lg | xl, a non-modal bottom sheet below 900px withpeek | half | fulldetents (drag or keyboard),padded,headerBorderand fixedheader/toolbar/footerslots. NewstageInsetsstore (and--ca-stage-inset-*on<html>) publishes the rectangle the frame covers so the stage can keep pins and routes clear of it;mode="page"setsdata-ca-stage="hidden"on<html>(#344). -
cbc214c: PageShell: new
asidesnippet (companion column, sticky beside the content from a 60rem-wide shell, after it below) withasideLabel, andwidth="reading" | "wide"to cap the content column at 46rem. AppShellLayout: newcollapseShortPanelprop collapses the panel to the rail for modules with 2 destinations or fewer (#342). -
539c4be: Preset role colors take alpha, and a preset carries domain tokens. A role in
colors.<scheme>is any CSS color (oklch(1 0 0 / 0.62),rgb(),color-mix(…)) or{ color, alpha }, so a translucent text or border follows the surface behind it instead of being flattened to a hex; a value that is not one CSS color throws at build time, andcaThemePlugin/caUIwarn when a hand-settext,textSecondaryortextHintis under 4.5:1 after compositing its alpha.PresetDefinition.tokens({ status: { new: { light, dark } } }) emits--ca-token-status-newplus derived-subtleand-contrastper preset and scheme, with names typed through theCaTokenRegistryaugmentation.tokenRgba("status.new")andonTokensChange(fn)(@codeatlasdev/ui/tokens) read any token as[r, g, b, a]within 1/255 of the CSS value, translucent colors included; the map entry'stokenColornow shares that resolver.#rrggbbpresets behave as before. -
5eebf5e: Theme presets can own the primary-button and field roles:
colors.<scheme>.brandFill,brandFillHoverandbrandContrast(--ca-brand-fill,--ca-brand-fill-hover,--ca-brand-contrast) andsizes.inputFontSize(--ca-input-font-size, stillmax(16px, …)on touch and below 900px). They are emitted with the same selectors as the other preset roles, soButton variant="filled"inside[data-preset="x"]needs no app CSS.caThemePluginwarns whenbrandContrastonbrandFillis under 4.5:1 (caUIfor Astro does too) (#354). -
70cce85:
PreviewStage: a resizable device frame for showcasing a component or a whole page at 390, 768, 1280 or any width. A segmented width control and a keyboard-operable splitter set the frame width; a frame wider than the stage is scaled whole. Inline content is themed in its ownThemeIsland(scheme,preset);srcshows a same-origin page in an isolated iframe. -
c99fb14:
Progressgetssize="sm" | "md" | "lg"(4px, 6px, 8px; the default stays 8px) and honours a--ca-progress-heightcustom property for any other height (#358). Determinate and indeterminate bars both follow it and the ends stay pill-shaped. -
2ff23ff:
AppShellLayoutnavigation for apps with many modules (#346):railLabelscaptions under rail icons, arailTopslot for a search / ⌘K trigger,NavModule.badge(number or'dot') withbadgeLabelfolded into the button's accessible name, a bottom navigation that keepsbottomNavPrimarytabs and moves the rest into a "More" dialog by default (native<dialog>,bottomNavOverflowslot; opt-inmoreSheet={NavMoreSheet}swaps in a fullSheet), andfilterModulesfor role-based navigation (the active module is resolved on the filtered list).Railtakestopandlabels,BottomNavtakesprimaryandoverflow. -
043c32e: Migrating a React app is now data, not prose (#383).
equivalents/react-ui.jsonmaps every shadcn/ui component (66) to the Radix primitive, Base UI package and MUI component that mean the same thing, the@codeatlasdev/uitarget with its import path and prop renames, and, for the nine components the library lacks, the gap issue (#432) and a workaround. It is served by the new MCP toolget_equivalent("Dialog"), shipped asdist/equivalents.json, and generated into the migration guide, so the page cannot drift from the data.codeatlas-ui migrate-reactlists every shadcn, Radix, Base UI and MUI import of a.tsxapp with its replacement, and in an already-ported.sveltefile renamesclassName,onClick,htmlForand the shadcnButtonvariants, reporting with file:line the props it cannot rewrite safely (onChange,onCheckedChange,asChild, spreads). The guide gains a Tailwind map and the React patterns that change shape (context, data fetching, routing, forms, SSR global state, per-request i18n), each with a type-checked Svelte block. -
5b50d09: The reset layer (
@layer ca.reset) now gives every interactive shape a pointer cursor:button,a[href],label[for],summary,[role=button],[role=tab],[role=option]and[role=menuitem*].:disabledand[aria-disabled=true]getnot-allowed. A custom clickable tile no longer shows the arrow cursor; an app overrides the rule with an unlayeredcursordeclaration, no!important. The stylesheet drops the now redundantcursor: pointerof.glass-btn*and.ca-rail-btn, the unused.ca-bar__actionsrule and the unused--ca-grid-columnstoken to stay inside its 17250 B budget. -
5931a06:
ResizablePanels,ResizablePanelandResizableHandle(#444, part of #432): panels split by WAI-ARIA window splitters. Sizes are percentages that add up to 100; a handle trades only with its two neighbours and stops at each panel'sminSize/maxSize. Arrow keys move 1%, Shift 10%, Home and End go to the limits (Up and Down in adirection="vertical"group, mirrored in RTL).persistkeeps the layout inlocalStorageand ignores a stored layout that does not match the panels;onlayoutreports it. Replacesreact-resizable-panels/ shadcnResizable. New i18n groupresizablePanels(en, pt-BR). -
194723a:
AppShellLayoutstagesnippet (#343): a persistent background layer (map, canvas, video) rendered once behind the pages, so it keeps its state across route changes. The content region passes pointer events through (panels opt back in with.ca-stage-panel), the shell paints nothing behind it, andstageVisible={false}ordata-ca-stage="hidden"on<html>hides and pauses it without unmounting. The shell exposes--ca-shell-inset-top|right|bottom|left(rail and panel, mobile header, bottom navigation and safe area). -
b46a5f7:
SimpleTablegets a dense, spreadsheet-like mode (#360):density="compact"(rows up to 40px),frame={false},scroll="none"(the caller owns scrolling, so the sticky header sticks to your page or panel),stickyColumns={n}(pinned columns keep an opaque background in hover, selected and focus states) andheaderCase="sentence". NewTableRow(selected,onactivate: Tab, Enter/Space and click, nested controls never bubble),TableGroupRow(collapsible,aria-expanded, sticky label) andTableSortHeader(sort,onsort,aria-sort). -
b8edc32: New optional entry
@codeatlasdev/ui/sound: opt-in, synthesized interface sound with no audio files (under 4 KB gzip). One sharedAudioContextis created only inside the first pointer or key event after the user turns sound on with<SoundToggle />(aria-pressed, accessible name, optional volume slider); the choice persists inlocalStoragekeyca-soundand sound is off by default. Every cue is a short oscillator and noise recipe behind four profiles (system,glass,brutal,terminal) that follow the design style through--ca-sound-profile(written bydesignStyle()), tunable with--ca-sound-gain,--ca-sound-pitchand--ca-sound-brightness. Hover and slider tick cues are off underprefers-reduced-motion; sound is suspended while the tab is hidden or media plays, with one cue per name per 40 ms and at most four voices. Button, Switch, Toast (toast.*(msg, { sound })), Dialog, Tabs and Slider take asoundprop (boolean | CueName) that emits throughsoundBus, a no-op hook exported from the root, so core stays silent without the entry.use:sound(thesoundexport is also the action) plays a cue on any DOM event. Docs: Feedback > Sound, and the Sound principles guide. -
b476462: Official list of stable override hooks (#381). The manifest gets a
stylingsection per component (components.json): stableca-*parts, internal parts (ca-x-*classes and@keyframesnames, which can change in any release), component-scoped--ca-*variables and thedata-*attributes its CSS selects on. It is surfaced inAPI.md, indist/parts.jsonand in a new MCP toollist_parts;get_componentreturns it too.codeatlas-ui workaroundsnow also scans.css,.svelteand.astrofor.ca-*selectors that are not a stable part, and--strictexits 1 on them (aca-ui-workaround(#n)marker on the line or the one above excuses it). A test fails when a documented part is no longer applied by any markup; it found and removed the dead.ca-date__seprule. -
3b50a5c: New
Stat(#359): a card-less statistic withlabel,value,unit,hint,trend,size(sm | md | lg),alignandskeleton. It renders a description list (label<dt>, value<dd>), has no surface, border or padding, uses tabular numerals, truncates the label and never wraps the value.KpiCardis now aStatinside aCard, so both share one typography: the doubled inner padding (card padding +.ca-kpi__innerpadding) is gone, and the unused.ca-kpi__topand.ca-kpi__value-rowwrapper classes were removed;.ca-kpi__title,__value,__unit,__trend(--up/--down)and__iconstay. -
b34ffd4:
StatandKpiCardtaketrendTone('positive-up' | 'positive-down' | 'neutral') so a trend can be good news when it falls (churn, cost, latency). The arrow still follows the sign, the colour follows the tone (hook:data-tone="good|bad|neutral"on.ca-stat__trend) and the accessible label says better or worse (Trend: -1.8%, better,Tendência: -1.8%, melhor). Without a tone nothing changes. Apps that droppedtrendand wrote the delta inhintcan pass it again. -
7bc89c7:
TextField:revealableadds a show/hide eye button totype="password"(aria-pressed, localized name, 44px hit area on touch, no layout shift),belowrenders a snippet under the field and its hint or error (a "Forgot password?" link, a strength meter) andrequiredIndicator={false}hides the asterisk of arequiredfield. New i18n grouptextField(en, pt-BR) (#348). -
3aacf62:
ThemeIsland: a block or a whole page with its own preset and scheme.documentalso mirrorsdata-preset,data-theme-scopeand thedarkclass onto<html>while mounted (sohtml/body, overscroll andcolor-schemematch) and restores the app's values on unmount, with overlapping islands and theme-store writes handled. SSR parity:setRouteTheme(event, { preset, scheme })in aload, orcaThemeHandle(theme, { routes })by route id prefix, writes the island on<html>in the server response, and the init script and the Astro<ClientRouter />swap keep the app's own preset aside for the navigation back. The docs page explains how nested presets anddata-theme-scoperesolve (#352). -
c39a426: New
ColorField(swatch, native picker,#hex/rgb()/oklch()entry and a live WCAG contrast readout) andSliderField(label, slider and an editable number with a unit).Slidertakes alabelso a thumb is announced as "Radius" instead of "Value 40". The color helpers behind them are public:parseColorInput,contrast,wcagLevel,toHex,toOklch,formatOklch,extractPaletteandpickBrand(derive a brand from the pixels of a logo), pluspresetContrastIssuesandpresetTokensfor theme editors. -
37cda52: toast: optional
description(secondary line) and a single inlineaction: { label, onClick }ontoast.showand thesuccess/error/warning/infoshortcuts, which now also take the options object as the second argument (positionaldurationstill works). Clicking the action runs it and dismisses the toast; an actionable toast lasts 8 s by default. The auto-dismiss timer pauses while the toast is hovered or focused and resumes with the time left (toast.pause(id)/toast.resume(id)), and Escape dismisses the focused toast. Errors and warnings are nowrole="alert", the others stayrole="status". The action is a text button with a 44px touch target on touch devices. -
15074bf: A named type scale by role.
title,heading,body,label,captionandnumare size / leading / tracking / weight bundles (--ca-text-<role>-size|leading|tracking|weight, and--ca-text-<role>as afontshorthand), applied with<Text role>or.ca-text-<role>;numrenders tabular digits so a ticking counter never shifts its neighbours.ThemeOptions.type = { label: { size: "13px", leading: 1.25, tracking: "0", weight: 500 } }overrides any part for the whole app, library components included:--ca-font-size-label,-body1,-captionand--ca-label-trackingare now derived from the roles, with the same default values, so existing components follow without edits. Unknown roles and values that are not a length, number or keyword throw at build time.
Patch Changes
-
817b5c4:
get_recipe,llms.txtand the decision tables now teach the 4.6 APIs: a map-first recipe (AppShellLayoutstage,MapStage,PageFrame), rail labels and the "More" sheet,ChipGroup,Stat, dense tables,Kanban,Drawer chromeless, toast actions,tone="neutral",ThemeIslandand per-preset button colors. The "common workarounds" page lists what each one replaces. -
caf1fa7:
AppListis generic:Tis inferred fromdata, so thelistof itsfilters,childrenandemptysnippets is aUseListStateReturn<T>(rows are typed,searchKeysandidKeyare checked againstT) instead ofany, and a snippet annotated withUseListStateReturn<User>type-checks. The recipes now keep app copy in a typedsrc/lib/messages.ts, so they passcodeatlas-ui lint; the new App copy page explains thei18n-literalrule and that pattern. -
7667a22:
AppShellLayout: only the most specific nav item is active. With items at/itemsand/items/a, visiting/items/amarks justItem A(aria-current, active style, section auto-open) and the mobile header title is its label, no longer the overview's. Same rule as modules: longest path wins whatever the order. The overview item stays active on its other sub-paths.findActiveItemPathfollows it and the newfindActiveItem(entries, pathname)returns the item itself. -
2ccbb9f:
caUI(Astro) now puts the theme first in<head>, ahead of every stylesheet. It usedinjectScript("head-inline"), which Astro renders AFTER every bundled<style>: withinlineStylesheets: "always"the init script sat behind hundreds of KB of CSS and ran only once the browser had parsed it, so a slow link could paint the wrong theme first. A middleware now rewrites the rendered page, at build time and in SSR and dev alike, so the order holds whatever the layout or the bundler does. Font preloads move ahead of the CSS too.caUI(theme, { head })takes extra first-paint markup (a boot script) that lands right after the theme.caThemeHandleandcaThemePluginalready emitted at the start of<head>. -
6d65542:
AuthLayout: abrandsnippet now replaces the whole logo. The default "CodeAtlas UI" name no longer renders beside it, in the side panel or the mobile strip; passlogoNameto show a name next to your brand (#340). -
06fb8aa: AuthLayout: on a flat panel (
panelTone="flat", orautounder theflatandoutlinedsurface styles)--panel-text-secondaryand--panel-text-tertiaryare the theme's--ca-text-secondaryand--ca-text-hintinstead of alpha mixes of the foreground, which measured 4.39:1 and 2.4:1 in dark and 2.21:1 in light over the raised surface. The brand gradient keeps the alpha levels. -
2b04e94:
AuthLayout: the compact mobile brand mark now sits on--ca-brand-fill(the surface--ca-brand-contrastis paired with) instead of--ca-brand-main, so its label holds 4.5:1 in dark on every preset. It failed color-contrast in dark at 390px on six of the eight built-in presets. -
1cf8c69:
--ca-brand-textis now solved per preset and scheme instead of aliasing--ca-brand-dark/--ca-brand-light. A light brand (lime, yellow, light cyan) no longer gets a 1.6:1 label on outlined and ghost buttons, links and active nav: the shade is darkened (light scheme) or lifted (dark scheme) along the brand hue until it holds 4.5:1 on every surface and on the hover/selected fills. Built-in presets keep their light value; dark values lift slightly where they were under 4.5:1.tests/contrast.test.tsmeasures every preset, light brands andcolors.<scheme>.brand. -
35b5c9b:
<Button type="submit">and<Button type="submit" form="id">type-check in apps withoutstrict(the SvelteKit 2 scaffold): svelte-check widens a statictype="submit"tostring, which matched neither branch of the Button props union (formwasnever). The button branch now accepts the widenedstring. Documented on Button and Dialog: a submit button in a Dialogactionsfooter names its form withform. -
ad005c0:
CardandGlassCard: the header no longer adds its own padding on top of the card padding, so the title shares the left edge and top spacing of the content (#341).flush/padding="none"cards keep the header inset. NewCardpropgap('sm' | 'md' | 'lg' | length | number) stacks several inline controls (Checkbox, Switch, TextField) in a column. -
419d775:
Carousel: the slide strip is now the focusable region (axescrollable-region-focusable; arrows, Home and End work while it has focus, also when no slide holds a control), it no longer widens a flex or grid slot to the slides' width, and previous / next / dots reach the 44px touch target.Menubaritems get the 44px hit area under a coarse pointer.TextFieldcan shrink inside a grid or flex slot narrower than the input's intrinsic width (addon fields overflowed phones). -
c39a426: Charts follow a theme scope. They read their colors with
getComputedStyleon<html>, so a chart inside aThemeIslandor anydata-preset/data-theme-scopeelement painted the page's colors, and its series palette ignored the island's preset.ThemeIslandnow provides its element as the scope, and the newChartScope(from@codeatlasdev/ui/charts) does the same for your own scoped containers. Without a scope nothing changes. -
73f38cd:
CodeSnippethighlights any language shiki bundles (markdown,yaml,diff, ...), loading it on first use, and falls back to plain text for a name it does not know. Before, a language outside the eight preloaded ones made the highlighter throw inside the promise and the snippet stayed on its unhighlighted fallback for good. -
73f38cd:
CodeSnippet: a code block that scrolls sideways is keyboard-reachable. The raw fallback shown before the highlighter loads (and the highlighted block) now usescrollFocusable, so a long line no longer leaves a scroll region that only a mouse can reach (axescrollable-region-focusable). -
475158b: The stylesheet drops 12 utility classes that no component, template or documented API ever used:
.glass-bg-extend,.glass-inner-sm|md|lg,.glass-prominent,.glass-scroll-edge-top|bottom,.ca-toolbar-sep,.ca-clear-btn,.ca-row-check,.ca-trend-up|down.styles/global.cssshrinks by 242 B gzip (17458 B to 17216 B) and its size budget goes back from 17500 B to 17250 B. An app that styled an element with one of these classes keeps the rule in its own CSS. -
6aafbb5: A page with no
data-styleon<html>now renders asdata-style="solid", the package default: no backdrop blur, opaque surfaces, no glow, the pulsing skeleton. Before, an app without the theme plugin (nothemeScript, nocaUI) kept the glass tokens, so everydata-ca-blurelement (chips, tabs, toasts, rail, panels) blurred. Glass, frosted and the other styles are unchanged: setdata-styleto opt in. -
ce8ed51: Docs, skill, AGENTS.md, README, MCP
list_surfacesand component JSDoc present the library as theme-driven withstyleas one knob (defaultsolid) and glass as an opt-in style. Glass-only rules (backdrop-filter,data-ca-blur,will-change) are scoped to a "Glass and frosted styles" section with their wording unchanged. Examples, templates and demo specs no longer pinmaterial="glass". Theliquid-glassnpm keyword is replaced bytheminganddesign-tokens. No API changes. -
866912f:
FileUpload: the drop zone's accessible name now includes the visible hint (aria-labelledbyover the label and the hint), fixing the axelabel-content-name-mismatch/ WCAG 2.5.3 failure (#490). With a customchildrensnippet the zone keepsaria-label. -
fbc0d75: The gallery filters by facet. Chips for kind, subtype, style, motion, interaction, layout, colour mode, density, tech and cost narrow the cards instantly, show how many entries each value would match, and drop combinations that match nothing. Words match titles, aliases, tags and components. Facets, words and the group live in the URL (
?kind=motion&style=aurora&q=fade), so every filter is a shareable link. Only published facets are offered. -
99a3dbd: A gallery item opens on Preview, Code, Prompt and Anatomy tabs (
?tab=prompt,?tab=anatomy). Prompt is the text to hand an AI: theget_democall, the library parts, the behaviour to keep, the exact motion timings and the reduced-motion and keyboard promises read from the index. Anatomy lists the components with their package and docs link, how the demo is built, and related items. Pages show Preview and Prompt. -
678d673:
init --writeon SvelteKit 2 with@sveltejs/adapter-staticcreatessvelte.config.jswith the adapter andvitePreprocess, or addsadapter: adapter()and its import to an existing config that names no adapter. Before,vite buildran with no adapter and nobuild/output. A config that already setsadapteris untouched; Kit 3 is unchanged (the adapter goes intovite.config). -
7fe318d:
initon SvelteKit adds"prepare": "svelte-kit sync"topackage.jsonwhen the app has nopreparescript, sosvelte-checkon a fresh clone no longer stops withFile '$app/tsconfig' not found.src/lib/messages.tscarries onlyappTitleunless--with pwais given: theofflinekeys are written (or added to the fileinitwrote) only with the offline page that reads them. -
b8e217e: Surfaces and chips inside a
ThemeIsland(and aPreviewStagewith aschemeorpreset) follow the island, not the page around it. The layer tokens (--ca-overlay-bg,--ca-panel-bg,--ca-chrome-bg,--ca-upload-bg) and the solid style's tint overrides are re-declared on[data-theme-scope], so a dark island on a light page no longer inherits the page's resolved light surface (aFileUploadzone, anAppFormfooter) under dark text. Thesuccess,error,warningandinfochip text follows[data-theme-scope]as well as.dark. -
fe9c5d3:
Kanbanno longer collapses to 0px wide inside an auto-width flex wrapper (a wrapping flex row, an island): the size-contained board now has a one-column floor width (contain-intrinsic-inline-size), while a narrower parent still shrinks it and the columns never widen the page (#530). -
7873a89: The Landing page recipe links only to in-page anchors and to
/sign-inand/sign-up(the Auth pages recipe), so copying it whole prerenders withadapter-static; before,/pricing,/docsand/contactfailed the build with a 404. -
85086c5: The Landing page recipe now prerenders when copied on its own: the sign-up and sign-in buttons and the plan CTAs use
signUp/signInconstants at the top of the page that are in-page anchors (#pricing), instead of linking to/sign-upand/sign-in, which 404 the prerender unless the Auth pages recipe is also in the app. Point the constants at the real routes once they exist. -
82edb9d: The close button of
Dialog,AlertDialog,DrawerandSheet(.ca-layer-close, drawn 28px) gets the invisible 44px hit area under a coarse pointer, like every other small control. It was missing from the touch list, so an overlay measured 27 by 27 on a phone, inline overlays included. -
b8edc32: A tooltip or hover card that was open when its trigger opened a dialog no longer leaves a second, stray copy in the page after the dialog closes. The layer stack put a hosted hint back at its old position even when the dialog had already been removed with the hint inside, resurrecting a node its owner had dropped (no
popover, in normal flow, sameid). -
503a842:
init --writeadds the"imports": { "#lib/*": "./src/lib/*" }map topackage.jsonand writes#lib/theme.ts/#lib/messages.tsimports on SvelteKit 2 as well as 3 (Kit 2's Vite and TypeScript resolve the subpath import, verified by building and type-checking a Kit 2 app). The recipes, app-copy, installation and PWA pages now use the one spelling#lib/...tson both majors instead of telling Kit 2 users to rewrite every import to$lib. An existing$libimport on Kit 2 keeps working and is never rewritten. -
9e751a2:
llms.txtand theAGENTS.mdblock written byinitsay thatdist/components.jsonkeeps the components under.components[]and every other export under.exports[]. -
3b76318:
llms-full.txtkeeps only the props table of each non-core page. The summary also carried the prose and examples that follow a table (the SEO page alone added 9 KB), which pushed the file past its size cap once the Concepts pages were added; the descriptions stay in each page's own file. -
ce45681:
llms.txtlinks the agent-readable.txtform of every docs page (/llms/<group>/<slug>.txt, index pages/llms/<slug>.txt) and of each recipe (/llms/recipes/<id>.txt), with the HTML URL beside it, and states the fetch rule at the top.dist/components.json(andget_component) now list the layout token props ofFlex,Stack,Grid,Box,Container,SectionandSurface(gap,align,justify,direction,wrap,columns,p,px,py) from theirLayoutPropspick. -
b8edc32:
LoadingBoundaryholds the placeholder for the fullminDuration: the hold re-checks the clock when its timer fires and re-arms for the remainder, instead of ending up to a millisecond early when a timer fires ahead ofperformance.now(). -
de1c873:
components.json(exports[].members),API.mdand the MCPget_exportnow list the fields of every exported interface (NavModule,NavItem,NavSection, option and state types) with type, optionality and JSDoc, instead of one collapsed line. -
573950f:
components.json,API.mdand the MCPget_componentspell out the values of a prop typed with a literal-union alias (Avatarshape: 'circle' | 'rounded',size,status,CardheadingLevel: 2 | 3 | 4 | 5 | 6,Buttonvariantand about 100 more) instead of the bare alias name, andAPI.mdkeeps the description of each snippet (SkipLinkchildrenreplaces the label). -
48e1d42:
@codeatlasdev/ui/map:MapStageworks withmaplibre-gl6, so the peer range is^5.0.0 || ^6.0.0again and you can take the fix for the critical attribution-sanitizer XSS inmaplibre-gl6.4.0 and earlier (GHSA-jrc7-96c5-q579, fixed in 6.4.1). Withengine="maplibre"the deck.gllayersnow go throughMapLibreOverlayfrom the new optional peer@deck.gl/maplibre(~9.4.0, which supportsmaplibre-gl4.5.1, 5 and 6) instead ofMapboxOverlay, which throwsCannot read properties of undefined (reading 'height')on every frame there.engine="mapbox"keeps@deck.gl/mapbox. Install@deck.gl/maplibreinstead of@deck.gl/mapboxwhen you usemaplibre-gl. Onmaplibre-gl6 the app must also callsetWorkerUrlonce (Vite:import workerUrl from "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url"), as MapLibre's v6 migration guide requires for every bundler; the Map page shows it. -
b53e1c2:
MapStage: fitting clear of a widePageFrameno longer throwsInvalid LngLat object: (-Infinity, NaN). The stage set the map's padding and passed the same padding tofitBounds, which subtracted it twice and left a wide frame no area; the bounds now fit inside the visible area exactly once (#407). -
0169158:
nativeFeel()no longer forces the arrow cursor on buttons, links, tabs, menu items and options.html[data-ca-native]setcursor: defaultinca.base, which outranks the pointer cursor ofca.reset, so an app shell never showed the hand on clickable controls (nornot-allowedon disabled ones). The rule keeps blocking text selection and the touch callout on chrome; the cursor now comes from the reset layer alone. -
b8edc32:
NavigationMenu: Enter or Space on a trigger always toggles its panel. The click grace that lets a pointer click confirm a hover-opened panel also swallowed a keyboard activation made within 600 ms of a hover open, so a keyboard user could not close the panel. -
2ccbb9f: The theme init script no longer fails with the page. When
localStoragethrows on access (private mode, blocked cookies, a sandboxed iframe) it used to abort before settingdata-preset,data-styleand thedarkclass, so the page painted unthemed and then switched once the store hydrated. It now falls back to the theme's defaults on the first frame, and a failingremoveItemwhile discarding a corrupt value no longer stops the rest of the script. A size-limit entry pins the emitted script (under 1 kB gzip).colorScheme.setTokensnow actually applies in dark mode with a brand preset. Runtime tokens were written as:root{…}(specificity 0,1,0) and lost to the preset's.dark[data-preset]block (0,2,0) whatever the source order, so the stored tokens replayed by the init script, and the ones the store wrote, silently did nothing. Both now use:root:root:root{…}. -
8e950af:
NotificationBell: the trigger's accessible name now starts with the visible unread count (2 unread notifications/2 notificações não lidas, plainNotificationsat zero), so it contains the visible badge text (WCAG 2.5.3, axelabel-content-name-mismatch). CustomnotificationBell.labeloverrides should keep the count in the name. -
ae3b5bd:
PageFrame: on mobile (<=900px) the full-screen panel and the bottom sheet no longer overflow the viewport by the frame gap withside="left". The desktopdata-sideposition rules out-ranked the mobileinset: 0reset, so the panel keptleft: 12pxwithwidth: 100%(right edge 402px on 390px). Fixes #489. -
57ff10e: PageShell: below 900px only
Buttons inactionsstretch across the row.StatusChip,Chip,Badgeand plain text keep their natural width instead of becoming a full-width bar (#339). -
e5cb35d:
PageShellandPageFramewarn once in development when the page has no<main>landmark (nomain/[role=main]ancestor and none in the document), pointing at the Standalone page recipe. Silent insideAppShellLayout,AuthLayout,DocsShellor your own<main>.llms.txtlists Standalone page first under "Start here". -
99ab5db:
.ca-proseand.ca-codefences: thecodeinside apreinherits the pinned--ca-font-monostack instead of the browser's baremonospace, which Chromium could resolve to a different font (and line height) between two paints. -
0168dcf: Theme:
--ca-font-size-body1no longer sets the root font-size.:rootkeeps the kit's rem base (calc(0.9375rem * var(--ca-font-scale)), 15px by default, still relative to the user's browser size), so default rendering is unchanged and overriding body1 only sizes the text onbody. A denser body no longer shrinks labels, inputs, buttons and captions (#351). -
b8e217e:
scrollFocusablere-measures when content is added at any depth, not only as a direct child of the scroller. A fixed-height wrapper never resizes when the content it holds grows, so a lazily loaded demo inside aPreviewStageframe left its scrollable region without a tab stop. -
b8e217e:
PreviewStageframes andResizablePanels that scroll are keyboard-reachable while their content overflows (scrollFocusable, WCAG 2.1.1): a demo taller than the stage, or a narrow panel whose content is wider than it, no longer traps its content out of keyboard reach. Without overflow they add no tab stop. -
b8e217e:
SegmentedControlitems are at least 44px wide as well as tall under a coarse pointer: in a narrow slot the equal shrinkable columns squeezed short labels (a "390" preset) to 38px, under the touch target. -
03c5f65: The Settings page recipe no longer tells a
Switchinside aSettingItemto carry its ownaria-label: the row is a<label>and already names it (as the SettingItem page says), so the redundant attribute and the rule are gone. -
f45caa6:
SiteHeader: aNavigationMenuplaced directly intrailingis hidden below 600px, andtrailingcan shrink (min-width: 0). A marketing bar with three nav links, aLocaleSwitcherand two buttons overflowed the page horizontally at 390px (right edge 437px); the locale switcher and the buttons now stay in the bar and the links belong in a mobile menu of your own (#476). -
169db3e: toast: identical messages no longer stack.
toast.error("x")ten times leaves one toast with a×10badge and a restarted timer, at most three toasts show at once (the oldest is dropped), and{ id }replaces a specific toast in place. The shortcuts (success,error,warning,info) also accept an options object ({ duration, id }) as the second argument; positionaldurationstill works.ToastItemgainscountandToastOptionsis exported. -
9a10232: Touch targets: the password reveal button (
TextField revealable) gets its 44px hit area on touch again. A merge had fused two selectors in the coarse-pointer list (#417). -
b8edc32:
VirtualListno longer drags the user back down after older rows are prepended. A prepend always takes its own scroll anchor, and a restore that was waiting for its tick when the prepend landed no longer applies the anchor it captured against the old offsets (the list jumped to the old first row, about 20 rows down, and skipped the nextonloadmore). -
cb6da91:
VirtualList: a scroll made while the anchor restore after a prepend or a row re-measure is still queued is no longer undone. On a slow machine the late restore dragged the list back to the old anchor, so a user scrolling to the top to load the next page was thrown ~1000px down.
Install
bun add @codeatlasdev/ui@4.6.0Special thanks
To our main supporters and special contributors, Fábio Henrique Araújo (@FabioHAraujo) and Richardson Douglas (@Richards0nd). 💛