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

@simple-base/css

v0.15.5

Published

Plain CSS component styles and typography for Simple Base.

Readme

@simple-base/css

Component styles and typography for Simple Base. Plain CSS, no build step, no runtime — the selectors are the API.

Styles cover components and their internal parts, including functional wrappers such as select arrows and scrollable tables. Page layouts, component galleries, and showcase helpers belong in your application.

Install

pnpm add @simple-base/css

Quick start

The package root includes the tokens and every component stylesheet. Import it once for its side effects:

import "@simple-base/css";

Or from CSS:

@import "@simple-base/css";

Neither form exports a JavaScript value or a CSS string — the import exists for its side effects.

To load only what you use, import the tokens first and then the components you need:

@import "@simple-base/tokens/css";
@import "@simple-base/css/button";
@import "@simple-base/css/badge";
@import "@simple-base/css/table";

Then apply the root class and configuration attributes:

<button class="sb-button" data-variant="primary" data-size="medium">Save</button>
<span class="sb-badge" data-variant="success" data-size="small">Active</span>

Theming

Colors come from @simple-base/tokens, which ships nine themes. Set data-theme on the root or on any nested region:

<html data-theme="nord"></html>

Cascade layer

Every Simple Base rule lives in the sb cascade layer. The library declares that layer; your application decides where it sits by declaring the full order before the imports:

@layer theme, base, sb, components, utilities;

@import "tailwindcss";
@import "@simple-base/css";

That order keeps resets out of the way and puts your own styles last:

| Layer | Holds | Relative to sb | | ------------ | ------------------------ | ---------------- | | theme | Tailwind theme variables | before | | base | Tailwind preflight reset | before | | sb | Every Simple Base rule | — | | components | Application components | after | | utilities | Tailwind utilities | after |

Layer priority is compared before specificity for normal declarations, so a later layer wins a conflict even when the earlier rule is more specific. Tailwind's preflight padding: 0 on * cannot strip a card's padding, while p-0 on that same card can set it.

Customize anything the library styles with one of these. None of them needs !important:

<!-- Tailwind utility -->
<div class="sb-card p-0">…</div>
/* Application component, in the components layer */
@layer components {
  .card-flush {
    padding: 0;
  }
}
/* Unlayered application CSS — always wins */
.checkout .sb-card {
  padding: var(--sb-semantic-space-4);
}
<!-- Inline style -->
<div class="sb-card" style="padding: 1rem">…</div>

The library sits before all of your layers, so it never needs !important. Reserve that for conflicts between your own styles, such as one utility that has to beat another.

If another layered framework shares the page, name its layers in the same statement and place them deliberately instead of relying on import order.

Migrating to 0.2.0

Card padding is no longer an option — it is part of the .sb-card surface:

  • data-padding was removed from this package.
  • CardOptions.padding and cardDefaults were removed from @simple-base/contracts.
  • Base and flat cards that were previously unpadded now receive the stylesheet's default interior padding.
  • Rule cards keep top-only padding.
  • variant is unchanged: flat, rule, or omitted for the base surface.

If a layout needs the previous unpadded look, override it from your own styles:

<!-- Tailwind utility, given the layer order above -->
<div class="sb-card p-0">…</div>
/* Application component */
@layer components {
  .card-flush {
    padding: 0;
  }
}

/* Unlayered application CSS */
.checkout-summary .sb-card {
  padding: 0;
}

Individual stylesheets

Each entry point is an explicit, extensionless subpath. The internal styles/ directory and .css filenames are not public import paths.

alert-dialog · badge · breadcrumb · button · card · checkbox · combobox · date-picker · dialog · disclosure · empty-state · field · input · keyboard-shortcut · menu · number-field · pagination · progress · radio · range · segmented-control · select · status · switch · table · tabs · textarea · tooltip · typography

Design conventions

The default Simple Base themes follow the Studio direction: warm paper neutrals, a forest green accent, and type chosen for legibility.

  • Body text uses Atkinson Hyperlegible Next and code uses Atkinson Hyperlegible Mono. .sb-display and .sb-heading-1 through .sb-heading-3 use the Young Serif display face at a regular weight; .sb-heading-4 through .sb-heading-6 stay in the body face. Headings are sized 36, 28, 24, 20, 18, and 16px. The package names these families but does not download them — load the fonts in your app, or the system fallbacks apply.
  • Write captions, table headings, and optional dialog kickers in sentence case. Monospace is for code, identifiers, shortcuts, and alignment-dependent values.
  • Status never relies on color alone. Success, warning, danger, and info badges include a glyph, field errors lead with a warning glyph, and the switch thumb shows a check or a cross. Glyphs, status dots, and alert marks take the status base color, so the status stays visible in themes whose palette color is too light or dark to set the label in.
  • The accent badge is a solid accent fill, like the primary button, so it stays readable in every ported palette.
  • Depth comes from edges, not blur. Cards use a border and a 1px baseline; blurred shadows are reserved for menus, popovers, dialogs, and toasts. Modal backdrops separate context with a scrim, not blur.
  • Every .sb-card ships its own interior padding; override .sb-card from application CSS when a layout needs different spacing.
  • Buttons stay in place on hover; pressing scales them slightly, except under prefers-reduced-motion. Disabled buttons drop their color rather than fading, so the label stays readable.
  • Focus is a solid 2px ring in the theme's focus color. Standalone controls offset it by 2px; bordered fields draw it flush with their border.

Selector conventions

  • .sb-<component> is the required root class.
  • .sb-<component>-<part> is a part of that component.
  • Configuration uses data-* attributes; state uses data-*, aria-*, or native pseudo-classes (:checked, :disabled, :hover, :focus-visible, [open]).
  • Element selectors only appear as descendants of a root class, to style the structure a component renders. They are not standalone hooks.

Variants and options

| Attribute | Component | Values | | -------------- | ------------------------------ | ------------------------------------------------------------------------------------------ | | data-variant | .sb-badge | default, success, danger, warning, info, accent, command, outline, muted | | data-variant | .sb-button | primary, secondary, tertiary, ghost, danger, danger-subtle | | data-variant | .sb-card | flat, rule | | data-variant | .sb-table cell | code, number | | data-size | .sb-badge | small, medium | | data-size | .sb-button | small, medium, large | | data-variant | .sb-menu-item | danger | | data-status | .sb-status-line, .sb-alert | success, warning, danger, info | | data-status | .sb-toast | success, warning | | data-status | .sb-progress | warning, danger, info; omit for success |

State attributes

| Selector | Applies to | | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [aria-invalid="true"] | .sb-input, .sb-textarea, .sb-checkbox, .sb-radio | | [data-invalid] | .sb-select-control, .sb-combobox-control, .sb-number-field-control, .sb-date-picker-control, .sb-field-description | | [aria-selected="true"] | .sb-tab, .sb-table tbody tr | | [aria-pressed="true"], [aria-checked="true"], [data-state="checked"] | .sb-segment | | [aria-sort="ascending"], [aria-sort="descending"] | .sb-table th (styles its .sb-table-sort-button) | | [aria-current="page"] | .sb-page-button, .sb-breadcrumb descendants | | [aria-disabled="true"], :disabled | .sb-button, .sb-select, .sb-tab, .sb-segment, .sb-page-button, form controls | | [data-disabled] | .sb-tab, .sb-segment, .sb-page-button, .sb-menu-item, .sb-number-field-control, .sb-number-field-trigger, .sb-date-picker-control, .sb-date-picker-nav-trigger, .sb-date-picker-cell-trigger; on .sb-field-label, .sb-field-title, .sb-field-description, or any ancestor of them | | [data-state="open"] | .sb-select-trigger, .sb-combobox-trigger, .sb-date-picker-trigger, .sb-tooltip-content | | [data-highlighted] | .sb-select-item, .sb-combobox-item, .sb-menu-item | | [data-placeholder-shown], [data-required] | .sb-select, .sb-combobox | | [data-required] | .sb-field-label, .sb-field-title | | [data-state="indeterminate"] | .sb-progress | | [data-instant] | .sb-tooltip-content (skips the enter animation) | | [data-readonly] | .sb-date-picker (removes the trigger's hover state) | | [data-inline] | .sb-date-picker-content | | [data-view], [data-selected], [data-today], [data-outside-range], [data-in-range], [data-range-start], [data-range-end], [data-unavailable] | .sb-date-picker-cell-trigger | | [open] | .sb-dialog-content, .sb-alert-dialog-content, .sb-disclosure, .sb-menu | | [data-state="open"], [data-state="closed"] | .sb-toaster .sb-toast |

Component reference

| Component | Root | Parts | | ----------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Alert dialog | .sb-alert-dialog-content | -trigger, -icon, -header, -kicker, -title, -description, -footer, -cancel, -action | | Badge | .sb-badge | — | | Breadcrumb | .sb-breadcrumb | -list, -item, -link, -separator | | Button | .sb-button | — | | Card | .sb-card | — | | Checkbox | .sb-checkbox | — | | Combobox | .sb-combobox | -control, -input, -trigger, -content, -list, -item, -empty | | Date picker | .sb-date-picker | -control, -input, -trigger, -content, -calendar, -view-control, -view-trigger, -nav-trigger, -table, -table-header, -table-cell, -cell-trigger, -presets | | Dialog | .sb-dialog-content | -trigger, -header, -title, -kicker, -description, -footer, -close, -action | | Disclosure | .sb-disclosure | -copy | | Empty state | .sb-empty-state | -mark (alias .sb-empty-mark), -title, -description, -actions | | Field | .sb-field, .sb-fieldset | -label, -title, -description, -error, .sb-choice-list, .sb-choice, .sb-visually-hidden | | Input | .sb-input | — | | Keyboard shortcut | .sb-shortcut | — | | Menu (popup) | .sb-menu-content | .sb-menu-item, -item-shortcut, -group-label, -separator | | Menu (native) | .sb-menu (on details) | -panel, -item, -item-shortcut, -group-label, -separator | | Number field | .sb-number-field | -control, -input, -affix, -trigger | | Pagination | .sb-pagination | .sb-page-button, .sb-page-ellipsis | | Progress | .sb-progress | -value | | Radio | .sb-radio | — | | Range | .sb-range | .sb-range-readout | | Segmented control | .sb-segmented-control (alias .sb-segmented) | .sb-segment | | Select | .sb-select-root (compound) | -control, -trigger, -value-text, -indicator, -content, -list, -item, -empty | | Select (native) | .sb-select-wrap | .sb-select | | Status | .sb-status-line, .sb-alert, .sb-toast | -dot, -icon, .sb-status-line-content, .sb-status-line-title, .sb-alert-mark, .sb-alert-content, .sb-alert-title, .sb-alert-actions, .sb-alert-close, .sb-live-region | | Toast | .sb-toaster (region), .sb-toast | -icon, -content, -title, -description, -action, -close | | Switch | .sb-switch | — | | Table | .sb-table-wrap, .sb-table | .sb-table-sort-button, .sb-table-sort-indicator | | Tabs | .sb-tabs | .sb-tab, .sb-tab-panel | | Textarea | .sb-textarea | — | | Tooltip | .sb-tooltip-content | -arrow, -arrow-tip | | Typography | .sb-display, .sb-heading-1 … .sb-heading-6, .sb-text-*, .sb-code-* | — |

.sb-checkbox:indeterminate draws a dash for a partly selected group. indeterminate is a DOM property with no HTML attribute, so set it from script (input.indeterminate = true); the browser clears it when the user toggles the box.

For a sortable column, put a button.sb-table-sort-button as the only child of the th, ending with an empty span.sb-table-sort-indicator marked aria-hidden. The indicator shows both directions while unsorted and an arrow when the th has aria-sort="ascending" or "descending". Set aria-sort on the sorted column only; sorting stays in your app.

Form text

Every form component uses the same label, description, and error classes, so their text looks identical wherever it appears. Select, Combobox, Number field, and Date picker have no label, description, or error classes of their own:

<div class="sb-number-field">
  <label class="sb-field-label" for="quantity" data-required>Quantity</label>
  <div class="sb-number-field-control">…</div>
  <p class="sb-field-description" id="quantity-description">Whole units only.</p>
  <p class="sb-field-error" id="quantity-error">Enter at least 1.</p>
</div>

These classes live in the field stylesheet. The select, combobox, number-field, and date-picker stylesheets import it themselves, so loading any of them alone still styles its text.

Element selectors

These rules style the markup a component renders. Use the listed elements, or add a part class where one is available.

| Parent | Elements | | ----------------------------------------------------------------------------------------- | ---------------------------- | | .sb-alert-dialog-icon, .sb-alert-mark, .sb-select-indicator, .sb-combobox-trigger | svg | | .sb-menu-item, .sb-tab, .sb-page-button, .sb-empty-state-mark, .sb-alert-close | svg | | .sb-number-field-trigger, .sb-date-picker-trigger, .sb-date-picker-nav-trigger | svg | | .sb-segment | input (visually hidden) | | .sb-tooltip-content | .sb-shortcut | | .sb-breadcrumb | a | | .sb-disclosure, .sb-menu | summary | | .sb-empty-state | p | | .sb-status-line, .sb-alert, .sb-toast | strong, p | | .sb-field | label, legend, small | | .sb-fieldset | legend | | .sb-choice | span, input (via :has) | | .sb-progress | span | | .sb-table | th, td, tbody, tr |

Positioned popups

Select, combobox, date picker, menu, and tooltip content are positioned the same way: the Zag.js popper writes --x, --y, --z-index, --available-width, --available-height, and --transform-origin on the positioner, and the content reads them. The tooltip arrow reads --arrow-size and --arrow-background from .sb-tooltip-content. @simple-base/solid provides that behavior for select, combobox, date picker, menu, and tooltip. Without the Solid package, position the content yourself.

The native menu (.sb-menu on <details>) keeps its absolute .sb-menu-panel for CSS-only use. It is a disclosure of buttons, not an ARIA menu.

Toast region

Toast styles ship with status. Inside .sb-toaster, toasts are positioned by the --x, --y, --scale, --height, --opacity, and --z-index variables that the Zag.js toast machine writes; @simple-base/solid provides that behavior. Outside a region, .sb-toast is a static surface for CSS-only use inside .sb-live-region.

Versioning

Simple Base is on 0.x. All its packages go to 1.0.0 together, once @simple-base/react ships the same components as @simple-base/solid. Until then a minor release can include breaking changes.

Links

License

MIT