Repository navigation
feat: the web collection (panelui-web) - #241
Merged
Merged
Conversation
PanelUI's components are React Native, so nothing in the library could be used in a browser. This is the start of a second collection, for React on the web, styled with Tailwind CSS on the tokens a shadcn project already defines. packages/web is its source. It is built by the compiler alone, one module per source file, so a 'use client' directive is still the first line of the file a bundler reads. scripts/build.mjs then completes each relative import against what was emitted, since a published ES module has to name `./cn.js` where the source, the docs and the copy-in registry all want `./cn`. RollingNumber is the first component: a number whose digits roll to their new values. Each digit is a column of ten moved by a share of its own height, so nothing is measured, and the clipping is done inside the column so the number still sits on the baseline of the text beside it. The contract tests cover what both ways of shipping the source depend on: every entry is a client module, every relative import is one the build and the registry can follow, and every npm import is declared. CI builds the package, which is the only place an import the build cannot complete would fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The docs had one collection to describe. With panelui-web there are two, with different components, different installation and different readers, so the sidebar now shows one at a time behind a Web / App switch at its top. content/docs/web is a root folder that the docs' root meta.json does not list. Fumadocs builds unlisted folders into a fallback tree and scopes the sidebar to the root folder of the current page, so /docs/web/* shows only the web sections and no existing page moved. Web component pages are generated, like the app's: - build-web-registry.mjs turns packages/web/src into items the shadcn CLI installs, served from /r/web. Imports that reached across the package become the project's own aliases, and one it cannot translate is an error. - gen-web.mjs writes each page from the props (the same extractor, pointed at the web source), the demos in components/web-demos and a usage file. - ComponentPreview renders a demo live with its own source behind a Code tab. The source is a fenced block in the generated MDX, so what a reader copies is what they watched run. Installation covers the registry, a namespaced address, the npm package and a guide per framework, with the framework grid as the way in. The app registry builder cleared the whole of public/r before writing, which deleted r/web whenever it ran second or alone. It now removes only its own files. The web registry has a budget of its own. page-status takes the version to measure "new" against, since the web package is versioned separately from the library. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Manual tab of a web component page had the component's whole source written into the MDX as a highlighted code block. For Planet that is forty kilobytes of source in a page most readers open for the preview and the install command, and compiling it took the dev server from 1.4 GB to 3.0 GB. Regenerating the docs twice with the server running ran it out of memory. RegistrySource fetches the files from the component's registry item when the tab comes on screen. The page compiles in a tenth of the memory, and the source shown is by construction what the CLI installs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A globe with the land drawn as dots, markers pinned to coordinates, and rotation by drag, by two buttons or from the keyboard. The globe is a canvas and the markers are ordinary elements over it. Each frame the canvas is redrawn and every marker is moved by a transform written straight to its element, so turning never re-renders React. There is no loop while the globe is still: a drag, a control, a resize or a theme change asks for a frame, and a frame asks for another only while something is moving. The land is a 360 by 180 grid of one bit per square degree, generated from Natural Earth's public-domain 1:110m polygons by scripts/generate-land.mjs. A grid of points at an even pixel spacing is laid over the sphere and those on land are kept, so a small globe and a large one are dotted equally finely. The generator unwraps outlines that cross the antimeridian: read as flat coordinates, Fiji's became an edge the width of the world and a whole latitude tested as land. Dots and shading are drawn in currentColor, so the globe follows the theme with nothing passed in. To a keyboard it is a slider whose value is the longitude facing the viewer. Markers fade towards the edge and stay in the page for a screen reader when they pass behind. A vertical swipe on a touch screen scrolls the page. The lead example is a realtime visitors card with an avatar per country. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A dialog whose body is a place to write, with Cancel and Submit in the band around it. It is the web counterpart of the app's Feedback and keeps its shape: a darker shell, a well set into it holding the question, the close button and a borderless field, and two equal pills underneath. Submit is inert while the field is empty and drops its fill in that state, since a dimmed accent pill still reads as the thing to press. It hands the message to onSubmit and leaves the dialog open, so a send that fails has somewhere to say so with the message still in the field. Built on the Base UI dialog, which the package depends on directly, so it does not vary with whichever dialog a project has. `dismissible` is on the root because that is where the primitive takes it; Escape has no switch of its own there and is refused in onOpenChange. `<Feedback.Content inline>` draws the same shell in the page with no backdrop, for a feedback box at the end of an article. Command or Control with Enter sends from the field. The shell has a hairline ring. In a dark theme over a dimmed page the band and what is behind it are both nearly black, and it had no edge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`Planet.Globe` is a property of a component, and a Server Component cannot read a property off something imported from a client module. A Next.js page without "use client" that rendered `<Planet.Globe>` failed with an error that names the problem and not the fix. Every dotted part of a client component is now also a named export: `PlanetGlobe` is `Planet.Globe`, `FeedbackContent` is `Feedback.Content`. The dotted form is unchanged and is still what the docs use. Each compound component's page gains a Server Components note with its own names, generated from the parts, and the Next.js guide says which two things need a Client Component of your own. The contract tests now require a named twin for every dotted part. They also stop requiring "use client" on a component that holds no state and handles no events, which should be renderable on the server; and a demo no longer has to be a client module, so one for such a component can show that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A period drawn as a grid of pills, one per day: filled when a goal was reached, dash-outlined when it was partly reached, faint when nothing happened. The three states differ in how they are drawn as well as in tint, so the grid does not depend on colour alone. Given `onValueChange`, each pill is a button and the grid is one Tab stop with the arrow keys moving between days, so thirty pills are not thirty presses of Tab. Without it the grid is a picture, announced once as a count of how many days reached the goal. `ActivityGrid.Legend` is a sibling of the grid, not a child, so it defines the accent variable itself: a swatch whose colour names a variable nobody set is not drawn at all. This is the first of five widgets built from the health dashboard designs. They share one accent, `--panelui-accent`, which a project sets once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One reading on a track between its lowest and highest, with the number written on the thumb, so where it is and what it is are read together. With `onValueChange` it is a slider: drag it, or use the arrow keys, Page Up and Down, Home and End. Without it the same drawing is a meter that shows a value and cannot be moved. `marker` draws a dashed line at another value, such as an average. The track's gradient is centred on the value, so the deepest part of it is always under the thumb. The thumb is held half its own width in from each end: centred on the reading it hung off the track, and off the card, at the lowest and highest values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A short run of values as capsule bars with one of them picked. It looks like a bar chart and behaves as a radio group: exactly one bar is picked, the arrow keys walk the row, and the figure for the picked bar is the caller's to show beside it. Every bar is drawn inside a full-height track, so an empty day is still a shape to aim at and a bar can be judged against its own track without an axis. The scale always starts at zero, because a bar cropped at the bottom is a length that lies about the number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A row of two to four figures, each large with its label under it. The first lines up with the left edge and the last with the right, so the strip shares the margins of whatever it sits in. It is a description list with the label before the value in the markup and the value drawn above it with CSS, so a screen reader hears "Average session, 9 minutes". It holds no state, so it is not a client module: a Server Component can render it, dotted parts and all, and it adds no JavaScript to the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stack of cards with one open and the rest folded to single rows that narrow with their distance from it, so they read as cards lying behind the open one. An item is one rounded surface that changes shape. The pill's row collapses as the panel's opens, in the same transition, so the surface is seen growing into the card and is not swapped for it. To everything but the eye it is an accordion: each pill is a button that reports whether its panel is expanded, each panel is a region named by its pill, and a folded panel is inert so it cannot be tabbed into while it cannot be seen. The lead example composes the other four widgets into the dashboard the designs show: zones in Forecast Bars, the last reading in a Stat Strip, and the daily average on a Range Meter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The framework pages were listed under Installation in the sidebar as well as in the grid on the Installation page, which is the same seven links twice. The sidebar now has the one entry, and a framework is chosen from the grid. The pages stay in the page tree and are hidden in the sidebar with CSS. The tree is also how the layout decides which section a page belongs to, so a framework page left out of it showed the app's sidebar. Installation stays highlighted while a framework page is open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A long message scrolled the field itself, which put the browser's own scrollbar down the inside of the well. The field is now always as tall as what is written in it, and it sits in a scroll area that does the scrolling, so the bar is a drawn one: a thin rounded thumb beside the text that shows while the pointer is over the field or the text is moving. `maxHeight` is how tall the field may grow before the area round it scrolls, 360 unless set. The field shrinks again when the text is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…roles The first pass of the widgets followed its reference designs too closely: the same shapes, the same sample text and one orange accent on everything. This replaces them with designs of their own, coloured from the theme. Every widget takes `color`, one of primary, info, success, warning and destructive, in place of a single accent variable. Registry items that use success, warning or info carry those three tokens, which a stock shadcn theme does not define. - Day Grid (was Activity Grid): a field of dots sized by level, with a lens that swells the dots near the pointer and a tag for the day under it. - Range Meter: a ruler of ticks that rises where the reading is, with the number in a tag above it that stays inside the ruler at both ends. - Bar Picker (was Forecast Bars): columns of lit segments, the picked column wider and at full strength. - Stat Strip: label over figure with rules between, a `Trend` part that takes the change as a number and colours it by `goodDirection`, and a vertical orientation. It sizes itself by its own width. - Fold Stack (was Pill Stack): a deck with one card in front and the next two as edges under it, and a row of tabs to bring any card forward. The deck keeps one height. - Planet: a lit sphere with a light round its edge in the theme colour, markers that take `color` and `pulse`, labels tied to their pins, joined controls, and pins on an opaque surface. A pin's fill was the theme's 4% tint, so the land showed through whatever the pin held. All demos have their own subjects and data. Computed tick sizes are rounded so the server and the browser write the same markup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The same frame the Bar Picker's demo is in: a band that names what is inside, round a panel that holds the figure, the grid and its legend. The two widgets sit on pages one after the other, and with one in a frame and the other in a plain card they read as two different libraries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… globe A marker given `onClick` is now a button. Pressing its pin or its label calls it, it can be reached with Tab, and `selected` draws it as the one that has been picked. The press is kept from the globe, which took it as the start of a drag, so the click never arrived. Markers on the far side are inert and cannot be tabbed to. The light added inside the globe's edge washed over the land there, where the dots are already at their faintest. It is gone, and the light round the outside is fainter. The lead demo picks a region when its marker is pressed, turns the globe to it and shows how it is answering. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deck of cards with a row of tabs read as tabs over a plain card. This goes back to what the component is for: a stack of one-line summaries, one of them opened out. An item is a single surface. Folded it is a row with an icon in a tinted tile, a name, a figure and four lights that show how high the figure is. Open, the same surface is as tall as its panel, and the folded rows narrow the further they are from it. `Summary` takes `icon`, `value` and `level`; `Panel` holds anything, and the lead demo puts a Range Meter in it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rid's The lead demos of Range Meter and Stat Strip are now in the frame the Day Grid and Bar Picker demos use: a band that names what is inside, round a panel that holds it. Day Grid's frame is the width of the others. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Preview and Code tabs are gone. A preview is now one frame: the component running on top, and under it the first lines of its source with line numbers, fading out behind a View Code button. Pressing it opens the whole file, with a copy button and a Collapse control. Behind a tab the code was something a reader had to ask for, and the component left the screen when they did. Under it, they can see that the code is there and read it with the component still in view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five designs of it were tried and none was right. It is taken out of the collection, and a board of lanes with cards that can be dragged between them takes its place in a following commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Components was listed under the Components heading, as if the index were one of the things it indexes. It is now in Get started, as it is for the app. The page is a gallery in the shape of the app's, by the same sections. Where the app's cards draw a silhouette, these run the component itself, from a small `thumb` demo written for the card, with its summary under it. The card is one link to the component's page, so the component in it takes no pointer or focus. The generator fails for a component that has no thumb. The section headings move to one file both galleries read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stat Strip was a row of figures and nothing else. It is replaced by KPI, which has the parts and the names of the app library's: Header, Icon, Title, Actions, Content, Stat, Value, Trend, Chart, Progress, Footer, Separator and Group. A metric written for one platform reads the same on the other. Trend takes the change as a number and colours it by `goodDirection`, as a line of colour or as a badge with an arrow. Chart is a sparkline of a list of numbers, in a fixed box beside the number or the width of the card under it. Progress is a bar towards a target. It is still not a client module. The page moves from /docs/web/components/stat-strip to .../kpi. Nothing links to the old address: the collection has not been published. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wide Day Grid takes `rows`, which fills the grid column by column. With seven, each column is a week, so a long period is wide and short where it was a tall stack of week rows. The arrow keys follow whichever way the days run. The Day Grid lead demo is 16 weeks across in a wide frame. The Planet lead demo puts the globe beside the reading and a list of the regions, which pick a region as its marker does; it stacks on a narrow screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lanes side by side, each a tray with a title, a count and its cards. A card is dragged to another place in its lane or into another lane. The board is given the lanes and says what they should become, through `onLanesChange` and, for the one card that moved, `onMove`. It keeps nothing the caller did not give it, so a move is refused by not applying it. Dragging uses pointer events and no library. A carried card is lifted and follows the pointer, a gap opens where it would land, and the cards round it slide to their new places. A finger picks a card up by resting on it, so a swipe still scrolls. Space or Enter picks a card up for the arrow keys, and each step is announced. Escape puts a carried card back either way. Parts for the inside of a card: Title, Code, Row, Steps, Tag and Meta. A component page can now ask for the full width, which this one does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A canvas of nodes joined by edges, with the parts and props of the app library's Flow: Background, Node, Handle, Edge, Group, Controls and MiniMap. The pane pans by dragging and zooms with a pinch or with Control and the wheel; the plain wheel is left to the page. Nodes are dragged, and moved with the arrow keys. A connection is drawn from a handle to another node and reported through `onConnect`, with `isValidConnection` to refuse it. Edges are drawn from the measured boxes of the nodes they join, so they follow a node whatever is inside it. The routing maths is the app's, ported without its worklet directives: bezier, smoothstep, step and straight, with the faces chosen from where the two nodes are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The web section's Get started gains four pages. Skills documents a real skill, `panelui-web`. Its component references are generated by `build-web-skill.mjs` from the same source as the docs pages: the props read off the TypeScript, the usage files and the lead demos. The manifest, setup and theming files beside them are written by hand. It runs in `docs:generate` and the docs prebuild, and is copied to the site with the other skills. MCP documents the shadcn MCP server with a `@panelui` registry namespace in `components.json`, which the existing registry already serves: view, search and add by name were checked with the CLI against a local copy. Style covers the colour prop, className, the variables meant to be set, editing the copied source, and the frame. Theme covers the five roles, the three tokens a stock theme lacks, and light and dark. The source-reading code the page generator had is shared with the skill builder through `web-source.mjs`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pipeline was taller than its canvas, so fitting it into view scaled it to about two thirds and the text in its nodes was too small to read. The canvas is taller and the stages are closer together, which fits it at a zoom of 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tilt Card is a card that leans a few degrees towards the pointer, with a soft light under it in the theme colour. `TiltCard.Layer` lifts something off the card by a depth, and layers further out move further as the card leans. It does nothing under a finger or under reduced motion. Command Dock is a small button that opens into a field, on one surface that widens and then grows downwards. `onSubmit` is handed what was typed, and the rows under the field are the caller's: a row slides in when it is added, so rows added one by one read as an answer being written. Escape and a press outside close it. The two are in one commit because the index, the registry listing and the skill are regenerated from every component at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closing swapped the panel for the button in one frame while the width was still animating, so the dock dropped to one row instantly and then slid narrower. Opening had the same cut the other way round. The button and the panel are now both kept in the page. The field sits exactly over the label and the two fade through each other, and the rows are folded with the same grid row that opens them. Closing folds the rows first and narrows the surface after; opening is the reverse. The easing no longer overshoots. The open width is one length, `min(100%, 28rem)`. As a width of 100% with a max-width, the closing was animated from the full width of the container. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The headline covers React Native and the web, and the line under it links to the web section. A new section after the showcase shows three of the web components running, each in a Tilt Card that links to its page, with the command that adds one and links to the components and to installation. The FAQ gains an entry on what works where. Site metadata is unchanged: the collection is not published yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two things went wrong round a drop. Every lane is as tall as the tallest, so moving a card out of the tallest lane made the whole board shorter. In a drag that happened under the pointer, which could put the card in the slot beside the one aimed at, and on the drop it moved everything below the board. The board now keeps the greatest height its lanes have had once cards are set down. The lifted card disappeared where the pointer let go while the real card faded in at its place, which read as a blink. The lifted card now flies the last of the way into the gap, and the real card is shown when it lands. Putting a card back with Escape does the same to where it came from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ome page The section of running components in tilting cards is replaced by one in the manner of the section above it: isometric figures you can press, each making one claim and reading back what the press did. There are three cards. The first is the width of the section, with its words beside a bench of two machines: a key that drops component files into a tray, and five keys that pick the colour role of a component. Under it, arrow keys that walk a mark round a grid with a key that stops it gliding, and a phone beside a browser window holding the same three parts. A figure can now ask for a panel of another shape and for the part of the drawing to show, which two objects side by side need: they sit at opposite corners of the box that holds them both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A release tagged `web-vX.Y.Z` publishes `packages/web` with provenance over npm Trusted Publishing, after a typecheck, a build and the package contract test, and after checking the tag against the package version. Any other tag skips the job. Run by hand, it does a dry run. Nothing uses it yet: the collection is unreleased, and the first version has to be published by hand once, because a trusted publisher can only be set on a package that already exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…limits Where a carried card would land was worked out from where the other cards were drawn. They slide to their places, and a card read half way through its slide could be on the wrong side of the pointer: the gap moved, the card slid back, and it moved again. Cards are now measured from where they are laid out, which a slide does not change. A lane takes `color`, a dot before its title, and `limit`: the count is written against it and turns to the warning colour once the lane is over. `renderLaneFooter` draws under a lane's cards. A second example uses all three, with richer cards and a button that adds one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Six links in one row are wider than a phone, and pushed the whole page sideways by up to 62px at 320 wide. They wrap to a second row there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The board grew with its longest lane, so adding a card to a full lane pushed the rest of the page down. It now has a height (28rem unless the new `height` prop says otherwise) and every lane fills it. A lane with more cards than fit scrolls on its own, with a drawn thumb in place of the browser's bar and its cards fading towards the edge they carry on past. `height="auto"` keeps the old behaviour. A carried card scrolls the lane it is over near its top or bottom, the drop target takes the lane's scroll into account, and a card moved with the keyboard is kept in view. The lane under a carried card was marked with a ring, which the board's own overflow clipped along the top and at either end. Every lane now has a border of its own, inside its box, which firms up when a card is over it, and the place the card would land is drawn as a dashed outline in place of an empty gap. Lanes may be 12.5rem wide at the least, down from 13, so four fit a docs page at 1280 without a few pixels of sideways scroll. A tag's border is mixed with the card and not with transparent, which left a coloured fringe on its corners where a theme colour is out of sRGB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A canvas set in a padded preview frame was a box in a box, with nothing in the margin. A preview can now be `flush`: the demo is given the whole frame, edge to edge. Flow's three use it, with the canvas's own border and radius taken off. The lead example is laid out as a snake of six stages to suit the wider canvas, and its buttons sit on the canvas. Each example measures its canvas and lays the graph out across or down to suit, so it is readable on a phone and beside a sidebar as well as on a wide screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The headline was a description of the category and the paragraph under it ran to four lines. The headline is now the one thing that sets the library apart, that it feels native and has no native code in it, and the paragraph is one sentence that says what it is, that it runs in Expo Go, and that there is a web collection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previews under the theme picker were pictures: marked inert, in a column the width of the page's text, cut off at a fixed height. Every card now does what the component it stands for does. Switches switch, the slider drags and takes the arrow keys, the calendar turns its month, a message can be sent, a failed upload retried, the table sorted. Seven cards are new: a code field, a plan picker, an accordion, settings rows, a rating with a stepper and toggle buttons, steps, and storage meters. The wall runs the full width of the window, in four columns and five on a wide screen, and the columns drift slowly up and down so the section keeps showing something new. It holds still while the pointer or the keyboard is on it, and for anyone who has asked for reduced motion. On a narrow screen the columns are swiped sideways. A skip link comes before it, since it is now some sixty controls between the hero and the rest of the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first version of panelui-web has to be published by hand, because a trusted publisher can only be set on a package that exists. Its GitHub release is cut afterwards, as the record of it, and that run then tried to publish the same version again and failed. The publish step now asks the registry first. A version that is already there is reported and left alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 185 files, which is 35 over the limit of 150. To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configuration
⛔ Files ignored due to path filters (3)
📒 Files selected for processing (185)
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The previews were inert, so the accessibility check on the home page passed over them. Now that they are working controls it reads them, and it failed: a failed upload's name, the KPI deltas and the days outside the calendar's month were below 4.5 to 1 against the card. Text in a status colour is drawn in that status's -foreground token, which is the one made for text, and the outside days in the full muted colour. Avatar initials, the inactive tabs and the alert's second line sat on a muted fill in the muted text colour, which fell short in the dark and Grass themes; they use the foreground colour, dimmed. The check passes in all three families, light and dark. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The header called it React Native components for Expo, and nothing below it mentioned the web. The header, the line under the badges and the introduction now say both, there is a link to the web docs, and the web collection has a section of its own: what it is, the two ways to install it, and what is in it. The agents section names its skill and where the shadcn MCP server comes in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The web collection's pages and its registry at /r/web are served from panelui.dev. A web-v* release published the package and left the site as it was, so the shadcn CLI would have gone on handing out the old source until the next library release or a deploy by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Adds the web collection:
packages/web(npm namepanelui-web), eleven React components for the web styled with Tailwind CSS on shadcn tokens, with their docs under/docs/web, a shadcn registry at/r/web, an agent skill, and the workflow that publishes the package.The components are Bar Picker, Command Dock, Day Grid, Feedback, Flow, KPI, Lane Board, Planet, Range Meter, Rolling Number and Tilt Card.
It also changes the home page: a shorter hero, a section about the web collection, and the previews under the theme picker are now working controls in a full-width wall.
Why
PanelUI has only been for React Native. This is the first release of the same library for React on the web. KPI and Flow share their parts and props with the app library.
Type of change
Nothing in
packages/paneluichanges, sopanelui-nativeis not affected.What is in it
packages/web: the eleven components, built per file to ESM with'use client'kept, one subpath export each. Compound parts are exported by name as well as on the component, so a Server Component can import them.apps/docs: a Web section with installation per framework, MCP, Skills, Style and Theme pages, a gallery of the components running live, and a page for each one with a live preview and its source. Pages, the registry (public/r/web) and the skill (skills/panelui-web) are generated from the source.ci.ymlbuilds the web package;publish-web.ymlpublishes it on aweb-vX.Y.Zrelease.How it was tested
Run on this branch with
mainmerged in:npm run release:check,typecheck,template:parity,metadata:check,catalogue:check,themes:checknpm run build,npm run build --workspace=panelui-webnpm run test:a11y,npm test(818 pass),node --test packages/web/test/*.test.mjsnpm run docs:generate --workspace=docsleaves no diffnpm run performance:check,npm run verify:cli-packagesnpm run build --workspace=docs(the production build)npm packofpanelui-web, installed from the tarball into an empty project: all eleven subpaths import, three components render on the server, and a strict TypeScript file using every component compiles withskipLibCheckoffNot run: iOS and Android. Nothing here touches the React Native library or the example app.
Checklist
npm run typecheckpassesnpm run buildpassesnpm run docs:generate --workspace=docshas been run and the generated files are committedNotes for the reviewer
panelui-webhas to be published to npm by hand, once: a trusted publisher can only be set on a package that already exists.publish-web.ymlsays so at the top, and skips a version it finds already published.🤖 Generated with Claude Code