Changelog
Public and date-stamped, so you can check how often releases ship before you renew.
0.1.2 (unreleased): Endpoint, Homescreen, Estate, Practice and Stay, templates for developer products, mobile apps, estate agents, practices and places to stay, and thirty sections
- Endpoint, a template for a product with an HTTP API: a console on the home page that sends a request and streams the response back with its status, its time and its region, an API reference written at build from your OpenAPI document, a quickstart and four guides, usage pricing with a slider, a status page, the regions on a map, a changelog with a feed, search and a contact page for sales. Six kinds of product from one source (an email API, serverless Postgres, a search API, feature flags, a media API and a log API), six palettes, three of them dark, and three type pairings, each one line of config.
- Six Pro sections for it: HeroRequest, ApiReference, RegionsMap, StatusBoard, QuickstartSteps and PricingUsage. ApiReference takes an OpenAPI 3.0 or 3.1 document as it is and writes a request in cURL, JavaScript, Python and Go for every operation; the language picked switches every sample on the page. PricingUsage reprices every plan at the usage picked with no script.
- In the free library: Gauge, a ring, an arc or a half circle that fills to a value as it arrives, with its colour picked from bands at build, and Tooltip, a tip on hover, focus or a tap that stays while it is pointed at and closes with Escape (802 B raw of its own).
- Homescreen, a template for a mobile app: a phone beside the store buttons whose screen takes a turn through the app, a tour where the phone stays in view and its screen changes with each feature, the widgets, the lock screen and the watch face, the screens on a ring, the ratings, the plans, what is new by version, support with a form, a privacy page as an outline to write and a press kit whose icon files are drawn at build. Six kinds of app from one source (a habit tracker, a budget app, a language course, a sleep app, a recipe planner and a trip planner), six palettes, one of them dark, and three type pairings.
- Six Pro sections for it: HeroApp, ScreensScroll, WidgetBento, RatingSummary, StoreCta and PressKit. The store buttons are text, since neither store lets its badge be redrawn, with a QR code to the app on a wide screen.
- In the free library: AppScreen, a phone app's screen drawn from rows (a ring, a checklist, bars, a map, a chat, a lock screen's clock and eight more) that a palette recolours and that fills as it arrives, and QrCode, a QR code encoded at build and drawn as one SVG path, with no script. Carousel takes effect="ring": the slides turn on view timelines of their own as the strip scrolls.
- Estate, a template for an estate agency: three homes beside the headline, the homes as cards that filter by kind and by bedrooms with no script, a page per home with its pictures under a magnifier, its floor plan room by room, a calculator for the monthly cost or the money to move in, its agent and a viewing request, the neighbourhoods as panels that widen, and the team. Six kinds of agency from one source (a city agency, prime homes, lettings, a new development, holiday homes and commercial property), six palettes, one of them dark, and three type pairings. Every home is drawn, so a palette recolours the pictures, and its picture travels from its card to its page.
- Seven Pro sections for it: HeroListing, ListingGrid, ListingGallery, FloorPlan, MortgageCalc, AreaPanels and AgentCards. MortgageCalc prints a table of the monthly cost by deposit and term at build, which a reader without JavaScript gets in place of the form.
- In the free library: HomeArt, a house, a terrace, a block of flats, a villa, a cabin, a tower, an office or a shop drawn from a seed, by day, at dusk or at night, whose dark windows light when its link is pointed at; ExpandingPanels, a row of panels that widens the one pointed at or focused, with no script; and Lens, a magnifier over anything, moved by the pointer or the arrow keys (tier B). ContactSplit takes a sent-headline slot, and a heading written with asterisks in the four templates that pass ContactSplit their words now shows its emphasis.
- Practice, a template for a practice that readers come to book and to check: a dentist, a family doctor, a physiotherapist, a vet, a law firm or an accountant. The practice as a card that says whether it is open now in its own time zone, a page per service with what happens step by step and what it costs, the people with their registrations, the fees as tables with what insurance covers in words, an appointment request in three steps, the address and the week's hours, and the notices a regulated practice owes, an emergency first, on every page. Six kinds of practice from one source, six palettes, one of them dark, and three type pairings. It opens calm: every reader sees it as one who asked for less motion does, and one setting turns the arrivals back on.
- Six Pro sections for it: HeroPractice, ServiceIndex, PeopleList, FeeTable, ServiceDetail and NoticeBand. ContactSplit takes steps, a form in steps through Stepper that reads as one form without JavaScript, and three field types: a date, a choice and choices, as chips over native radios and checkboxes.
- In the free library: Stepper, a form in steps whose Next checks the step with the browser's own validation (2 kB raw of its own); OpenStatus, a line that says whether a place is open now and until when, read in the place's own time zone (tier B); and FocusSiblings, a group whose item pointed at or focused stays while the others step back, with no script.
- Stay, a template for a place to stay: the place by day and by night with light falling over the day picture, the dates and the guests handed to your own booking engine by its address, the rooms drawn with their beds, sizes and prices and a page each, the rates by season, what is around and how far, the house rules and an enquiry form. Six kinds of stay from one source (a guesthouse, a villa, a boutique hotel, an eco lodge, a surf camp and a mountain cabin), six palettes, one of them dark, and three type pairings. No booking is kept on the site.
- Five Pro sections for it: HeroStay, BookingBar, RoomCards, NearbyList and HouseRules. BookingBar is a plain GET form to the engine without JavaScript; with it the dates cannot start in the past, the nights are said as they change, and a date is rewritten for an engine that reads day first or month first.
- In the free library: RoomArt, a room drawn from a seed (a single, a double, a twin, a suite, a family room, a bunk room or a loft) with the window's sky by the time of day; LightRays, soft beams from an edge or a corner that sway slowly and stand still with reduced motion; and PixelReveal, a picture that clears from a scatter of squares on its own view timeline, with no script.
- Five more primitives in the free library: HoldButton, a button that acts only once it has been held, by a pointer or by Space, with its fill counting the hold; Pattern, stripes, diagonals, checks, crosses, zigzags, waves or hexagons behind a box; RadialMenu, items fanning out round a button in a popover that opens and closes with no script; Preloader, an opening in three cuts (words, stairs and split) gone under two seconds; and FlutedGlass, reeded glass over a picture.
- New props: Tilt takes glare="foil", a holographic foil whose colours slide with the pointer; HighlightMarker takes shape, a circle, a box, a squiggle or an arrow drawn round or under a phrase; Orb takes state, idle, thinking or speaking, for an assistant; PageTransition takes four more presets, columns, iris, tiles and peel, which reveal the new page over the old.
- The palette generator writes dark palettes too: a palette whose paper is darker than its ink sets color-scheme: dark, and the demo switcher brings a palette's scheme along when it changes palettes. DocsShell takes main={false} for a layout that already has its main, and ChangelogSection gives a release an id.
0.1.1: this site in Plus Jakarta Sans and JetBrains Mono
- This site sets its text in Plus Jakarta Sans and its code in JetBrains Mono, where it used Inter and Geist Mono, and its share cards are drawn in the same two faces. JetBrains Mono's ligatures are off, so => and != in a code sample show the characters you type.
- On /theme/, the editorial and technical pairings now set their body text in Plus Jakarta Sans and the technical headings in JetBrains Mono, and the CSS you copy names those fonts. The library ships no fonts, so load them in your project. Nothing in @moonarc/core or Pro changed.
0.1.1: the terms, privacy and refunds, checked against Indonesian law
- The terms, the privacy policy and the refund policy were read against Indonesia's consumer protection, personal data and e-commerce law, and changed where they fell short. The new text is effective from 2026-10-06. The checkout is not open yet, so no order was placed under the old one.
- Terms. You must be 18 or older to buy, and when you buy for a company, "you" in the terms and the license means the company. Polar resells every plan as merchant of record; where it collects no tax, a tax your country charges is yours to pay. A new Complaints section: write to hello@moonarc.dev from the address on your order, get a reply within 2 working days and a settlement within 14 days or a reason and a date, and in Indonesia you can also go to the Ministry of Trade's consumer protection directorate. The terms are in English, with a translation on request where your law requires one. A change to the terms never takes back the license for what you have already installed or downloaded.
- Privacy. Polar is the controller of your payment data, and holds the customer records it keeps for us as our processor, under its data processing addendum. Each transfer of your data to the United States names the safeguard it rests on, as UU PDP Article 56(3) allows. The 3 × 24 hours UU PDP sets now cover a request to stop a use you consented to, or to pause or limit one, as well as a request to see or correct your data.
- Refunds. Polar may refund an order itself, for example to settle a payment dispute, and a longer or wider right to a refund under the law where you live applies.
0.1.1: core and the MCP server on npm
- Version 0.1.1 of @moonarc/core and @moonarc/mcp is on npm. Core adds FillText, Motif, PortraitArt, ProductArt and Waveform. Nothing in it was removed or renamed, and the integration's ^0.1.0 range takes it, so a new install gets 0.1.1 with no change on your side.
- The MCP server's catalogue lists the five new components with their props and what each one costs.
- A template release now checks that the core on npm holds the same files as the core the template was built against, so every template you download builds with the version it names.
- /compare/ was measured again with Moonarc at 0.1.1, every setup built on Astro 7.3.5. Every verdict held. Motion's mini animate() came out at 3 388 B gzip (it was 3 882 B gzip) and the React island at 110 500 B gzip (it was 110 816 B gzip).
0.1.1 (unreleased): Creator, a template for one person's work, and seven sections
- Creator, a template for a newsletter, a podcast, a course, a coach, an illustrator or a video maker: a bento of soft tiles on a strong block (a drawn portrait with a ring of words, the latest piece, a count of readers that rises, the links), the issues or episodes pictured as a waveform, a drawing or a video frame, a signup beside the next issue's preview, what you sell, kind words pinned up as notes, a now list and a footer that signs off like a letter. Six kinds of work from one source, six palettes and three type pairings, each one line of config.
- Seven Pro sections for it: HeroBento, PieceList, SignupPreview, OfferShelf, NotesWall, NowList and FooterSignoff. A piece with an audio file plays in the page.
- In the free library: Waveform, a row of bars drawn from a word, with a played share and motion while it plays or is hovered, in CSS. Zero JavaScript.
- On /templates/, each family shows its combinations: every palette it ships as a strip of its five colour blocks, and how many sites its kinds, palettes and type pairings make (144 for Agency, 108 each for Atelier, Gather and Creator).
0.1.1 (unreleased): Gather, a template for events, and six sections
- Gather, a template for an event: the name and the dates set large on a poster with a countdown to the doors, the lineup drawn as portraits that turn over to their sessions, the programme by day, the tickets with what is left, the venue on a drawn map, and a footer made like a ticket. Six kinds of event from one source (a conference, a music festival, a monthly meetup, a weekend market, a city race and a season of workshops), six palettes and three type pairings, each one line of config.
- Six Pro sections for it: HeroPoster, LineupGrid, ScheduleDays, TicketTiers, VenueGuide and TicketStub. A link to a session opens the programme on that session's day.
- In the free library: PortraitArt, a drawn bust seeded from a name (eight kinds of hair, five skin tones, six hair colours, a collar, now and then glasses, headphones or an earring) for a speaker or an artist with no photograph yet. Zero JavaScript.
0.1.1 (unreleased): Atelier, a template for makers, and nine sections
- Atelier, a template for a maker or a small product brand: the collection drawn on coloured plates, a page per product with its colourways and a pre-order form, how a thing is made, what it is made of, and a visit card that says whether the studio is open now in its own time zone. Six industries from one source (a ceramics studio, a clothing label, a coffee roaster, a skincare maker, a furniture workshop and a bakery), six palettes measured so every colour block reads at 4.5 : 1, and three type pairings.
- Nine Pro sections for it: HeroSpecimen, ProductRail, ProductGrid, MaterialIndex, ProcessThread, ProductDrop, ProductDetail, VisitCard and FooterLabel. NavBar's translucent bar keeps the hue of a warm palette's card (it mixed in OKLCH and lost it).
- In the free library: ProductArt, 29 products drawn flat in six sets (clothes, ceramics, coffee, skincare, furniture and objects, bakery), each in any colourway from one colour, for a shop with no photographs yet. Zero JavaScript.
0.1.1 (unreleased): Agency, a template in colour blocks, and nine sections
- Agency, a template for a business that sells what it does by the package: the offer with its prices, the work, the story and a contact form, in colour blocks with drawn pictures. Six industries from one source (a creative agency, a restaurant, a small place to stay, a beach club, a spa and a photography studio), eight palettes and three type pairings, each one line of config. Light only.
- Nine Pro sections for it: HeroBlocks, BlockCards, TickerBands, CaseStack, StickerCloud, PackageCards, MotifStats, DotProcess and BlockCta. FooterBigType takes nameEm, a second word in the emphasis face.
- In the free library: Motif, eight flat drawings in the two colours of the block they sit on, for a slot with no photograph yet; FillText, a statement that fills from faint to full as it is read, with no JavaScript. @moonarc/core/blocks.css adds five colour blocks to the theme, and @moonarc/core/palette writes a palette of them from eight colours, measuring every pair that has to read and refusing a palette under 4.5 : 1.
0.1.1 (unreleased): the /theme preview, and the templates on hueless greys
- The preview on /theme/ opens on a board of the theme: a tile in each of the four tones, the motion preset's curve drawn from its own linear() with a dot that runs it at its duration, and both colour scales with their roles. The sections follow in a browser frame.
- Shuffle picks a brand at least 40° of hue from the current one and redraws until every contrast pair passes, with a new radius, font pairing and preset. Replay motion plays every reveal again, and so does picking a preset. A new swatch or a shuffle transitions --ma-brand and --radius, so every colour derived from them moves with it; under reduced motion the change is instant.
- Waitlist, Launch and Agentic set --ma-neutral to a grey with no hue. A derived neutral carries the brand's hue, which gave Waitlist cream and brown grounds and Launch olive ones. Each takes a new brand: Waitlist a red-orange (#ff5a1f), Launch an emerald (#10b981), Agentic a violet (#8b5cf6). Launch's screenshots and video were shot again, and /templates/ opens on a gallery of the four home pages.
0.1.1 (unreleased): Agentic and Studio, two templates and twelve sections
- Agentic, a template for an AI agent or an MCP product. The home page opens on a prompt box whose examples open the demo on a task, and a run of the agent replays lane by lane, from the failed job to the approval. The demo page plays a conversation with each tool call and what it returned. Drawn dark first; every replay stops on its own, and a pause holds it.
- Studio, a template for a design studio, or with its solo preset for one designer. A project chosen in the index grows into its case study, the work filters by discipline with a clip on hover, and the footer is revealed from under the page. Four sample projects, each with pictures made from the library's own effects and pages.
- Twelve Pro sections for them: HeroPrompt, AgentRun, ChatDemo and FeatureCode; NavFullscreen, HeroKinetic, ProjectIndex, WorkGrid, CaseStudyHero, ServicesList, FooterBigType and BlogHoverList. LogoWall can stand still (marquee={false}), and CtaSection, PricingCards, ChangelogSection and IntegrationBeams can turn their loop off.
- In the free library: ThemeScript takes initial, the scheme a site opens in before the reader picks one. PromptInput's placeholder reads at 4.5 : 1. RotatingText with cycles starts settled under reduced motion. SplitText's masked lines, FilterGrid's count and FooterReveal's reduced-motion rule are fixed. AnimatedBeam takes cycles, HoverImageLinks a list of its own, StaggerMenu labels for its buttons.
0.1.1 (unreleased): RotatingText can stop, and fixes from a full check
- RotatingText takes cycles: the words go round that many times, then the first one comes back and stays. That is how a rotation meets WCAG 2.2.2 when it would otherwise run past five seconds. A pointer over the words pauses them. Still no JavaScript.
- CodeTabs seats its copy button in the tab bar without a plate or a border again, whatever order the stylesheets load in. CopyButton takes --ma-copy-bg and --ma-copy-edge for any component that does the same, and a compact CopyButton is its icon alone again: its label showed beside the icon. On a phone, CodeTabs wraps a long tab label instead of letting the last tabs slide out of sight.
- On this site, Join the list on /pricing and /pro led nowhere, because a link saved empty on the host printed an empty href. It falls back to the email now, and the link check fails any empty href.
- The Agent Skill that /resources/ mentioned exists now: /skills/moonarc/SKILL.md, with its install steps on /mcp/ and a line in llms.txt.
- MegaMenu's columns sit side by side. HoverVideo's link has a name without a title attribute. A shader that fails to link warns in development, and the production build carries none of that code.
- This site answers every request with security headers: nosniff, a same-origin frame rule, a strict referrer policy, and a permissions policy that turns off the camera, the microphone and location. It runs on Astro 7.3.5, and pnpm audit, which listed six advisories (two high), lists none.
0.1.1 (unreleased): a template takes its address from the build
- A template reads MA_SITE_URL at build time in place of url in src/config/site.ts, so one source tree deploys to a demo, a preview and production, each on its own origin, with no edit. A value that is not an absolute https origin stops the build, as url does.
0.1.0 (unreleased): the licences, the terms and privacy, and a Templates page
- The Pro licence and the template licence are final. They name the author (Azri Putra, Denpasar, Bali, Indonesia) and who "you" is: one person, with a freelancer and their own business counting as one, or a team of up to the plan's number of people in one company. You may build a SaaS or an app on the code for any number of customers who never get the source, and a deployed site's HTML, CSS and JavaScript do not count as the source. You may not make a template, theme, starter, UI kit or component library from it, or train or fine-tune an AI model on it, and a site builder, a no-code tool, a template marketplace or an AI site generator needs a separate licence. A breach ends the licence, and sites finished before then may stay online. Indonesian law governs, and disputes go to the District Court of Denpasar.
- The terms and the privacy policy are final, effective 2026-09-29. The terms name Azri Putra as the operator and Polar as the merchant of record for payment, tax, invoices and disputes, and cover each plan, renewal, cancelling, refunds, keys and a liability cap; a consumer keeps the rights of their own country. The privacy policy names the controller under UU PDP and the GDPR, says the site sets no cookies and runs no analytics, lists the browser storage keys the code uses and what the key route and the MCP server send and keep, and says how long data is kept.
- /templates/ shows Waitlist and Launch: screenshots on desktop and phone in light and dark, every page each one ships with its measured JavaScript, the Pro sections it uses, and what the download contains. Both come with Pro and Team. The header reads Components, Templates, Themes, Docs, Setup, Pro.
- The links to the GitHub repository are gone from the site: the repository is private, so each one answered 404 to everyone but its owner. "Open an issue" goes to the email instead.
0.1.0 (unreleased): one URL per page, and what search results show
- Every page has one URL: /components answers 308 to /components/. Each Markdown twin names its page as canonical and llms.txt asks to be left out of the index, so neither appears as a second copy. Test pages and fixtures stay out of the index and the sitemap.
- A component page's description is a summary of 120 characters or fewer followed by its measured cost; 160 of 196 descriptions had run past the 160 characters a search result shows. The ten category pages get headings such as "Animated backgrounds for Astro" and a row of links under the catalogue's lede.
- Structured data where Google documents a feature for it: WebSite and Organization on the home, breadcrumbs on the 172 pages two levels deep, TechArticle on the guides. Every share card names the site, its locale and its image.
- The home hero holds still. Its typed install line grew a second row as it finished and pushed the panel 20 px, and the layout shift on a phone measured 0.121 on the home and 0.107 on /pro/; it is 0.000 and 0.004 now.
0.1.0 (unreleased): seven sections and the Launch template
- Seven Pro sections, 35 in all. HeroProduct: a headline, two actions, a demo video in a dialog, and the product in a browser window that stands upright as the page scrolls. FeatureSticky: steps beside one visual that stays in view, under a lead that brightens as it is read. FeatureTabs: features as tabs, each panel sliding in from the side of its tab. IntegrationBeams: the product in the middle and its integrations on beams and an orbit, also written out as a list. BlogArticle: a post page with a reading beam and a contents list that follows the reader. ContactSplit: contact details beside a form that sends, with or without JavaScript. SignIn: an email, then a one-time code, as the front end of your own auth service.
- NavBar takes a mode: condense (as before), static, or hide, which slides the bar away on the way down and back on the way up and whenever it holds the focus, for 613 B raw of script; the default NavBar still loads none. It can end with the theme switch and carry an announcement above it in the same sticky header. PricingCards rolls its prices digit by digit when a reader switches between monthly and yearly billing, in CSS, and each card set gets ids of its own.
- Smaller fixes on the way: StatefulButton stays focusable while it sends and follows the state of a form bound by the form library; Tabs takes a static slot per panel, so a panel can hold an image or a component; StickyBanner names its close button in your language; TestimonialMarquee names its rows; PricingCards, StatsRow, BlogGrid and ChangelogSection can open a page as its one first-level heading, and the last two print a date in your locale.
- Launch, the SaaS template: twelve pages (home, features, pricing, changelog, blog and posts, about, contact, sign-in, a 404, and two pages the forms land on without JavaScript), content collections for every sentence, an RSS feed, a contact form on Formspree, Netlify or your own endpoint, and a two-step sign-in for your own auth service. Its heaviest page, a post with an image gallery, loads 8 277 B raw of JavaScript.
0.1.0 (unreleased), fix: the rest of the audit's fixes
- PricingCards takes priceFormat, locale, perYear, perMonth and its labels as props. Their defaults print what it printed before.
- On this site: the home's ticker says nine of its ten components load no JavaScript, where it said all ten (CountUp loads Reveal). The GPU effects' figures on the home include their GL libs, and the live meter counts the lib chunks the hero loads. The worst text contrast in the /pro/ hero went from 2.54 : 1 to 5.08 : 1 under a denser scrim. The 404 page asks to be left out of the index.
- llms.txt links each Pro item's Markdown page with its install command, lists the four free and three Pro MCP tools with their signatures, and has a section on pricing and the licence.
0.1.0 (unreleased): /compare/ measures nine setups, and every byte figure names its size
- /compare/ builds one scroll reveal nine ways: Moonarc, AstroAnimate, astro-reveal (default and observer modes), AOS, GSAP with ScrollTrigger, Motion (hybrid and mini animate()) and Motion for React in an island. For each it gives the JavaScript and CSS of one reveal and of ten, and what the reveal does without ClientRouter, after two navigations, under reduced motion and with JavaScript off, in Chromium, Firefox and WebKit. Five fixes for the other setups are measured the same way. The page also comes as /compare.md and /compare.json.
- Search engines and agents can read the site. robots.txt lets every crawler in and names a sitemap of every page, and a request that asks for text/markdown gets the page's Markdown twin at the page's own URL.
- Every byte figure on the site says raw (as built, minified) or gzip after its unit, where 1 237 of 1 473 had said neither. Moonarc's own costs lead with raw, and a comparison with another library states gzip on both sides. Corrected on the way: the invoker polyfill is 403 B raw (six pages said 250 B raw), and 72 Markdown twins counted a dependency for components that load no other script.
- The site calls the React library Motion for React, the name on its README, and /compare/react/ is built on motion/react 13.4.4, the version /compare/ measures. It had been built on framer-motion 13.2.0.
0.1.0 (unreleased): yearly plans
- Pro is $129 a year for one person, and Team $349 a year, one flat price for up to 10 people in one company. Founding 200 stays $249 once. There is no monthly plan: everything you install stays licensed after you cancel, so one month would have bought the catalogue for good.
- PricingCards takes monthly as optional, and leaves out the billing toggle when no plan has a monthly price.
- The licence clause on /license/ and /pro/ drops "MIT": an MIT grant would have allowed the redistribution the licence forbids.
0.1.0 (unreleased), fix: what the audit of every product found
- An audit of every product on 2026-09-27 held each component to what its page and its docs promise, in the browser. This release fixes what it found that needs no decision from the owner; the rest waits for one.
- Overlays. Nothing inside an open Drawer took a mouse or pen click, because the sheet captured the pointer on every press; a press becomes a drag after 6 px now. In a browser without invoker commands, VideoDialog played its video, sound included, behind a dialog that stayed closed. CommandPalette keeps its list still under the arrow keys, closes on a click on its backdrop in Safari, which has no closedby, and announces how many results it found. A morphing Dialog no longer fades and scales like a plain one, and its close button sits at the inline end on a right-to-left page. Toasts are announced from a region that exists from page load, and their clock stops on hover, on focus and in a hidden tab. LinkPreview's card can be hovered, as WCAG 1.4.13 asks.
- Buttons and inputs. Ripple drew its rings beside the element in every engine. StickyBanner's remember reopened the banner after a ClientRouter navigation. StatefulButton was named by all of its faces at once; it is named by the one you see. ThemeToggle changes the page on the first click when its choices leave out system, shows the right icon from the first frame and stays hidden without ThemeScript. PriceSwitch shows the price of the chosen period in browsers without round() and mod(). CopyButton no longer stays on "Copied" after a transition:persist swap. A style prop no longer replaces CountUp's duration or Progress's variables. OtpInput takes an id of its own per field and keeps its digits in order on a right-to-left page.
- Pointer, media and layout. Cursor gives the native pointer back when reduced motion turns on. AnimatedBeam draws one packet of light per path, as documented, where it drew dashes. FilterGrid keeps a filtered-out card hidden whatever display the card sets. Magnetic no longer takes the clicks of the elements beside it. HoverVideo is a link that plays on keyboard focus. ImageCompare, AvatarStack, ArrowButton and FileTree follow a right-to-left page. AnimatedList says to prepend a live item, since an appended one never shows in a full list, and a list too short to cycle stands still.
- Underneath. Globe, Particles and Confetti kept a frozen canvas after their tab was hidden and shown again. After a navigation from a page that loads no Moonarc script, html[data-ma-js] was gone for the rest of the visit, so the components that wait for JavaScript showed their content at once; the head gate sets it again after every swap. A component whose setup throws no longer stops the rest of the page from binding. The reduced-motion switch also stops Astro's route transitions now, and mirrors media query lists and combined queries. With Tailwind, duration-ma and ease-ma resolve, so a data-ma-preset subtree retunes the utilities too. A library view transition no longer captures every element named with transition:name. An element you take out of the page is released by the next refresh(), with its listeners and its GPU context.
- Pro sections. MegaMenu's panels were unreadable on the ink and brand bars; they keep the popover's colours on every tone, and the triggers reset the native button. NewsletterSection can post to Mailchimp or ConvertKit: a name prop for the field, a honeypot, formAttrs, a slot for hidden fields, and post only. ChangelogSection's link sits under the items on a phone. FeatureAtlas no longer hides a live demo slot from screen readers. LogoWall's height prop sets the logo height in both layouts. StepsSection is a valid list, and its line and FeatureRows' copy follow a right-to-left page. The fixed English labels of six sections are props, and 27 of the 28 usage examples start with the import path the registry writes.
- On this site. A Pro page listed related sections and effects as free; it lists the primitives a section composes, or an effect's free relatives, and its Markdown twin says the same. A GPU effect's readout prints what it loads with its GL libs, where it printed its own script alone. A free component page says what its registry item installs and which files a paste needs, where every page said the runtime was its only import. The /theme preview shows sample plans.
0.1.0 (unreleased): the logo
- The site wears its logo kit: the mark in the header and the footer, and the same drawing on the share cards, the favicons and the home screen icon. With a mouse, the lettering wipes in beside the mark on hover or keyboard focus, in CSS only, and fades in place under reduced motion.
0.1.0 (unreleased), fix: what the component audit found
- CountUp read wrong in Safari and every WebKit browser: each group of digits showed the first group's value, so 2,847 read 2,002. The counter is now reset on the pseudo-element that prints it.
- Spotlight measured the pointer in screen pixels and painted in its own, so under a scaled ancestor the glow sat at half the distance. AvatarStack's count grows from a circle into a pill when it is wider ("+2,398"), and keeps its circle in a project without a CSS reset.
- PricingTable: an empty cell says none, and screen readers still hear "Not included". When a narrow screen cuts the table, the section scrolls sideways with a tab stop, a name (the new label prop, "Plans compared" by default) and a focus ring on its border in the tone's ring colour.
- The usage examples of CompareTable, PricingTable, PricingCards, StatsRow, Progress, MegaMenu, HeroTerminal, HeroEditorial, AnimatedList and StickyBanner use sample data where they typed Moonarc's own prices, counts, bytes or releases. The demos on this site read theirs from the store, the catalogue and this changelog, and label the rest as samples.
- The Pro registry answers a key check that got no answer (a rate limit, an outage, a timeout, a misconfigured site) with 503 and Retry-After. It used to answer 401 with the upgrade prompt, which told a paying buyer to buy Pro. Keys are checked through Polar's authenticated API, and the requests npx shadcn add makes at once share one check.
0.1.0 (unreleased): the site measures before it prints, and an About page
- The live site printed "not measured" on every share card and readout, and counted all 102 primitives as zero bytes: its production build ran without the budget report. The production build now measures the component fixtures and then the pages it prints, and stops when a figure is missing. The runtime reads 1.9 kB raw everywhere; six places had it typed as 1.8 kB raw.
- Claims about other libraries name their source or state our measurement, with versions and browsers, or are gone. React's cost is the one /compare/react/ ships, React 19.3, where the site had printed React 18's size. Three counts of primitives that sounded alike have one phrase each: "load no JavaScript", "no script of their own" and "never load the runtime".
- lenisPage: a link clicked, or the back button pressed, while Lenis was still easing a wheel scroll left the next page where that scroll was heading. The resync stops the animation first and then restores the position the router set. lib/lenis is 986 B raw, under its 1 000 B raw ceiling.
- The gated Pro registry answered 500 to every request in production, with or without a key: it read its files from a disk the server does not have. It serves them from the build now.
- /about/ says who builds Moonarc, why, how it is tested and paid for, and what keeps working if it stops; /about.md is the same page for agents.
0.1.0 (unreleased): the library is Moonarc
- The library is called Moonarc and lives at moonarc.dev. Install it with npx astro add moonarc; the packages are @moonarc/core, @moonarc/mcp and @moonarc/pro.
- moonarc@0.0.1 on npm is a name-claiming placeholder. The packages publish at 0.1.0; @moonarc/core and @moonarc/mcp are not on npm yet.
- Every custom property, class, data attribute, event, window global and environment variable the library defines starts with ma now: --ma-brand, .ma-sr, data-ma-theme, ma:form-done, MA_FORM_DEMO. Both prefixes are two letters, so no raw size grew. The runtime, and every figure measured with it, lost 5 B raw, because the tag on its error log is the new, shorter name; gzip sizes shifted by a few bytes either way.
- The shadcn registry namespaces are @moonarc and @moonarc-pro. Items install into src/components/moonarc/ and src/lib/moonarc/, and the two stylesheets are moonarc.css and moonarc-theme.css.
- The MCP server reads a Pro key from MOONARC_KEY.
- npx -y @moonarc/mcp starts the MCP server, and npx @moonarc/mcp audit src runs the audit in CI. Before, npx stopped with "could not determine executable to run": the package had two commands in two files and neither carried its name, so npx could not choose. Both commands now run one file, which starts the server when an agent launches it and runs audit or measure when you name one.
- The integration package and the MCP server now ship the MIT licence text with them.
0.1.0 (unreleased), fix: from a 280 px phone to a 2560 px screen
- Reveal measures its threshold against the screen when an element is taller than the screen. A stagger group stacked into one column on a phone could never show the share of itself the threshold asked for, so three home sections and the /pro/ grid never appeared on a 320 × 568 phone. Reveal grew from 1 126 B raw to 1 322 B raw.
- Fixed at 320 px: SplitText's authored lines wrap (HeroEditorial's headline was cut by 177 px); NavBar puts its links on a row of their own that scrolls sideways; NewsletterSection, BrowserFrame and CodeTabs stay inside their box; Typewriter's caret stays on the last word. On phones and tablets, StatsRow, PricingCards and FaqSection fill their rows without an empty cell or a lone plan.
- On this site, the header's search button opened nothing at any width, so a phone, which has no ⌘K, had no search. It opens now, near the top, where a phone's keyboard leaves it in view. The props table becomes one block per prop on a phone, copy buttons show without hover, the reduced-motion switch is in the phone menu, and text fields are 16 px so iOS does not zoom into them.
0.1.0 (unreleased), fix: shadcn add puts the files where their imports look
- npx shadcn add wrote every registry file flat into src/components/, so a component's import of the runtime found nothing, and 40 of 102 free components, 38 of 39 Pro ones and three libs failed astro build after install. Component paths now start with components/ and lib paths with lib/, so the files land in src/components/moonarc/ and src/lib/moonarc/, and Pro under pro/.
- The components.json that sets up the CLI without shadcn init, which would add seven unused packages and React, is printed on /setup/, /account/ and /mcp/, with the @/* alias tsconfig.json needs. A test installs six items with the real shadcn CLI into a blank Astro project and builds it.
0.1.0 (unreleased): the home stops carrying 141 demos
- The home page ships 177 009 B raw of CSS where it shipped 410 361 B raw, and 38 598 B gzip where it shipped 74 643 B gzip. Its JavaScript is byte-identical. The CSS of /pro went from 407 423 B raw to 267 196 B raw, and from 73 860 B gzip to 53 065 B gzip.
- What was wrong: the catalogue card looked its demo up in a glob of the whole stages folder. A demo's CSS follows the module that imports it, and Astro gives that CSS to every page that can reach the module, so a page with one card carried the styling of all 141 demos. The card takes its demo from the page now, and the home imports the twenty-one it shows, free and Pro, with a build that fails if a tile names a stage nobody imported. The catalogue, a category page and a component page ask for the whole shelf on purpose, through one module that says so.
- A non-eager glob in the card looks like the answer and is not: attribution walks dynamic importers too, so every page still reached every stage, and splitting one file into 140 small ones cost 9 kB gzip on the home. The mechanism content collections use to attach styles per rendered page does not survive the compiler on a component with a script. Both measurements are in docs/stage-css.md.
- A component page still carries every demo, because one module renders all 141 of them and CSS is attributed per module. The file is shared, so a reader who opens a second component page pays nothing more for it.
0.1.0 (unreleased): the docs get an index and a contents list
- /docs/ is a hub of five groups, and the sidebar, the hub and the previous and next links read one order. Every h2 and h3 carries a link, a contents list marks the section being read, and code blocks in the docs get the copy button and the file title the rest of the site has. The guides use the same layout.
- A link to /docs/installation/#manual, on every component page that ships a script, pointed at a heading that never existed. The link check now reads fragments on the docs, the guides and /setup/.
0.1.0 (unreleased), fix: typing that arrives in Safari
- Typewriter and HeroTerminal type all the way through in Safari. Each character, and each line a terminal prints, is now revealed by an interpolated keyframe, which every engine lays out whenever it lands.
- What was wrong: a stepped keyframe that lands more than 1 000 ms into a page reaches computed style in WebKit 26.6 but never layout. The home hero had been showing 2 of its 26 characters, with the caret 464 px to the right of them, and a terminal printed none of its three output lines. Chromium does the same for every property except display; Firefox lays out every case. Animations that loop, and stepped ones with a long active interval, were never affected. The browser gate now reads the laid-out box on the home hero and on a terminal, in whichever engine it is pointed at, and both checks were run red against the old CSS first.
0.1.0 (unreleased): /setup rebuilt, and astro add installs core
- npx astro add moonarc writes @moonarc/core into your package.json beside moonarc. Core was only a dependency of the integration, so pnpm and Yarn Plug'n'Play left it out of the project and import Reveal from '@moonarc/core/Reveal' failed to resolve. As a peer it is installed with the integration, as the installation docs already said.
- /setup/ is rebuilt: the astro add log in a terminal, the install for npm, pnpm, yarn and bun with the config change, three first components beside their code and measured cost, four per-project settings, the agent setup (Claude Code, Cursor, VS Code, Codex, the MCP tools, the shadcn registry) and how to check the build. /setup.md is the same page for agents.
- The old build check was wrong: Astro inlines a small script into the page, so a one-page site has no .js file in dist/_astro and ls dist/_astro/*.js matched nothing. The page shows the real shape of dist/index.html now.
0.1.0 (unreleased), phase 7F.3: the default palette
- A new default palette in theme.css, from one brand colour. Cerulean #2978a0 is --ma-brand; a tinted neutral (--ma-neutral, Jet #253031) and an accent (--ma-accent, Sand #bcab79) are derived from it, and a neutral scale, --ma-neutral-50 to 950, is what the page, the text, the borders and the fields point at. Set --ma-neutral or --ma-accent to choose your own: the builder at /theme has an input for each.
- The painted tones are theme scopes. A section with tone="muted", "ink" or "brand" re-declares the card, border, field, focus ring and elevation tokens inside it, so its cards, hairlines and fields follow the ground. The brand tone has a ground of its own, --ma-tone-brand: the brand colour at lightness 0.46 in both schemes. The tone attribute is now data-ma-tone.
- Status colours: --ma-success, --ma-warning and --ma-info, each with a -foreground, and new values for shadcn's --destructive. Each reads at 4.5 : 1 or more as text on the page and under its own text. With the Tailwind bridge they are text-success, bg-warning and bg-info.
- With theme.css, secondary text is never fainter than 0.75 opacity (0.8 on muted, 0.9 on brand, including the brand panel CtaSection and HeroField paint when no tone is set), so a pasted theme with a gentler text colour still reads at 4.5 : 1. The Waitlist template no longer patches this itself.
- BlogGrid shows each post as a card. The light --primary is the brand's 600 step (it was 700). The registry's theme item carries the seeds and the roles and leaves every derived value to theme.css, so a later --ma-brand recolours the scales.
- FeatureAtlas keeps to one column under 40rem: its wide cell no longer adds a second column beside the others.
0.1.0 (unreleased), phase 7G: the template foundation
- Two Pro sections, 28 in all, and both of them are the waitlist: HeroWaitlist (a RetroGrid floor under a plate, CountdownFlip to the launch date, a VanishInput email field, the consent CheckDraw, an AvatarStack of proof and a ShimmerButton that submits, 43 B raw of its own) and ThankYou (the page the form leaves you on: the position read back from the record the form lib wrote, never from the URL, a referral link with a CopyButton, and one Confetti burst that never fires twice, never on a reload and never in reduced motion; 981 B raw, and the none variant ships no canvas at all). Five axes, named slots, and no tone unless you set one, so neither paints a ground it was not given.
- @moonarc/core/form: the send path, in core and MIT, because a form that arrives is not a paid feature. One listener over every <form data-ma-form> encodes as JSON, form-data or url-encoded for the adapter (demo, Formspree, Netlify, or your own endpoint), locks the form with data-pending and aria-disabled rather than disabled, so focus is never dropped from under the pointer, writes its status into a role="status" node outside the form, waits for the field to finish vanishing before it leaves, carries the answer to the thanks page in sessionStorage and dispatches ma:form-done or ma:form-error exactly once. 3.0 kB raw; 4 789 B raw on its fixture page with the runtime under it. With JavaScript off the same markup is a plain form and posts natively (the server's 303 lands on the thanks page), and every path is checked in three engines, with the tab hidden, offline, across a ClientRouter swap and back through bfcache. Written up at /docs/forms/ and served as a registry:lib item at /r/form.json.
- templates/: the tree the products live in. templates/kit is what every template is built on: Layout, Head, Analytics and LanguagePicker, plus modules for the site config (zod-checked, and a bad colour now fails when the config loads and names the key instead of halfway through a share card), forms, robots.txt, 1200 × 630 share cards rendered at build, i18n, RSS and Pagefind search. templates/waitlist is the first template: brand Lanternfold, three pages with every string in content and none written into the markup, the theme pasted out of /theme, the logo and the five avatars drawn here, nothing third-party. Each page's JavaScript under a ceiling of its own, which the template gate measures; 49 links, no violations across four passes and two states, and 26 text-on-ground boxes at or over 4.5 : 1 under the shipped theme and two pasted third-party ones. templates/fixture is the other half: a template nobody buys, whose whole job is to exercise what Waitlist does not use: all four form adapters, MDX posts with a feed, search, two locales and a ClientRouter page.
- templates/release.mjs writes what the buyer actually gets, and tests/templates proves it is the same project as the demo. The copy carries the template file for file, with Pro and the kit vendored under vendor/ as file: dependencies (only what the import walk reaches: six Pro components and no lib, each package with its licence and the fonts with their OFL), and a package.json that may differ from the repo's in its dependency specifiers and in nothing else, diffed line by line before the copy is allowed out: 56 files, 519.6 kB raw, and a zip that is byte-identical written twice. The gate builds and audits the workspace copy, then installs the release copy outside the workspace with npm and again with pnpm (the two resolve a file: directory differently), and runs a11y, links, the form flow, SEO and the ceilings over both; then rebrands that copy through src/config, src/content, src/assets and public alone, holds the diff against that rule before anything is built, and checks that the demo adapter is still refused without its opt-in. The CSS of all five builds is byte-identical, which is how we see that Tailwind never scanned vendor/. The site gates can be aimed at another build for it: a11y and links take --dist, and with it links also reads form actions and every srcset.
- Fixed underneath, in the primitives a template leans on. VanishInput had one id per field name, so two forms on a page shared it, and its hint and placeholder sat at 2.52 : 1. They take their colour through the --ma-dim scalar now and clear 6.4 : 1 in light and 7 : 1 in dark in all three engines. CountdownFlip hid its own done text and never spoke it, said "1 days", clipped a three-digit day count, and had shown two digits at once after every flip since it shipped (the flap is a translucent tint, so the old bottom read through it). CopyButton was named by all three of its faces at once while its aria-live announced nothing: the inactive faces leave the accessibility tree and the status is a role="status" node beside the button. Confetti handed var() straight to fillStyle, so the site had been dropping black confetti, and it takes trigger="event" now, fired from your own code without importing the lib. FooterSection rendered an empty <p>. And ThemeToggle bound itself to [data-ma-theme], which since 7F is also the theme-scope attribute, so a click anywhere inside a section demo cycled the site theme; it binds button.ma-theme now, the same fifteen characters, and the home page is byte-identical at 43 512 B raw.
0.1.0 (unreleased): an accessibility check on every page
- axe-core 4.13 checks every page of this site against WCAG 2.0 to 2.2 AA and its best practices, in light, in dark, on a phone and with full motion, plus six opened states, and the test suite fails on a single violation. Its first run found 11 147 on 184 pages. This release fixes all of them.
- SplitText, RotatingText, CountUp and nine other text components keep their string in a visually hidden .ma-sr element, where they had it in aria-label on the root. To change NumberFlow's value from a script, write the text of .ma-nf__static.
- Tabs and CodeTabs are radio groups with a new label prop, and Marquee, NavBar and FooterSection take a label too. --ma-dim is the opacity secondary text takes, the lowest that keeps 4.5 : 1. readableCode() in @moonarc/core/contrast is a Shiki transformer that moves each token colour just far enough to read at 4.5 : 1 on its ground, at build time; the registry has it as contrast.
- Bugs the check turned up on this site: the catalogue opened about 1 650 px down the page, because a demo's dialog focused its input on load; the AnimatedSelect demo took the keyboard focus every 2.4 s; the home's byte meter changed the order of its breakdown on every load.
0.1.0 (unreleased): GPU effects in Pro
- Ten more GPU effects, 11 in Pro: LiquidImage (a real <img> that turns liquid under the cursor and settles back), Dither (Bayer-dithered waves in pixel cells), Silk (lit fabric folds), Plasma (glowing ribbons), LiquidChrome (a pool of liquid metal), Gooey (metaballs that follow the pointer), Hyperspeed (light streaks; hold to punch the drive), FlowParticles (GPGPU particles on curl noise with trails), InkReveal (ink washes a GPU cover off real content) and WaveField (the wave equation in halftone, with rain). Each is tier C on its own script, demoed and tuned on its page, and checked for lost contexts, the motion switch and navigation leaks.
- One GL core under all of them, extracted from FluidField with its measured behaviour: lib/gl (the context lifecycle: made the first time the effect is on screen, restored after a GPU reset, released on navigation; plus passes, float targets and pointer deltas), lib/fluid (stable fluids with walls) and lib/shader (the runner for one-pass effects). They are Pro code: shipped as files of each gated registry item, never as a public /r/ item. FluidField's own script went from 8.4 kB raw to 1.9 kB raw.
- Home hero: the second background is WaveField (ripples on the GPU, a wake behind the cursor and rain while it rests) in place of the glyph field, tuned from the same strip. Reaction–diffusion was tried for the slot and filled the box within seconds. The home ceiling moved from 41 984 B raw to 43 520 B raw for it; the composition is printed in the budget file.
- /pro now opens on the home's hero layout running what it sells: FlowParticles, with fluid, waves, silk and dither on the strip, each with its readout, cost and page.
0.1.0 (unreleased): FluidField and the new home hero
- Pro now has two kinds: sections, and GPU effects: WebGL primitives priced as Pro because they are the expensive work, demoed and tuned on their page like any primitive. FluidField is the first and moved from core to Pro (@moonarc/pro/FluidField, registry @moonarc-pro/fluid-field); it never shipped in a core release.
- FluidField (Pro): smoke that follows the cursor, from a real fluid simulation on the GPU (WebGL2: advection, vorticity confinement, a pressure solve) in two tones, the ground and one colour. Velocity on a 128-cell grid, the dye on 512 and in one half-float channel, so the colour is applied at display time and follows the theme without resetting the smoke; dots draws it as a halftone. A slow emitter wanders the focus while the pointer is still and on touch; every constant is a custom property, re-read when the style changes. The loop runs only on screen in a visible tab, the context is released on every swap, reduced motion draws one settled still, and without WebGL2 float targets a CSS glow stands in. 7.4 kB raw, above tier C by decision, stated in budgets.json.
- Home hero rebuilt: the ink panel is gone. The copy sits on the ground with a scrim, the halftone FluidField in the site accent runs behind it by default, and a strip at the bottom prints what is running and what it costs, switches the background between the fluid and the glyph field (hidden, so the other costs nothing), and opens tune for whichever is showing. The choice lives in the URL. The home JS ceiling moved to make room for the fluid.
0.1.0 (unreleased), phase 7F: the theme layer
- The 26 Pro sections now read the shadcn variable names (--background, --card, --primary, --border, --radius and the rest) with the base tokens as fallback (var(--card, var(--ma-panel))), so a pasted shadcn or tweakcn theme recolours every section without editing one, and a page with no theme renders exactly as before: measured pixel by pixel on every section page, dark and light, desktop and mobile, zero changed. No radius, section padding or colour is hard-coded in a section any more.
- @moonarc/core/theme.css: the contract with its default light and dark values, --ma-brand with a scale derived in CSS (oklch(from …)) over a static ramp for browsers without it, --ma-tone-ink for the always-dark panel shadcn has no name for, a radius scale on --radius, --ma-density, fonts and shadows. Every rule is :where(), so a consumer's :root or .dark wins; a data-ma-theme subtree carries a theme of its own. The Tailwind bridge (@theme inline, bg-background, rounded-lg on --radius) is a second, optional file.
- Five axes on the sections, each a prop and the data-* attribute it writes: tone (surface · muted · ink · brand; the primary action inverts on a painted ground), density (compact · cozy · roomy through --ma-density), align, columns and media. Named slots (eyebrow, headline, copy, actions, media, aside) on every section that has the part; a slot wins over the prop. Each section page lists its classes and slots and has a variants panel that writes the attributes on the running demo.
- The theme builder at /theme: one colour, a radius, a density, fonts and a motion preset; six real sections follow at once in a data-ma-theme scope; the CSS to paste, with the brand scale precomputed under @supports not for browsers without relative colour; the contrast of every changed pair; the choice in the URL. The registry serves the default theme as a registry:theme item (npx shadcn add …/r/theme.json).
- Contrast is checked by a script. The scale promises white text on the 700 step and near-black on the 400 step at full and 0.75 opacity for every hue up to chroma 0.28, the ring 3 : 1, adjacent chart colours ΔEok ≥ 0.09 in colour and under a deuteranopia simulation; check-contrast.mjs proves every pair a section paints in both default themes and per tone. Two things it changed: --primary is the 700 step, not 600 (0.75-opacity copy needs L ≤ 0.44), and the brand tone raises its faintest text to 0.8 opacity (a 0.6 kicker never passes on a colour lighter than L 0.38).
- Site: --muted and --accent were renamed to --muted-fg and --signal: both are surfaces in the contract and were text colours here; the contract is aliased in global.css to what the sections painted before, so the demos are unchanged. Sparkline and BarGrow default to --chart-1 when a theme defines it. Browser support: relative colour and contrast-color() added to the audit.
0.1.0 (unreleased), phase 7E: input, loader, magnet
- Eleven primitives, ten at zero bytes. Six of them are form controls that stay native: Switch (a checkbox with role="switch" and appearance: none; the input is the track, its ::before the thumb on the preset spring), CheckDraw (a checkbox whose tick is a pathLength-1 stroke drawn on :checked, a bar on indeterminate), OtpInput (one input, because paste, SMS autofill and Backspace work only that way, drawn as cells by a computed letter-spacing; the check draws when full and valid), AnimatedSelect (a native select; where appearance: base-select exists the picker takes the tokens and the options stagger by sibling-index(), elsewhere the styled native control), PromptInput (a form with a field-sizing textarea, a model select and a real file input; submitOnEnter is a 327 B raw variant that sends on Enter, breaks the line on Shift+Enter and never interrupts an IME), VanishInput (hints take turns through one registered number; a submit the host prevented splits the value into characters that blur away, 604 B raw), AvatarStack (overlapping avatars that spread on hover, names as CSS tooltips, no tab stops), CounterPreloader (0 → 100 through counter-set on a fixed layer that never takes the pointer; once inlines 187 B raw and re-runs after every swap), DynamicIsland (every state declares its size; the shape transitions on the spring, the content cross-fades), PhoneFrame (a generic phone in container units, with an island, notch or punch and no brand, and a screen that drifts) and LiquidGlass (glass in every engine; the rim refraction is an SVG displacement map on its own backdrop-filter layer, painted by Chromium and by nothing else, measured).
- Catalogue tiles pause when they are more than a viewport away: one IntersectionObserver owned by the site (never the home page) marks the stage data-stage-off and animation-play-state: paused takes every animation under it, since content-visibility: auto skips style, layout and paint but not animations (measured in 7D). Stage scripts skip their ticks through the same attribute. Idle style work in the middle of the catalogue fell by a third; see docs/phase-7e.md for the table.
- Fixed: the catalogue filter nulled ClientRouter's history state when it wrote the URL, so back from a component page changed the address and left the page, and a dev-server reload landed at the top. It now keeps the router's state, remembers the query in sessionStorage, and the "Components" breadcrumb on a component page returns to the filtered list.
- Fixed: CommandPalette scrolled the whole page to itself whenever it rendered results off screen: scrollIntoView() scrolls every ancestor, and the catalogue tile types on a timer. The list now moves its own scrollTop only. On the catalogue this was the page jumping while you scrolled.
- Browser support: :user-valid / :user-invalid, appearance: base-select with ::picker(select) and ::checkmark, sibling-index(), field-sizing and the Safari switch attribute added to the audit from BCD 8.1.1; backdrop-filter: url(#svg) measured in three engines (Chromium paints it, Firefox and WebKit accept the value and paint nothing on that layer). Found: a lightningcss build with both backdrop-filter and -webkit-backdrop-filter written keeps only the prefix; a node kept through transition:persist restarts its CSS animations; a <style> literal in a component doc comment confuses the budget harness.
0.1.0 (unreleased), phase 7C: pointer, backgrounds, canvas
- Twelve primitives, eight at zero bytes: Cursor (a ring or a "View" pill trailing the pointer: the lag is a CSS transition, the targets one delegated listener, 1.0 kB raw; not installed on touch), ImageTrail (a server-rendered pool of lazy images popped along the pointer's path every N px, 875 B raw), Confetti (a burst on a fixed canvas that exists only while pieces fall; physics as custom properties per preset; also confetti() from your own handler), RetroGrid (a perspective floor translating one cell on the compositor), AnimatedBeam (packets of light along server-computed curves between slotted nodes (hub, row, fan), nothing measured), OrbitingCircles (one transform keyframe per item, count from :has()), Orb (three gradients breathing, one registered angle turning every stop's hue), ProgressiveBlur (bands of doubling backdrop blur under overlapping masks), DottedMap (the world from a 1° Natural Earth mask as one dash-dotted path, 5.9 kB raw, no script), Particles (a drifting field that parts around the cursor, 1.6 kB raw), Meteors (seeded streaks on a twinkling sky, the same seed the same sky) and Globe (dots on a turning sphere from a 1.2 kB raw mask, three batched fills a frame, draggable with inertia, 2.8 kB raw).
- The first canvas components come with a shared helper, @moonarc/core/canvas (1.7 kB raw, measured on its own fixture): a ResizeObserver fit with the pixel ratio capped at 2, a frame loop that runs only on screen and in a visible tab, and a theme observer that re-reads the colours. The tokens reach the context through the canvas element's computed colour, so oklch and color-mix() arrive as rgb.
- Land data: packages/core/scripts/build-land.mjs rasterises Natural Earth 110m land (public domain) into a 1° bitmask, imported on the server only; DottedMap draws from it, Globe ships a downsampled copy as JSON in its HTML.
- tests/consumer is green again across Astro 5, 6 and 7 in static and server output: the Aurora check named keyframes from before phase 6, and the Typewriter check read the first character in the one frame its 1 ms step-end animation shows the from keyframe. It was a race in the test, proven with a per-frame probe on the server build; it now waits for the condition.
- Browser support: backdrop-filter added to the audit (Chrome 76 · Firefox 103 · Safari 18); ProgressiveBlur states its gradient fallback.
0.1.0 (unreleased), phase 7A: transitions, text, numbers
- Fifteen primitives, thirteen at zero bytes: SplitFlap (a departures board: every letter a CSS counter on a registered integer, a flap per step), TextRoll, CircularText, GlitchText, StrokeDraw (SVG text, one stroked glyph at a time), HighlightMarker, UnderlineHover (in from the left, out to the right), BubbleText, NumberFlow (digits roll to a value written as one custom property; round() and mod() do the digits), PriceSwitch (two radios + :has() driving NumberFlow, the badge through @starting-style), CountdownFlip (one interval a second, the fold is CSS, 1.1 kB raw), Sparkline and BarGrow (server-rendered SVG and bars that draw and grow on scroll), Curtain (a full-viewport wipe between pages on the root view-transition snapshots (iris from the click, blinds, shutter, doors, pixels, stagger, diagonal, title), 479 B raw for the click point) and PersistPlayer (an <audio> element that rides into the next page under transition:persist).
- A tenth category, data, for the numbers and charts; CountUp and Progress stay where they are.
- Six props on existing primitives: SplitText mask (each part rises from behind its own line) and scrub (words light up on the block's view() timeline), RotatingText fluid (the box follows the current word), Reveal from="clip" (a wipe from the bottom edge; it is a mask because Chromium's IntersectionObserver applies the target's clip-path), Progress shape="ring", PageTransition preset="scope".
- Catalogue tiles are skipped while off-screen (content-visibility: auto on the card stage): with fifteen more live tiles the catalogue page idles at 18 ms/s of style work against 31 before, and layout at the top is gone. Stage boxes clip instead of hiding overflow, so a view() timeline inside a stage runs against the page again.
- The demo pages carry a Curtain, a persistent player and a mode picker; the two pages are coloured differently so the shape between them shows, and a named PageTransition region keeps its own preset through the same swap.
- Browser support: clip-path basic shapes and mask-composite added to the audit, round() joins abs()/mod(). Verified in Chromium only: Firefox and WebKit are not in this machine's Playwright cache; every fallback is stated on the card.
0.1.0 (unreleased): the glyph field
- GlyphField: a field of braille dots (ascii and block sets as a prop) as CSS counters on registered integers: contours from server-side noise, two waves stepping one inherited integer per row (mod() and abs() fold each cell's crest), colour per cell along a sunset arc, a hover trail that decays one character at a time. 0 B; the swish prop inlines one measured pointer listener for a two-dimensional wake with a tail.
- Home hero rebuilt on GlyphField, motion.dev's canvas as the reference and the brief "wilder, not a copy": kicker with the live counts, an amber first word, availability chips, the readout printing the measured swish bytes, and a tune panel (palette, glyph set, wave, reach, fade, hue) that writes custom properties on the running field.
- HeroField (Pro) now composes GlyphField and takes available, hue and swish.
- Fixed: RotatingText never showed a word on a page that also had CountUp, and Tabs, Typewriter and Spotlight had the same latent collision. @property registrations are global, and a short name (--ma-n, --ma-y) registered by one component as non-inherited zeroed it for another that read it through inheritance. Every custom property is now namespaced per component (--ma-rotate-*, --ma-count-*, --ma-tabs-n, --ma-type-n, --ma-spot-*).
- Full audit: every route swept at desktop and mobile for errors, overflow, unlabelled controls, heading outline and stale copy. The type scale is now the measured pixel scale (44/40/23/15/13/11; rem values on a 15px root had left every size 15/16 short, the kicker at 10.3px). Hero sections, the 404 and the docs shell take a level prop so a demo never adds a second h1. A 404 page built from NotFoundSection. Component pages fold the full source to a screen and a half with a zero-JavaScript expand. The search field has a name. The home prints the latest changelog entry from one shared list; every remaining typed count reads from the catalogue.
- Pro page rebuilt from the Pro sections themselves (HeroField, StatsRow, PricingCards, FeatureRows, the whole collection as live cards, CompareTable, FaqSection, CtaSection), each labelled "built with" its readout and measured cost. Carousel: the previous/next buttons are positioned with plain insets (anchoring a scroll-button pseudo-element to its own container landed both on one spot). /docs opens on Installation. Home: the preset demo cards are sage and numbered, the navigation counter prints its numbers in the measured colour, developer-tool rows light up amber on hover; every Pro stage reads its counts from the catalogue instead of a typed number.
- Site upgrades: the measured colour is sage instead of cyan (an autumn pair with amber, and the one hue the hero arc never crosses); every section heading and grid reveals on scroll; a tile's stage morphs into the component page's demo through transition:name; the hero field sinks and dims on a view() timeline as it scrolls out; hovering the showcase ticker slows everything in it to a quarter speed instead of stopping it; the tune panel prints the prop line and keeps its choice in the URL, as does every Playground; a readout is a button that copies its line; and a motion switch in the header previews prefers-reduced-motion.
- Integration: motionAttribute option mirrors every reduced-motion rule to html[data-ma-motion="reduce"] at build; prefersReducedMotion() reads the attribute.
- Home section 03 rebuilt as six effect tiles: one effect a cell, its name in the corner, nothing overlapping; an off switch leaves the tile with its name.
- Base tokens: --ma-dark, a 0/1 scalar that follows the theme so a component can lean lighter or darker in calc() without repeating the three-state rule.
- Browser support: abs()/mod() and container query units added to the audit; GlyphField is labelled newly with its fallback stated.
0.1.0 (unreleased): the redesign
- 36 primitives: Accordion, Aurora Background, Border Beam, Card Stack, Carousel, Count Up, Cursor Grid, Dialog, Dot Pattern, Drawer, Gradient Text, Grid Pattern, Horizontal Scroll, Magnetic, Marquee, Noise, Page Transition, Parallax, Popover, Progress, Ripple, Rotating Text, Scroll Progress, Scroll Reveal, Scroll Scrub, Shiny Text, Skeleton, Spinner, Split Text, Spotlight, Tabs, Text Scramble, Tilt, Toast, Typewriter, Weight Wave.
- 26 Pro sections composed from the primitives: 404, Blog Grid, Call to Action, Changelog, Comparison Table, Docs Shell, Editorial Hero, FAQ, Feature Atlas, Feature Rows, Field Hero, Footer, Hero Section, Integration Grid, Logo Wall, Mega Menu, Newsletter, Pricing Cards, Pricing Table, Stats Row, Steps / Timeline, Sticky Nav Bar, Team Grid, Terminal Hero, Testimonial Cards, Testimonial Marquee. Demos public, source gated.
- Motion theme: five sampled springs (snap · ui · gentle · lively · ambient) as linear() curves with settle-time durations; one data-ma-preset attribute retunes every component. No component hard-codes a curve any more.
- Browser support stated per component from @mdn/browser-compat-data, printed on every card, with the fallback named for the scroll-driven family and the carousel chrome.
- Marquee: min-width: 0, so a strip inside a grid column no longer locks the column to its content width.
- Aurora rebuilt: three coprime drift periods, an accelerated hue rotation, film grain stepped at 12 fps.
- Site: a playground on every component page (knobs from the running instance, replay), ⌘K search, Open Graph cards for every page, /compare with the same hero as a React island measured side by side (22 kB raw against 374 kB raw), and a mobile menu.
- Site rebuilt showcase-first: a cursor field hero, a numbered atlas of live tiles with readouts and costs, a preset switcher with zero JavaScript, a ticker of ten live components, and a catalogue you can filter by cost, category, trigger, support and the library you are leaving.
0.1.0 (unreleased)
- Catalogue: Reveal, Split Text, Typewriter, Rotating Text, Marquee, Aurora, every one with a measured byte cost, a reduced-motion branch and a page showing the source.
- Runtime: onMount / onPage / prefersReducedMotion / refresh. It binds now and after every ClientRouter navigation, never double-binds.
- Adapters: gsapPage (context + revert on the Astro lifecycle, ScrollTrigger refresh after paint and fonts) and lenisPage (one instance per document, resynced after each swap).
- Verified in a real browser on Astro 5.18, 6.4 and 7.3, static and server output, with real gsap and lenis installs.
- Site: per-component pages, categories, docs, guides, setup, MCP page, llms.txt, Markdown variant of every page, shadcn registry at /r/.
- MCP server @moonarc/mcp: search_motion, get_component, get_install_command, get_usage.
0.0.1
- Name-claiming placeholders published on npm, under the library's earlier name.