npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@weasyprint-tsx/ui

v0.2.17

Published

Print-optimized Preact components for [weasyprint-tsx](../../README.md) documents.

Downloads

674

Readme

@weasyprint-tsx/ui

Print-optimized Preact components for weasyprint-tsx documents.

This package provides layout primitives rather than a full design system. WeasyPrint's support for display: flex and display: grid is partial and comparatively expensive to lay out over large paginated documents, so every component here is built on plain block/inline-block flow, table/table-cell, floats, and CSS custom properties instead. This keeps documents fast and predictable to paginate, at the cost of the ergonomics you'd get from flex/grid in a browser context — expect to reach for ratio/basis/gap-style numeric props rather than justify-content or grid-template-columns.

import { H1, H2, Page, UL, LI, Table, Entry, BlockBox, Block } from "@weasyprint-tsx/ui";

All components accept a style prop; CSS custom properties are merged onto it via mergeStyle, so an explicit style value always wins over a shorthand prop that maps to the same variable. All components also forward an optional className, appended alongside the component's own class(es).


Page layout

Page

Wraps a page section. Maps to a <section className="wsx--page">.

<Page page="cover">
  <H1>My Document</H1>
</Page>

| Prop | Type | Description | |------|------|-------------| | page | string | Value for the CSS page property (named page rule) | | ...rest | ComponentProps<"section"> | All standard <section> attributes |

Class names: wsx--page on the <section>.

CSS variables: none — page is applied as a direct CSS property, not a custom property.


PageBreak

Forces a page break at the current position. Renders <div className="wsx--pagebreak">.

<PageBreak />

| Prop | Type | Description | |------|------|-------------| | page | string | Value for the CSS page property (named page rule) |

Class names: wsx--pagebreak on the <div>.

CSS variables: none.


Headings

H1 – H6

Render <h1>–<h6> with optional marker, color, font-size, and CSS-counter overrides.

<H1>Title</H1>
<H2 color="#1e40af" fontSize="14pt">Section</H2>
<H3 marker="§">Subsection</H3>

| Prop | Type | Description | |------|------|-------------| | marker | string | Sets data-marker attribute, rendered via a ::before rule in the stylesheet | | color | string | Text color | | fontSize | string | Font size | | count | number | Sets the heading's CSS counter to this value (e.g. to resume/restart numbering) | | ...rest | ComponentProps<"h1"> etc. | All standard HTML heading attributes |

Class names: wsx--h1…wsx--h6 on the respective heading element.

CSS variables (N = 1–6, e.g. --wsx--h1--color):

| Variable | Default | Set by | |----------|---------|--------| | --wsx--hN--color | inherit | color | | --wsx--hN--fontsize | inherit | fontSize |

Headings also drive CSS counters for automatic numbering (H1 → upper-roman, H2 → decimal, H3 → upper-alpha, H4 → decimal, H5 → lower-alpha, H6 → lower-roman) via the stylesheet's counter-reset/counter-increment rules — no additional setup needed in consuming documents.


Lists

UL

Unordered list wrapper. Children should be <LI> elements. Renders <div className="wsx--list--ul">.

<UL marker="→" indent="1cm">
  <LI>Item one</LI>
  <LI>Item two</LI>
</UL>

| Prop | Type | Default | Description | |------|------|---------|-------------| | marker | string | "-" | Default bullet for all items (overridable per-LI) | | indent | number \| string | — | Left padding of the list | | spacing | number \| string | — | Horizontal gap between marker and item content | | ...rest | ComponentProps<"div"> | — | All standard <div> attributes |

Class names: wsx--list--ul on the wrapper <div>.

CSS variables:

| Variable | Default | Set by | |----------|---------|--------| | --wsx--list--indent | 0.5cm | indent | | --wsx--list--marker-spacing | 0.1cm | spacing |


OL

Ordered list wrapper. Children should be <LI> elements. Renders <div className="wsx--list--ol">.

<OL start={1} separator=")" format={n => String(n)}>
  <LI>First</LI>
  <LI>Second</LI>
</OL>

| Prop | Type | Default | Description | |------|------|---------|-------------| | start | number | 0 | Starting counter value (first LI increments from this) | | format | (n: number) => string | (s) => \${s}.`| Formats the counter value into marker text | |separator|string|"."| Appended after the formatted marker | |indent|number | string| — | Left padding of the list | |spacing|number | string| — | Horizontal gap between marker and item content | |...rest|ComponentProps<"div">| — | All standard` attributes |

Class names: wsx--list--ol on the wrapper <div>.

CSS variables: same as UL (--wsx--list--indent, --wsx--list--marker-spacing).


LI

List item, used inside UL or OL. Renders <div className="wsx--li"> containing a <div className="wsx--li--marker"> plus the item content.

<LI>Plain item</LI>
<LI marker="★">Custom marker (inside UL)</LI>
<LI value={5}>Jump the counter (inside OL)</LI>
<LI format={n => `#${n}`}>Custom per-item format (inside OL)</LI>

| Prop | Type | Description | |------|------|-------------| | marker | string | Overrides the parent UL's marker for this item only | | value | number \| ((n: number) => number) | Sets or transforms the running counter (OL only) | | format | (n: number) => string | Overrides the parent OL's format for this item only | | ...rest | ComponentProps<"div"> | All standard <div> attributes |

Class names: wsx--li on the item <div>; wsx--li--marker on the inner marker <div>.

CSS variables: none directly — LI renders inside its parent's indent/spacing context.


Table

Table

Renders an HTML <table className="wsx--table">. Children must be <Entry> components.

<Table orientation="col" headerBg="#1e40af" borderColor="#cbd5e1">
  <Entry content={["Alice", "Engineer", "Berlin"]} />
  <Entry content={["Bob", "Designer", "Paris"]} />
</Table>

| Prop | Type | Default | Description | |------|------|---------|-------------| | orientation | "col" | "row" | "col" | "col": each Entry is a column header with its content as the rows beneath it (<thead>/<tbody> grid). "row": each Entry becomes one <tr>, its heading text is a row header (<th scope="row">) and content fills the cells across that row. | | headerBg | string | — | Background color for header cells | | cellBg | string | — | Background color for data cells | | headerFontSize | string | — | Font size for header cells | | cellFontSize | string | — | Font size for data cells | | borderWidth | string | 1px | Border width between cells | | borderColor | string | currentColor | Border color between cells | | contentClass | string | — | Extra class applied to all data cells (merged with each Entry's own contentClass) | | headerClass | string | — | Extra class applied to all header cells | | ...rest | ComponentProps<"table"> | — | All standard <table> attributes |

Class names: wsx--table always; plus wsx--table--row when orientation="col", or wsx--table--col when orientation="row" (these track the resulting border layout, not the orientation value itself). Header/data cells don't carry a fixed wsx-- class — they're styled by .wsx--table th / .wsx--table td descendant selectors — but do receive headerClass/contentClass if supplied.

CSS variables:

| Variable | Default | Set by | |----------|---------|--------| | --wsx--table--header-color | transparent | headerBg (on Table or per-Entry) | | --wsx--table--cell-color | transparent | cellBg (on Table or per-Entry) | | --wsx--table--header-fontsize | inherit | headerFontSize (on Table or per-Entry) | | --wsx--table--cell-fontsize | inherit | cellFontSize (on Table or per-Entry) | | --wsx--table--border-width | 1px | borderWidth | | --wsx--table--border-color | currentColor | borderColor |


Entry

Data row or column for Table. Renders null — Table reads its props directly and never mounts it as a real element.

<Entry content={["Name", "Role", "Location"]} headerBg="#e2e8f0" />

| Prop | Type | Description | |------|------|-------------| | content | ComponentChild[] | Cell values (rows beneath the header in "col" orientation, or cells across the row in "row" orientation) | | contentClass | string | Extra class for this Entry's data cells | | headerBg | string | Overrides Table's headerBg for this Entry | | cellBg | string | Overrides Table's cellBg for this Entry | | headerFontSize | string | Overrides Table's headerFontSize for this Entry | | cellFontSize | string | Overrides Table's cellFontSize for this Entry | | ...rest | ComponentProps<"th"> | Forwarded to the header cell (e.g. children as header label) |

Class names: none — Entry itself renders nothing; its className/contentClass are read by Table and applied to the cells it generates.

CSS variables: none of its own — see Table.


BlockBox / Block

Ratio-based multi-column layout without flex/grid: BlockBox lays out <Block> children as inline-block columns sized with calc().

BlockBox

<BlockBox gap="1cm">
  <Block ratio={2}>Wide column</Block>
  <Block ratio={1}>Narrow column</Block>
</BlockBox>

| Prop | Type | Default | Description | |------|------|---------|-------------| | basis | number | sum of children's ratio (each defaulting to 1) | Total ratio units the columns are divided into. Set explicitly to reserve empty space (e.g. columns summing to less than basis). | | align | "top" | "bottom" | "middle" | "middle" | Default vertical alignment for blocks (vertical-align), overridable per Block | | gap | string \| number | — | Horizontal gap between blocks | | children | VNode \| VNode[] | — | Block elements (or any other children, each wrapped in an equal-width column) | | ...rest | ComponentProps<"div"> | — | All standard <div> attributes |

Class names: wsx--blockbox on the container <div>; wsx--block on each generated column <div> (whether it came from a Block child or a plain child).

CSS variables:

| Variable | Default | Set by | |----------|---------|--------| | --wsx--blockbox--gap | 0 | gap | | --wsx--blockbox--align | middle | align (container default; a Block's own align overrides it locally on that block) |

Block

A single column inside BlockBox. Must be a direct child for its ratio/align to be read — BlockBox inspects each child's props itself and renders the column <div> on its behalf, rather than Block rendering its own layout.

| Prop | Type | Description | |------|------|-------------| | ratio | number | Relative width versus sibling Blocks (default 1); width is calc(100% * ratio / basis) | | align | "top" | "bottom" | "middle" | Vertical alignment override for this block only | | ...rest | ComponentProps<"div"> | All standard <div> attributes |

Class names: none of its own — see wsx--block above, applied by BlockBox.


Stack

Vertical stack of children with configurable per-child left/right margins — a display: block alternative to a flex column.

<Stack gap="4mm">
  <div>First</div>
  <StackChild align="right">Right-aligned</StackChild>
  <div>Third</div>
</Stack>

| Prop (Stack) | Type | Description | |------|------|-------------| | gap | number \| string | Vertical gap between stacked children | | align | "left" | "right" | "middle" | Default horizontal alignment for all children ("middle" centers, "left"/"right" push flush to that side) | | children | — | Any children; plain children and StackChild children are both wrapped in a "wsx--stack--child" row | | ...rest | ComponentProps<"div"> | All standard <div> attributes |

StackChild is a marker component — wrap a child in it only to override gap/align for that one item; Stack reads its props and re-renders the row itself.

| Prop (StackChild) | Type | Description | |------|------|-------------| | align | "left" | "right" | "middle" | Horizontal alignment override for this child only |

Class names: wsx--stack on the container <div>; wsx--stack--child on each generated child row.

CSS variables:

| Variable | Default | Set by | |----------|---------|--------| | --wsx--stack--gap | 5mm | gap on Stack | | --wsx--stack--ml | auto | align ("left" → 0, else auto) | | --wsx--stack--mr | auto | align ("right" → 0, else auto) |


DotLine

A repeating dot pattern, useful for fill lines in tables of contents or forms.

<DotLine width="100%" />
<DotLine inline width="4cm" lineHeight="1.2em" />
<DotLine count={3} />

| Prop | Type | Default | Description | |------|------|---------|-------------| | count | number | 0 (renders 1 line) | Number of stacked dot-line rows | | inline | boolean | false | Render a single inline-block dot line instead of count block-level rows | | width | number \| string | — | Width of each dot line | | lineHeight | string | — | Minimum height of each dot-line row | | marginBottom | string | — | Bottom margin of each dot-line row | | ...rest | ComponentProps<"div"> | — | All standard <div> attributes |

Class names: wsx--dotline on each dot-line row <div>.

CSS variables:

| Variable | Default | Set by | |----------|---------|--------| | --wsx--dotline--line-height | 1em | lineHeight | | --wsx--dotline--margin-bottom | 0.2em | marginBottom |


CodeBlock

Syntax-highlighted code block using highlight.js (Atom One Dark theme by default).

<CodeBlock language="typescript" code={`const x: number = 42;`} />
<CodeBlock language="python" code="print('hi')" bgColor="#f8fafc" color="#0f172a" />

| Prop | Type | Description | |------|------|-------------| | language | string | highlight.js language identifier (e.g. "typescript", "python", "bash") | | code | string | Source code string | | bgColor | string | Background color | | color | string | Text color | | ...rest | Omit<ComponentProps<"code">, "children"> | Forwarded to the inner <code> element |

Class names: wsx--code-block on the outer <pre>. The inner <code> gets its highlight.js token classes from hljs.highlight(), not a wsx-- class.

CSS variables:

| Variable | Default | Set by | |----------|---------|--------| | --wsx--code-block--bg | #0f172a | bgColor | | --wsx--code-block--color | #cbd5e1 | color |

Renders with break-inside: avoid to prevent page breaks mid-block.


LaTeX

LaTeX/chemistry equation rendering via KaTeX.

<LaTeX displayMode>{"E = mc^2"}</LaTeX>
<LaTeX tex="H2O" chemical />
<LaTeX aligned displayMode>{"x &= a + b \\\\ y &= c + d"}</LaTeX>
<LaTeX numberFormat={false}>{"1234567"}</LaTeX>

| Prop | Type | Default | Description | |------|------|---------|-------------| | tex | string | — | LaTeX source (takes precedence over children) | | children | string | — | LaTeX source, used if tex is not set | | displayMode | boolean | false | Render as block-level (display) equation instead of inline | | aligned | boolean | false | Wrap in \begin{aligned}…\end{aligned} | | chemical | boolean | false | Wrap in \ce{…} for chemical notation | | numberFormat | boolean | true | Auto-inserts thousands separators (\,) into bare numbers in the source | | padding | number \| string | — | Padding around the rendered equation | | ...rest | ComponentProps<"div"> | — | All standard <div> attributes |

Class names: wsx--latex on the <div>.

CSS variables:

| Variable | Default | Set by | |----------|---------|--------| | --wsx--latex--padding | 2px | padding |


Chart

Renders a chart.js chart server-side as a base64-encoded PNG <img> (via @napi-rs/canvas, no browser required).

Chart

import type { ChartConfiguration } from "chart.js";
import { Chart } from "@weasyprint-tsx/ui";

const config: ChartConfiguration = {
  type: "bar",
  data: {
    labels: ["Jan", "Feb", "Mar"],
    datasets: [{ label: "Sales", data: [12, 8, 21] }],
  },
};

<Chart config={config} width={600} height={300} />

| Prop | Type | Default | Description | |------|------|---------|-------------| | config | ChartConfiguration | — | chart.js configuration object (rendered with animation: false, responsive: false) | | width | number | 800 | Canvas width in pixels | | height | number | 500 | Canvas height in pixels | | ...rest | Omit<ComponentProps<"img">, "src"> | — | All standard <img> attributes except src |

Class names: none — plain <img>, only your own className if passed.

CSS variables: none — plain <img>, no component class.


chartFunction

Samples a numeric function into a data array for chart datasets. Produces sample + 1 values evaluated at n * step for n in 0..sample.

chartFunction(func, options?)

| Param | Type | Default | Description | |-------|------|---------|-------------| | func | (n: number) => number | — | Function to sample | | options.step | number | 1 | Multiplier applied to the index before calling func | | options.sample | number | 100 | Number of intervals (yields sample + 1 points) |

import { Chart, chartFunction } from "@weasyprint-tsx/ui";

<Chart config={{
  type: "line",
  data: {
    labels: chartFunction(n => n, { step: 0.1 }),
    datasets: [{ label: "sin(x)", data: chartFunction(Math.sin, { step: 0.1 }) }],
  },
}} />

labelFunction

Same signature as chartFunction but the sampling function may return a number or string. Use to generate axis labels.

labelFunction(func, options?)

| Param | Type | Default | Description | |-------|------|---------|-------------| | func | (n: number) => number \| string | — | Function to sample | | options.step | number | 1 | Multiplier applied to the index | | options.sample | number | 100 | Number of intervals (yields sample + 1 values) |


QrCode

Renders a QR code server-side as a base64-encoded PNG <img>, wrapped in a link to the encoded URL.

<QrCode href="https://example.com" size={150} color="#000" bgColor="#fff" />

| Prop | Type | Default | Description | |------|------|---------|-------------| | href | string | — | URL to encode, and the href of the wrapping <a> | | size | number | 200 | Target pixel size (actual output is rounded to a whole number of QR modules) | | margin | number | 4 | Quiet-zone margin in pixels | | color | string | "#000" | Module (foreground) color | | bgColor | string | "transparent" | Background color | | ...rest | Omit<ComponentProps<"img">, "src"> | — | All standard <img> attributes except src |

Class names: none — the <a> wrapper and <img> carry no wsx-- class, only your own className on the <img> if passed.

CSS variables: none — colors are baked into the generated PNG, not applied via CSS.


Utilities

A handful of small helpers are also exported for building custom components on top of the same conventions:

| Export | Description | |--------|-------------| | joinClasses(...classes) | Joins class names, filtering out undefined/empty values | | mergeStyle(base, extra) | Merges an inline style prop (object or string) with an object of extra properties, dropping null/undefined entries | | cssString(value) | Wraps a string in literal double quotes for use as a CSS content value, or undefined if the input is undefined | | toRomanNumberals(n, lower?) / toLowerRoman(n) | Converts a number to Roman numerals | | toAlphabetical(n, lower?) / toLowerAlphabetical(n) | Converts a number to a base-26 letter sequence (A, B, … Z, AA, …) |