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

@stealthscale/component-typography

v0.1.0

Published

Draws the components that are text: a heading, a paragraph, a snippet of code, a key a reader is asked to press.

Readme

@stealthscale/component-typography

Text components: a paragraph, a heading, inline code, keycaps, runs of stressed, important, highlighted and quoted text, the matches of a search, an icon, a list and a block quotation. Each component binds a recipe, and a theme restyles it by extending the recipe. Every value a theme can change is an axis of the recipe, so a caller sets it as a prop. as changes the element. A component with parts is a namespace, such as Kbd.Root and List.Item.

Install

pnpm add @stealthscale/component-typography

The package peers on react, @stealthscale/theme and @stealthscale/hooks, whose useHighlight finds the matches Highlight marks. Add the preset under ./theme to the presets of the application's compiler.

Text

Text renders a paragraph in a p element. Set as="span" for text inside a line.

import { Text } from "@stealthscale/component-typography";

<Text size="lg" tone="muted" weight="medium">
  Your session ends in five minutes.
</Text>;
<Text align="center" truncate>
  Payouts settle within two business days in 34 countries.
</Text>;

truncate hides the end of the text and mask fades part of it. Show the full text elsewhere, such as in a detail view. Set the inverted ink only on a bg.inverted surface. It fails the text contrast ratio on the page.

| Axis | Values | Default | | ---------- | ------------------------------------------------------------------------------- | --------- | | size | xs, sm, md, lg, xl | md | | tone | default, muted, subtle, inverted, info, success, warning, error | inherited | | weight | normal, medium, semibold, bold | inherited | | align | start, center, end, justify | inherited | | truncate | true | off | | motion | fade, rise, reveal | none | | mask | bottom, edges, radial | none |

Heading

Heading renders a heading in an h2 element. as sets another level. size sets the prominence and is independent of the level.

import { Heading } from "@stealthscale/component-typography";

<Heading as="h1" display size="4xl">
  Welcome back, Ada
</Heading>;
<Heading as="h3" size="md" tone="muted">
  Account settings
</Heading>;

display sets the display text role: its sm step at 2xl, its lg step at 4xl and its md step at every other size. Text and Heading set overflow-wrap: anywhere, so a word wider than its container breaks.

| Axis | Values | Default | | ---------- | ------------------------------------------------------------------------------- | --------- | | size | xs, sm, md, lg, xl, 2xl, 3xl, 4xl | lg | | display | true | off | | tone | default, muted, subtle, inverted, info, success, warning, error | inherited | | effect | gradient, shine | none | | motion | fade, rise, reveal | none | | truncate | true | off |

Code

Code renders code inside a line of text in a code element. A block of code with lines, a title and a copy control is CodeBlock in @stealthscale/component-content.

import { Code, Text } from "@stealthscale/component-typography";

<Text>
  Run <Code>pnpm add @stealthscale/theme</Code> in the application's directory.
</Text>;
<Code palette="error" variant="solid">
  ENOENT
</Code>;

| Axis | Values | Default | | --------- | ---------------------------------------------------------------------------------- | --------- | | variant | solid, subtle, surface, outline, plain | subtle | | size | sm, md | md | | palette | primary, secondary, accent, neutral, info, success, warning, error | neutral |

Em

Em marks stressed text in an em element, which has the emphasis role. The recipe sets the italic face.

import { Em, Text } from "@stealthscale/component-typography";

<Text>
  The export starts <Em>after</Em> the backup finishes.
</Text>;
<Em as="i">Stealth Scale</Em>;

Set as="i" for italic text with no stress, such as a product name or a term. An i element has no role.

| Axis | Values | Default | | -------- | ------------------------------------------------------------------------------- | --------- | | tone | default, muted, subtle, inverted, info, success, warning, error | inherited | | motion | fade, rise, reveal | none |

Strong

Strong marks text as more important than the surrounding text, in a strong element, which has the strong role. The weight axis has no normal value.

import { Strong, Text } from "@stealthscale/component-typography";

<Text>
  Deleting a workspace <Strong tone="error">cannot be undone.</Strong>
</Text>;

Set as="b" for bold text with no importance, such as a keyword in a definition. A b element has no role.

| Axis | Values | Default | | -------- | ------------------------------------------------------------------------------- | ---------- | | weight | medium, semibold, bold | semibold | | tone | default, muted, subtle, inverted, info, success, warning, error | inherited | | motion | fade, rise, reveal | none |

Mark

Mark highlights text inside a line, such as a search hit, in a mark element, which has the mark role. MarkPropsProvider sets the variants of every mark below it.

import { Mark, MarkPropsProvider, Text } from "@stealthscale/component-typography";

<Text>
  Found <Mark>chassis</Mark> in 3 files.
</Text>;
<MarkPropsProvider value={{ palette: "warning", variant: "solid" }}>
  <SearchResults />
</MarkPropsProvider>;

A highlight that conveys meaning needs a second cue. The text look sets a heavier weight, and a VisuallyHidden beside the run states the meaning to a screen reader. Most screen readers announce a mark only when the user enables it, and WCAG 1.4.1 fails a distinction made by colour alone.

| Axis | Values | Default | | --------- | ---------------------------------------------------------------------------------- | --------- | | variant | solid, subtle, surface, outline, plain, text | subtle | | palette | primary, secondary, accent, neutral, info, success, warning, error | inherited | | radius | l1, l2, l3, full | l1 | | inset | none, xs, sm, md. The plain and text looks set no inset | xs | | motion | fade, rise, reveal | none | | effect | glow, shine | none |

none is for a match inside a word, where padding splits the word: "Pay" marked in "Payout" reads "Pay out" at xs.

Highlight

Highlight renders a string with every match of a search query in a Mark, so a reader sees why a result matched.

import { Highlight, Text } from "@stealthscale/component-typography";

<Text>
  <Highlight query={query}>{article.title}</Highlight>
</Text>;
<Highlight palette="warning" query={["refund", "chargeback"]} variant="solid">
  {entry.summary}
</Highlight>;

| Prop | Values | Default | | ------------ | ------------------------------------ | ---------- | | children | the text, a string | required | | query | a term, or an array of terms | required | | ignoreCase | true, false | true | | inset | the mark's insets | none | | every other | the mark's axes and a mark's props | the mark's |

  • The matching is useHighlight's from @stealthscale/hooks: every occurrence of every term, by substring, letter case ignored unless ignoreCase is false, each term trimmed, the longer term first. It matches as a filter scope keeps rows, so a result the filter keeps shows its match.
  • Every match renders in Mark with the props the caller passes, at the none inset unless the caller passes another.
  • The component renders no element of its own. The text between the matches renders as it is, so a highlight goes wherever its string would.
  • A screen reader announces a mark only where its user turns highlights on.

Not offered:

  • Marking the first occurrence alone.
  • Matching whole words only.
  • Matching cafe to café, which a filter scope does not match either.

Quote

Quote renders a quotation inside a line of text in a q element. The browser adds the quotation marks of the language in lang, so the text contains no marks. cite takes the address of the source.

import { Quote, Text } from "@stealthscale/component-typography";

<Text>
  The auditor called the report{" "}
  <Quote cite="https://example.org/audit">complete and accurate</Quote>.
</Text>;

Set marks="none" for text that contains its own punctuation, such as a quotation inside another. A quotation set as its own block is Blockquote.Root.

| Axis | Values | Default | | -------- | ------------------------------------------------------------------------------- | --------- | | marks | auto, none | auto | | tone | default, muted, subtle, inverted, info, success, warning, error | inherited | | motion | fade, rise, reveal | none |

Span

Span renders text inside a line in a span element, which has no semantics. The recipe has no size axis, so a span inherits the font of its line. Text with as="span" sets the md body size, which resets the size inside a heading.

import { Span, Text } from "@stealthscale/component-typography";

<Text>
  Due <Span weight="semibold">€1,024.00</Span>
</Text>;
<Span truncate>/var/log/nginx/access.log.2026-09-19.gz</Span>;

Use the element with semantics where one applies: Em for stress, Strong for importance, Mark for a highlight and Quote for a quotation.

| Axis | Values | Default | | ---------- | ------------------------------------------------------------------------------- | --------- | | tone | default, muted, subtle, inverted, info, success, warning, error | inherited | | weight | normal, medium, semibold, bold | inherited | | truncate | true | off | | motion | fade, rise, reveal | none |

Kbd

Kbd.Root renders one keycap in a kbd element. Kbd.Group renders a key combination: a kbd around one kbd per key, which is the HTML markup for a combination. The group sets its size, variant and palette on every keycap inside it, and a value set on a keycap takes precedence.

import { Kbd, Text } from "@stealthscale/component-typography";

<Text>
  Press{" "}
  <Kbd.Group>
    <Kbd.Root>⌘</Kbd.Root>
    <Kbd.Root>K</Kbd.Root>
  </Kbd.Group>{" "}
  to search.
</Text>;
<Kbd.Root variant="outline">Esc</Kbd.Root>;

A keycap is 19.2, 21.6 or 24px tall at sm, md and lg, so it fits a 24px line of body text. A one-character key is square. The keycap uses the body face, which renders ⌘, ⇧ and ⌥ at the height of the letters.

| Axis | Values | Default | | --------- | ---------------------------------------------------------------------------------- | --------- | | variant | raised, outline, subtle, plain | raised | | size | sm, md, lg | md | | palette | primary, secondary, accent, neutral, info, success, warning, error | neutral |

Icon

Icon renders an icon in an svg element with the img role. Pass the artwork as paths in the children, or pass an icon component through as. The icon is hidden from assistive technology by default. For an icon without text beside it, set aria-hidden={false} and aria-label.

import { StarIcon } from "lucide-react";

import { Icon } from "@stealthscale/component-typography";

<Icon aria-hidden={false} aria-label="Favourite" as={StarIcon} size="lg" tone="warning" />;
<Icon viewBox="0 0 24 24">
  <path d="M12 2 2 22h20Z" />
</Icon>;

An svg without a fill attribute fills with the current colour. An icon that sets fill, such as a lucide icon with fill="none", keeps it. Set mirrored on an icon that points, such as an arrow. The icon then flips in a right-to-left page.

| Axis | Values | Default | | ---------- | ------------------------------------------------------------------------------------------ | --------- | | size | inherit, xs, sm, md, lg, xl, 2xl, 3xl, 4xl | inherit | | tone | current, default, muted, subtle, inverted, info, success, warning, error | current | | motion | spin, float, twinkle | none | | mirrored | true | off |

List

List.Root renders a ul element with List.Item entries. Set as="ol" for a numbered list. The plain look removes the browser's markers, and each item renders its mark in a List.Indicator. The indicator is one line tall and centres its content. Assistive technology skips it, as it skips the browser's marker. Put the meaning of a mark, such as a done state, in the item's text.

import { CheckIcon } from "lucide-react";

import { Icon, List } from "@stealthscale/component-typography";

<List.Root as="ol" gap="sm">
  <List.Item>Verify your email address</List.Item>
  <List.Item>Connect a bank account</List.Item>
</List.Root>;
<List.Root variant="plain">
  <List.Item>
    <List.Indicator>
      <Icon as={CheckIcon} tone="success" />
    </List.Indicator>
    Unlimited invoices
  </List.Item>
</List.Root>;

| Axis | Values | Default | Styles | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------------------- | | variant | marker, plain | marker | the root and the item | | gap | xs, sm, md, lg, xl, 2xl, 3xl, 4xl | md | the root | | align | start, center, end | start | the item | | marker | disc, circle, square, dash, decimal, leading-zero, lower-roman, upper-roman, lower-alpha, upper-alpha, lower-greek | the element's | the item | | motion | rise, reveal | none | the item |

Blockquote

A block quotation is a Blockquote.Root figure that contains Blockquote.Icon, Blockquote.Content (a blockquote) and Blockquote.Caption (a figcaption).

import { Blockquote } from "@stealthscale/component-typography";

<Blockquote.Root palette="accent" variant="surface">
  <Blockquote.Icon />
  <Blockquote.Content>
    Moving billing to one ledger cut our month-end close from nine days to two.
  </Blockquote.Content>
  <Blockquote.Caption>Priya Raman, Head of Finance at Northwind</Blockquote.Caption>
</Blockquote.Root>;
  • Blockquote.Icon renders the library's quote mark when it has no children. Children replace the mark, such as a lucide icon with viewBox="0 0 24 24" on the part.
  • The icon is one line of the quotation tall at every size. At justify="start" it hangs in a start gutter beside the quotation and the caption. At center and end the icon is above the quotation.
  • The quotation is upright. Use Em for stress inside it.

| Axis | Values | Default | Styles | | --------- | ---------------------------------------------------------------------------------- | --------- | --------------------- | | variant | subtle, solid, surface, plain, glass | subtle | the root and the icon | | palette | primary, secondary, accent, neutral, info, success, warning, error | neutral | the root | | size | xs, sm, md, lg, xl | md | the root | | justify | start, center, end | start | the root and the icon | | motion | rise, reveal | none | the root |

Types

| Type | Props of | | ------------------------- | ------------------------------------------------------------------ | | TextProps | Text: the recipe's variants and a p element's props | | HeadingProps | Heading: the recipe's variants and an h2 element's props | | CodeProps | Code: the recipe's variants and a code element's props | | EmProps | Em: the recipe's variants and an em element's props | | StrongProps | Strong: the recipe's variants and a strong element's props | | MarkProps | Mark: the recipe's variants and a mark element's props | | HighlightProps | Highlight: the text, the query, ignoreCase and MarkProps | | QuoteProps | Quote: the recipe's variants and a q element's props | | SpanProps | Span: the recipe's variants and a span element's props | | Kbd.RootProps | Kbd.Root: the recipe's variants and a kbd element's props | | Kbd.GroupProps | Kbd.Group: size, variant, palette and a kbd's props | | IconProps | Icon: the recipe's variants and an svg element's props | | List.RootProps | List.Root: the recipe's variants and a ul element's props | | List.ItemProps | List.Item: an li element's props | | List.IndicatorProps | List.Indicator: a span element's props | | Blockquote.RootProps | Blockquote.Root: the recipe's variants and a figure's props | | Blockquote.IconProps | Blockquote.Icon: the icon recipe's variants and an svg's props | | Blockquote.ContentProps | Blockquote.Content: a blockquote element's props | | Blockquote.CaptionProps | Blockquote.Caption: a figcaption element's props |

Licence

MIT. See LICENSE.