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

@skirbi/pinta

v0.0.49

Published

Skirbi's theme song

Readme

@skirbi/pinta

Skirbi's theme song

Pinta is the theme + token engine for Skirbi.

  • Semtic provides semantic structure and components
  • Pinta provides tokens, derivations, and component recipes
  • Dibuho provides higher-level composition/layout patterns

Pinta is intentionally small.

It is not:

  • a utility framework
  • a Tailwind replacement
  • a Bootstrap clone
  • a layout engine

Pinta follows the 80% rule:

  • sensible defaults
  • clean tokens
  • predictable recipes
  • easy overrides with normal CSS

Everything is CSS. No runtime. No JS required.

General-purpose utility attributes were removed in v0.0.12. Use classes or meaningful attributes as selector hooks, then let CSS handle spacing, borders, shadows, and responsive layout.

Installation

npm install @skirbi/pinta

Import the stylesheet from the package:

@import "@skirbi/pinta/pinta.css";

Philosophy

Pinta is built around:

color families
  -> derivations
    -> component recipes

Instead of:

utility attributes
  -> inline composition

You are encouraged to use:

  • semantic HTML
  • Semtic components
  • normal CSS classes

Example:

<semtic-panel login-card>
semtic-panel[login-card] {
  max-width: 28rem;
}

Or if you prefer classes:

<semtic-panel class="login-card">
.login-card {
  max-width: 28rem;
}

Pinta intentionally embraces native CSS instead of competing with it.

Opt-in

Pinta is explicit opt-in.

Nothing happens unless [skirbi-theme] exists.

Whole page:

<html skirbi-theme>

Subtree:

<section skirbi-theme>

Named theme:

<html skirbi-theme="voodoo">

Why Skirbi and not Pinta?

The namespace skirbi- is chosen over pinta- because @skirbi/dibuho also paints the canvas and is part of the skirbi-universe. Having one namespace makes things easier.

Cascade layers

Pinta defines stable layer order:

@layer skirbi-base, skirbi-theme, skirbi-transform, skirbi-components,
  skirbi-app;

Meaning:

  • skirbi-base

    • structural defaults
    • typography
    • accessibility defaults
  • skirbi-theme

    • palette
    • tokens
    • theme overrides
  • skirbi-transform

    • color derivations and background transforms
  • skirbi-components

    • light component recipes for Semtic elements
  • skirbi-app

    • application-specific overrides

Theme authors should override in:

@layer skirbi-theme {
  [skirbi-theme="foo"] {
  }
}

For normal declarations, later layers take precedence over earlier layers. Specificity and source order still resolve competing declarations within a layer. Unlayered normal CSS takes precedence over layered normal CSS.

Theme modes

Pinta supports:

<html skirbi-theme skirbi-theme-mode="light">
<html skirbi-theme skirbi-theme-mode="dark">

Dark mode changes:

  • environment base
  • page and surface source colors
  • surface derivation lifts
  • default foregrounds

Mode can be applied to any subtree.

Pinta provides light/dark switching through skirbi-theme-mode. Toggle the attribute between light and dark; Pinta updates the environment, source colors, foregrounds, and surface derivations for that subtree.

const root = document.documentElement;
const currentMode = root.getAttribute('skirbi-theme-mode');

root.setAttribute(
  'skirbi-theme-mode',
  currentMode === 'dark' ? 'light' : 'dark',
);

Color families

Pinta uses color families instead of loose palette slots.

Primary

--skirbi-primary-main
--skirbi-primary-soft
--skirbi-primary-strong
--skirbi-primary-contrast

Secondary

--skirbi-secondary-main
--skirbi-secondary-soft
--skirbi-secondary-strong
--skirbi-secondary-contrast

Usually only *-main needs to be set.

Pinta derives:

  • soft
  • strong

unless explicitly overridden.

Contrast defaults to either --skirbi-on-dark or --skirbi-on-light. It is a selected foreground, not a calculated contrast check, so theme authors must verify contrast for their palette.

The older --skirbi-*-light and --skirbi-*-dark properties remain as deprecated aliases for soft and strong.

Supporting colors

Helper colors exist for supporting accents.

--skirbi-tertiary-main
--skirbi-tertiary-soft
--skirbi-tertiary-strong
--skirbi-tertiary-contrast
--skirbi-quaternary-main
--skirbi-quaternary-soft
--skirbi-quaternary-strong
--skirbi-quaternary-contrast

Tertiary and quaternary expose the same complete family contract as primary and secondary.

Button tokens

--skirbi-button-radius
--skirbi-button-lead-bg
--skirbi-button-lead-color
--skirbi-button-lead-hover-bg
--skirbi-button-support-bg
--skirbi-button-support-color
--skirbi-button-support-border
--skirbi-button-extra-bg
--skirbi-button-extra-color
--skirbi-button-extra-border
--skirbi-button-antagonist-bg
--skirbi-button-antagonist-color
--skirbi-button-antagonist-border

Visual tokens

--skirbi-divider-image   /* background-image for semtic-divider */

Background transforms

Pinta derives atmospheric backgrounds from brand colors.

Transform modes

<html skirbi-theme skirbi-bg-transform="none">
<html skirbi-theme skirbi-bg-transform="wash">
<html skirbi-theme skirbi-bg-transform="reverse-wash">

Wash controls

--skirbi-wash-perc: 8%;
--skirbi-reverse-wash-perc: 85%;

Default page backgrounds use a wash transform.

This keeps themes calm and readable by default.

The selector attribute changes the active percentage. Setting only --skirbi-bg-transform does not switch the transform.

For exact backgrounds, set --skirbi-page-bg, --skirbi-surface-bg, or --skirbi-surface-bg-elevated. Component and application CSS should consume the matching *-final tokens so explicit overrides and derived fallbacks both work.

Status colors

Status colors are semantic, not branding.

--skirbi-info-base
--skirbi-success-base
--skirbi-warn-base
--skirbi-error-base

They are intentionally opinionated for:

  • readability
  • contrast
  • colorblind resilience

It is strongly advised not to override them without proper testing and validating assumptions.

Typography

Typography is opt-in and token-driven.

Core knobs

--skirbi-font-scale
--skirbi-line-height

--skirbi-h1-scale
--skirbi-h2-scale
--skirbi-h3-scale
--skirbi-h4-scale

--skirbi-prose-gap

Font families

--skirbi-font-sans
--skirbi-font-mono

Form control tokens

One token sizes a field:

--skirbi-field-height

It is the height of a text input and of a select, and the checkboxes and radios take 2/3.5 of it, so they sit well inside the height of the field beside them rather than reading as a big empty box. A field is tall mostly through padding, which a choice control does not have, so a ratio near 1 would track padding the box is not carrying. Set it once and all four follow.

[skirbi-theme] {
  --skirbi-field-height: 3.5rem;
}

Unset, it works out to the padding, border and line-height of the shared control recipe, which is what these controls have always measured. The textarea is left out on purpose: it is running text sized by its rows.

Two knobs remain for the exceptions:

--skirbi-choice-size
--skirbi-field-line-height

--skirbi-choice-size sizes the choice controls directly, for when 2/3.5 of the field is not what you want. Either way the result lands in --skirbi-choice-size-final, which is what the tick and the radio dot are scaled from, so they follow the box whichever way it was set.

Invalid fields

A field reads as wrong in two cases, drawn the same way:

  • [semtic-error] holds a message
  • the browser's own constraint validation fails, e.g. pattern, required, type, min/max

The second uses :user-invalid, not :invalid, so the browser holds the styling back until the field has been left or a submit has been attempted. A half-typed value is not marked wrong on the second keystroke, and a required field is not red before it has ever been filled in.

The control border, its focus ring and the label take the error colour in both cases. Only a real message also gets the host its wash and padding, because that is what label-error-position lays out and what changes the size of the field: a native invalid state stays inside the box it already has, so nothing moves as the user tabs out of it.

--skirbi-error-base

Note that constraint validation only runs on a native form submission. A form submitted from script, e.g. a framework binding on a button, never triggers it, so :user-invalid there only fires once a field has been left.

Component recipes

Pinta ships light recipes for Semtic elements.

Examples:

  • semtic-panel
  • semtic-tile
  • semtic-terminal
  • semtic-nav
  • semtic-sidenav
  • semtic-table
  • semtic-aside

These recipes are intentionally conservative and easy to override.

Layout primitives

Semtic ships minimal layout primitives:

<semtic-grid>
<semtic-flex>
<semtic-stack>

These are structural primitives, not utility APIs.

Use normal CSS for:

  • spacing
  • layout tuning
  • responsiveness
  • composition

Pinta provides light defaults only.

The plain recipes expose --skirbi-columns, --skirbi-grid-gap, --skirbi-flex-gap, and --skirbi-stack-gap. semtic-flex retains its component-specific semtic-direction, semtic-justify, semtic-align, and semtic-wrap="wrap" mappings. There is no numbered semtic-gap mapping or responsive semtic-columns-* API.

Pinta also provides semtic-grid[diptych] and semtic-grid[triptych] recipes. Their shared layout thresholds are 48rem and 69rem.

Example:

<semtic-grid player-details>

Use normal CSS for semtic-grid[player-details] to define columns.

Accessibility defaults

Pinta ships default focus handling:

--skirbi-focus-ring-size
--skirbi-focus-ring-color

Focus styling is theme-level because it affects accessibility.

Native CSS is the escape hatch

Pinta intentionally prefers normal CSS over utility APIs.

Example:

<semtic-panel class="featured">
.featured {
  border-color: var(--skirbi-primary-main);
}

This is the preferred extension model. Whether you pick class based vs attribute based is up to you as the author.

Documentation source

lib/themes/pinta.css is the source of truth for the detailed style documentation.

The stylesheet uses DocumentCSS-style comment blocks for the public theme contract:

  • @page for the Pinta overview and layer documentation
  • @styles for token groups, cascade layers, transforms, and Semtic recipes
  • Markdown tables and examples inside the comment body

The README stays the human-facing overview. Generate the detailed style guide with npm run docs.

Example documentjs.json shape:

{
  "sites": {
    "styles": {
      "glob": "lib/**/*.{css,md}",
      "dest": "styleguide"
    }
  }
}

Removed utilities

Pinta v0.0.12 removed built-in utility attributes from the core package.

Core Pinta intentionally stays focused on:

  • tokens
  • derivations
  • recipes
  • theming