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

@pyreon/styler

v0.12.10

Published

Lightweight CSS-in-JS engine for Pyreon

Downloads

4,768

Readme

@pyreon/styler

Lightweight CSS-in-JS engine for Pyreon.

3.81 KB gzipped | SSR & static export ready | TypeScript strict

Installation

bun add @pyreon/styler

Quick Start

import { styled, css, ThemeContext } from '@pyreon/styler'
import { useContext, pushContext, popContext, onUnmount } from '@pyreon/core'

const Button = styled('button')`
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  border-radius: 4px;
  background: ${({ theme }) => theme.colors.primary};
  color: white;
  cursor: pointer;

  &:hover {
    opacity: 0.9;
  }
`

API

styled(tag)

Creates a styled Pyreon component from an HTML tag or another component.

// HTML tag
const Box = styled('div')`
  display: flex;
`

// Shorthand (via Proxy)
const Box = styled.div`
  display: flex;
`

// Wrapping a component
const StyledLink = styled(Link)`
  color: blue;
  text-decoration: none;
`

Dynamic interpolations

Function interpolations receive all props plus the current theme:

const Text = styled('p')`
  color: ${({ theme }) => theme.colors.text};
  font-size: ${(props) => props.$size || '16px'};
`

Polymorphic as prop

Render as a different element at runtime:

const Box = styled('div')`
  padding: 16px;
`

// Renders as a <section>
Box({ as: 'section', children: 'Content' })

Transient props

Props prefixed with $ are not forwarded to the DOM:

const Box = styled('div')`
  color: ${(p) => (p.$active ? 'blue' : 'gray')};
`

// $active is used for styling but won't appear on the <div>
Box({ $active: true })

Custom prop filtering

const Box = styled('div', {
  shouldForwardProp: (prop) => prop !== 'size',
})`
  font-size: ${(p) => p.size}px;
`

css

Tagged template for composable CSS fragments:

const flexCenter = css`
  display: flex;
  align-items: center;
  justify-content: center;
`

const Card = styled('div')`
  ${flexCenter};
  padding: 16px;
`

Supports conditional patterns:

const Box = styled('div')`
  display: flex;
  ${(props) =>
    props.$bordered &&
    css`
      border: 1px solid #e0e0e0;
      border-radius: 4px;
    `};
`

keyframes

Creates @keyframes animations:

const fadeIn = keyframes`
  from { opacity: 0; }
  to { opacity: 1; }
`

const FadeBox = styled('div')`
  animation: ${fadeIn} 300ms ease-in;
`

createGlobalStyle

Injects global CSS rules (not scoped to a class):

const GlobalStyle = createGlobalStyle`
  *, *::before, *::after {
    box-sizing: border-box;
  }

  body {
    margin: 0;
    font-family: ${({ theme }) => theme.font};
  }
`

ThemeContext & useTheme

Provides a theme object to all nested styled components via Pyreon's context system:

import { ThemeContext, useTheme } from '@pyreon/styler'
import { pushContext, onUnmount, popContext } from '@pyreon/core'

// Provide theme
pushContext(new Map([[ThemeContext.id, myTheme]]))
onUnmount(() => popContext())

Access the theme from any component:

const MyComponent = () => {
  const theme = useTheme()
  // use theme values
}

TypeScript theme augmentation

Extend DefaultTheme for strict typing across your app:

declare module '@pyreon/styler' {
  interface DefaultTheme {
    colors: { primary: string; text: string }
    spacing: (n: number) => string
  }
}

sheet & createSheet

The singleton sheet manages CSS rule injection. For SSR, use createSheet for per-request isolation:

import { createSheet } from '@pyreon/styler'

const sheet = createSheet()
const html = renderToString(App({}))
const styleTags = sheet.getStyleTag()
sheet.reset()

@layer support

Wrap all scoped rules in a CSS Cascade Layer:

const sheet = createSheet({ layer: 'components' })

How It Works

Static path (zero runtime cost)

Templates with no function interpolations are resolved once at component creation time. The CSS class, rules, and <style> element are pre-computed and cached.

Dynamic path

Templates with function interpolations resolve on every render. A cache skips sheet.prepare() and <style> element creation when the resolved CSS text hasn't changed.

CSS Nesting

Native CSS nesting is supported out of the box. The engine passes CSS through without transformation, so &:hover, &::before, nested selectors, and @media queries work as-is in all modern browsers.

const Card = styled('div')`
  padding: 16px;

  &:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  }

  & > h2 {
    margin: 0 0 8px;
  }

  @media (min-width: 768px) {
    padding: 24px;
  }
`

Benchmarks

Bundle Size

| Library | Minified | Gzipped | | ----------------------- | -----------: | ----------: | | goober | 2.32 KB | 1.31 KB | | @pyreon/styler | 10.13 KB | 3.81 KB | | styled-components | 44.93 KB | 17.89 KB | | @emotion/react + styled | 48.26 KB | 16.59 KB |

Performance (ops/sec, higher is better)

| Benchmark | styler | styled-components | @emotion | goober | | ------------------------- | --------: | ----------------: | -------: | -----: | | css() creation | 25.2M | 9.0M | 2.2M | 26K | | css() with interpolations | 24.9M | 5.6M | 2.3M | 28K | | Template resolution | 21.4M | 3.9M | — | — | | Nested composition | 8.3M | 2.2M | 1.4M | 8K | | SSR renderToString | 307K | 69K | 192K | 18K | | styled() factory | 17.3M | 109K | 933K | 18.2M |

Peer Dependencies

| Package | Version | | ------------------ | -------- | | @pyreon/core | >= 0.0.1 | | @pyreon/reactivity | >= 0.0.1 |

License

MIT