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

@dougborg/solarized-ui

v0.8.0

Published

A small, framework-free Solarized design system: tokens, typography, components, local fonts, and a light/dark theme control.

Readme

solarized-ui

A small, framework-free design system built on the exact Solarized palette: semantic tokens, mixed IBM Plex Sans and JetBrains Mono Nerd Font typography, a few content components, and an optional light/dark theme control. It serves personal sites and internal tools, and started as the foundation of resume.dougborg.org.

See the reference page for every component in both themes.

Install

pnpm add @dougborg/solarized-ui

The package contains only built files:

| Path | Contents | | --- | --- | | dist/solarized-ui.css | Fonts, tokens, base typography, and components in one stylesheet | | dist/fonts/ | WOFF2 files and their licenses, referenced relative to the stylesheet | | dist/theme.js | Optional native module for the theme control | | dist/theme-control.html | Markup for the theme control |

Serve the stylesheet with fonts/ beside it; its font URLs are relative, so the bundle also works under a subdirectory. With a bundler, import "@dougborg/solarized-ui" resolves to the stylesheet, and the fonts resolve through the same relative URLs. Without one, copy node_modules/@dougborg/solarized-ui/dist/ into your public assets directory as part of your build. Do not hotlink assets from another deployment.

Start with this document structure:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="color-scheme" content="light dark">
  <title>Your page</title>
  <link rel="stylesheet" href="assets/solarized-ui.css">
</head>
<body>
  <a class="skip-link" href="#main">Skip to main content</a>
  <main id="main" tabindex="-1">
    <h1>Your page</h1>
    <section class="content-section" data-accent="violet">
      <h2 class="section-title">Projects</h2>
      <article class="surface">Your content</article>
    </section>
  </main>
</body>
</html>

Add page-specific layout CSS after the stylesheet. The foundation supplies typography and components, not a fixed page grid.

Theme control

Paste dist/theme-control.html before </body>. Its script tag loads assets/theme.js; adjust that path to wherever you serve the module. The shared stylesheet and control default to Dark, regardless of the system preference. The control cycles Dark → Auto → Light → Dark; Auto follows the system, including live preference changes. An explicit selection is saved in localStorage under solarized-ui-theme and restored on later visits to the same origin, including other pages using the package. Choices are not shared across different domains or subdomains, and no preference is sent to a server. Missing or invalid saved values use Dark; blocked or full storage does not prevent theme switching for the current page. Without JavaScript, the stylesheet defaults to Dark; set data-theme="auto" or data-theme="light" on <html> to opt into system colors or Light without the control. Browsers without light-dark() retain the light fallback and no unusable control. This changes the previous automatic system default; consumers must upgrade their pinned package or copied release files to adopt it. Above 860px, reserve var(--control-gutter) at the right and at least 60px at the bottom so the floating control cannot cover content. At 860px and below, the control gets its own round surface and floats over content, so pages use var(--reading-inset) on both sides instead, as .page-column does; keep at least 60px at the bottom.

Tokens and components

| Contract | Purpose | | --- | --- | | --paper, --rail, --ink, --ink-2, --ink-3 | Page, raised surface, and text roles | | --accent, --accent-ink, --rule | Controls, hover/focus, and separators | | --solarized-* | The exact 16 official Solarized sRGB values | | --tone-{blue,cyan,green,yellow,orange,red,magenta,violet} | Unmodified accent aliases, identical in light and dark | | --tint-{accent}, --section-tint | Quiet surfaces derived from each accent's hue; data-accent sets --section-tint, which only .surface[data-tint] and .card paint | | --accent-band | Six-accent hard-stop gradient (blue, cyan, green, yellow, orange, magenta) | | --sans, --mono, --text-*, --leading, --measure | Mixed typography and 68ch reading measure | | --space-*, --radius, --radius-lg | Shared spacing scale, 4px corners, and 12px panel corners | | --control-inset, --control-gutter, --reading-inset | The theme control's offset from the edge, the right gutter that keeps content clear of it on wide screens, and the even side margin used at 860px and below | | --panel, --panel-ink, --panel-border, --panel-divider | Quiet panel surface, text, edge, and row dividers | | --shadow-color, --elevation | A faint two-layer shadow in light themes; transparent in dark themes and without relative color | | .content-section, .section-title, data-accent | Accent scope and lowercase section heading | | .surface, --card-padding | Borderless grouped content with optional padding override | | .surface[data-tint] | A surface tinted from its data-accent | | .entry-heading, .entry-title, .meta | Wrapping title/metadata row | | .text-list, .supporting-details | Accomplishments and one nested detail level | | .facts | Semantic dl category/value pairs | | .stack, .cluster | Vertical and wrapping horizontal composition | | .action, .status | Outlined link/action and explicit textual status | | .nerd-icon, .section-icon | Decorative glyphs alongside visible text | | .page-column | Single reading column with the control gutter reserved; top-level blocks in its main are spaced apart | | .page-column--wide, --page-width | Add the modifier to .page-column for a 1600px outer page limit, matching the résumé and consulting site; gutters are included, while .prose retains the 68ch reading measure | | .masthead, .masthead-title, .site-nav | Site title and primary navigation; mark the current page with aria-current="page"; these links drop the resting underline because they sit apart from body text, and underline on hover | | .article-header | An article's h1 and .meta line above its body | | .post-list | A list of posts built from .entry-heading, .entry-title, .meta, and an optional summary | | .page-footer | Small, ruled site footer | | .masthead--band, .masthead-title--identity | A masthead ruled by the accent band, and a bold blue identity name | | .accent-band | The accent band as a standalone 4px rule, such as an hr | | .meta-row | Inline metadata items separated by space, not punctuation | | .timeline-list, .timeline-list--marked, .timeline-marker | An ordered list with a rail and a marker per item in its data-accent; the marked variant holds initials or a logo | | .card-grid, .card | Tinted cards with an accent edge in a grid that reflows to one column; --card-min sets the column width | | .chip-list, .chip | Static tags, optionally with a leading .nerd-icon | | .pager | Older and newer links (rel="prev", rel="next") at the end of a page | | .read-progress | A CSS-only reading bar fixed to the top; hidden without scroll timelines, under reduced motion, and in print | | .panel, .panel-title, .panel-description | A bordered, softly raised group with a title and a muted line | | .panel-rows | Put on a panel ul/ol, or on a panel whose direct child is a table (with a wrapper between them, the outer rows lose their curve and the body groups their dividers): rows with dividers, title on the start side and a value or status on the end side | | li or tr with data-state or data-accent inside .panel-rows | A marked row: the state's or accent's tint, a 4px leading edge that curves with the panel's corners (a head or caption counts as hidden only with .visually-hidden, and a caption counts as on top even with caption-side: bottom), and card ink for text; a .status--pill inside sits on the plain panel with a ring of its color | | .status[data-state] | ok, warn, down, or info: a round dot in green, yellow, red, or blue beside the label, which stays in ink | | .status--pill | A status on its tint, for standalone badges | | .callout, .callout-icon, .callout-title | An icon badge beside a headline and sentence, toned by data-state (ok, warn, or down; without one it uses the blue accent, which also serves as info); combine with .panel, and add role="status" when it updates live | | .callout--tinted | The callout fills with its state's tint and takes the same leading edge as a marked row; the icon gets a ring | | .section-label | A small, muted, sans-serif heading for a group of panels, quieter than .section-title | | .numeric | Tabular figures that never wrap, for latencies, counts, and dates | | .table--stack | A table that becomes labelled rows below 560px, using each cell's data-label; give it explicit ARIA table roles, since stacking hides its table semantics in some browsers, as reference/status.html does | | .masthead--plain | A masthead without its rule | | .visually-hidden | Hidden visually but kept for assistive technology, such as a table's header row | | .prose | Long-form writing: headings, lists, quotes, wrapped code blocks, tables, figures, rules, and footnotes | | .token.* | Prism syntax tokens, highlighted by weight and style only, never by color |

The scale is 4, 8, 12, 16, 24, 32, 44, 48px; token suffixes count 4px units. Body text is 1rem with 1.55 leading; small/section/title sizes are 0.875/1.1875/1.75rem. Use sans-serif for prose, titles within entries, and data; monospace for section labels, code, and controls. Preserve proper nouns. Use a visible label for each status: color alone must never mean success, maintenance, or failure. Use the official Solarized values for every color token, without mixing or opacity. There are two exceptions. The tints: each --tint-* keeps its accent's own hue and changes only lightness and chroma (oklch(from var(--solarized-*) L C h)), so it adds no new color; browsers without relative color fall back to the neutral raised surface. The shadow: --shadow-color is base03 with a few percent opacity (oklch(from var(--solarized-base03) l c h / 7%)) in light themes and transparent in dark ones; it is the only color with transparency, and it never carries text. Accents belong on decorative glyphs, rules, swatches, and markers. Small text and links use Solarized neutrals; underlines identify links. Status text has a colored marker, so the exact red does not have to meet text contrast on a dark surface. State labels stay in ink because Solarized green and yellow fall below 4.5:1 as small text on the light background; the dot repeats the state in color. Plain surfaces use the exact secondary background; tinted cards keep the card text color, which meets AA on every tint in both themes. Stable hue tokens retain their values across themes.

Use native headings, lists, links, and buttons. .text-list keeps native numbering on ordered lists; chevrons and nested dots apply only to unordered lists. Decorative glyphs need aria-hidden="true"; icon-only controls need an accessible name. Examples of bundled codepoints are in the reference page. The Nerd Font is a subset, so verify new glyphs before using them. Keep focus visible and controls at least 44px. This is not a complete form, navigation, or application-widget library.

Icon alignment

Center icons by their painted bounds, not their text advance or an SVG's unexamined view box. The bundled Nerd Font symbols can paint beyond their monospace advance, so flex/grid centering alone does not center the visible shape. For new icon controls, prefer SVGs generated at build time with view boxes centered on the artwork bounds, and keep glyph metrics or optical adjustments in the shared package rather than individual sites. Keep the 44px hit target separate from the visible icon size, and test every control state against its painted bounds after fonts load. Review small screenshots in both themes as well: geometric centering is a useful guard, but an asymmetric symbol can still need an intentional optical adjustment.

See the example article for the site and prose components together. For code highlighting, use a highlighter that emits Prism token classes, such as Astro's syntaxHighlight: "prism"; highlighters that write inline colors bypass the contrast rules above. Class names are global and unprefixed, so check them against any other stylesheet an application loads. The timeline is .timeline-list, not .timeline, because the résumé already uses that name. See the example status page for the quiet patterns together.

Versioning

The public API is the set of token names, class names, data-accent values, file paths under dist/, and the theme-control markup. Releases follow Semantic Versioning against that API:

| Change | Commit type | Before 1.0 | From 1.0 | | --- | --- | --- | --- | | Remove or rename a token, class, accent, dist/ path, or control markup | feat! | minor | major | | Change what a token or class means | feat! | minor | major | | Add a token, component, or icon glyph | feat | minor | minor | | Adjust appearance within the same contract | fix | patch | patch |

Pin an exact version and review updates with your application's own screenshot and accessibility checks. See the changelog for release notes.

Develop

Use the pinned Node (.nvmrc) and pnpm (packageManager).

pnpm install --frozen-lockfile
pnpm build          # dist/ (package) and site/ (reference page)
pnpm check          # Biome, rumdl, tsc, Knip, unit tests
pnpm exec playwright install chromium
pnpm test:browser   # Playwright and axe against the built reference page
pnpm preview        # http://127.0.0.1:4173

Edit src/css/fonts.css, tokens.css, base.css, and components.css; the build concatenates them in that order. src/theme.ts owns the control's behavior and src/theme-control.html its markup. reference/index.html adds examples and its own responsive layout. New components or palette changes need equivalent contrast, reflow, keyboard, and print coverage in test/browser/. The Nerd Font is a subset; see font provenance before using a new glyph.

Release

Merges to main use Conventional Commit titles. The release-please workflow keeps a release PR with the next version and changelog; merging it tags the release. It opens that PR with an installation token from the dougborg-release-please GitHub App (ID 4392719), so the required checks run on it. The App must be installed on this repository, with the repository variable RELEASE_PLEASE_APP_ID set to 4392719 and the secret RELEASE_PLEASE_APP_PRIVATE_KEY holding its private key; without them the release job fails instead of opening a release PR. The publish job then builds, checks, and stages the version on npm with trusted publishing and provenance, so the repository stores no npm token. The trusted publisher may only stage, so a maintainer approves each version with two-factor authentication before it goes live, using npm stage list and npm stage approve <stage-id> or the Staged Packages tab on npmjs.com. See staged publishing.

License

MIT. Solarized is MIT licensed by Ethan Schoonover, and the bundled fonts keep their own licenses; see third-party notices.