Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Invoice Skeleton Block for Svelte

An invoice document that loads in place: the labels and column heads stay while the number, dates, addresses, six line items and totals come in, and Download CSV saves the lines.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Show an invoice while it loads, then swap it in row by row, with this invoice skeleton for Svelte with Ark UI. A hairline frame with a plus mark on each corner holds a document from Stylobate, a freelancers' billing tool, in five bands: the title with its number and dates, the two parties, the line items, the totals and a foot. The words every invoice has (Invoice, Issued, Due, Billed to, From, the column heads and the total labels) are real from the first paint; the number, the dates, both addresses, the six line items and the totals are Skeleton bars that become the invoice in the same cell. The subtotal, the sales tax and the total are worked out from the lines in integer cents. Once it is in, Download CSV, a Button, really saves the lines as a file. On a phone each item folds its quantity and price under the description. It's built from Skeleton, Skeleton Text, Button and Guides, with a CSS entrance only. For invoices, receipts, statements and any document that waits for a query.

FAQ

What stays local, and how do I wire it to a real query?

Nothing is fetched. The invoice is typed in LINES, Load starts a 700 millisecond timer, and when it ends the phase becomes ready and the data swaps in; Reload goes back to the bars and in again. To wire it for real, delete the timer, the Load button and the polite line, and set data-state (loading or ready) and aria-busy on the region from your query's pending state, or use the bars as a Suspense fallback and render the real content when it arrives. Download CSV is the one real action: it builds the file in the browser from the typed lines; in your app, link to the file your server makes, or stream it. Keep one component for the bars and the data so they can't drift apart.

How are the totals worked out?

In integer cents, from the lines: each line is its unit price times its quantity, the subtotal is their sum, the tax is the subtotal times the rate divided by 100 (Math.round((SUBTOTAL * TAX) / 100), never a division first), and the total is the two added. Display goes through moneyOf, which sets the dollars and the two decimals as a left-to-right string. Never add floats.

What does Download CSV save?

A text file named invoice-sb-2026-0142.csv with a header row and one row per line item: description, detail, quantity, unit price and line total, in plain dollars and cents. It is built with a Blob and a temporary link in the click handler, so no library is needed, and the polite line names the file. It is unavailable until the invoice has loaded.

Why doesn't the invoice jump when the data arrives?

Every value shares a grid cell with its bar, the item rows have a fixed height that holds a title, a detail and, on a phone, the quantity line, and the table is fixed-layout with set column widths. The addresses are three-line blocks whose bars and words share one cell. The polite line reserves two lines, so the panel is exactly as tall before, during and after loading.

Is it accessible?

The region is marked busy while it loads and carries a screen-reader-only "Loading invoice…", and the polite line announces what loaded once. The bars are hidden from assistive technology and the layer that isn't showing is invisible, so it is neither read nor focusable. The table has a hidden caption and column heads with a scope, and the totals are a description list. The pulse stops when the data is in, and under reduced motion there is no pulse, no spinner and no stagger: the swap is instant.