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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@mincho-js/css

v0.0.4

Published

Natural CSS in the Typescript

Downloads

15

Readme

@mincho-js/css

The @mincho-js/css package provides framework-agnostic APIs for CSS-in-JS styling.

:seedling: Easy adoption from Vanilla Extract.

:syringe: Vanilla Extract with the power of a preprocessor — Features inspired by Sass, Less, Stylus, etc.

:cartwheeling: Syntax optimized for TypeScript — Carefully designed as if it were native to TypeScript.

:globe_with_meridians: Works with any front-end framework — or even without one.

:lock: Still type-safe styles for TypeScript.


Install

This package has vanilla extract as a peer dependency, so we install them together.
You'll also need to set up bundler integration.

npm install @mincho-js/css @vanilla-extract/css

# or
yarn add @mincho-js/css @vanilla-extract/css

# or
pnpm install @mincho-js/css @vanilla-extract/css

Usage

Define styles in a file named .css.ts:

// styles.css.ts
import { css } from "@mincho-js/css";

export const container = css({
  padding: 10
});

css() returns a class name, which you can import and use in your app:

// app.ts
import { container } from "./styles.css.ts";

document.write(`
  <section class="${container}">
    ...
  </section>
`);

API

css()

The css() function takes a style object and generates a unique class name for the given styles.

Usage Example

import { css } from "@mincho-js/css";

const buttonStyle = css({
  backgroundColor: "blue",
  color: "white",
  padding: {
    Block: 10,
    Inline: 20
  },
  _hover: {
    backgroundColor: "darkblue"
  }
});

cssVariant()

The cssVariant() function is used to define conditional styles. It allows easy creation of components with multiple variants.

Usage Example

import { cssVariant } from '@mincho-js/css';

const buttonVariants = cssVariant({
  primary: {
    backgroundColor: "blue",
    color: "white"
  },
  secondary: {
    backgroundColor: "gray",
    color: "black",
    "%primary &":{
      color: "white"
    }
  },
  danger: {
    backgroundColor: "red",
    color: {
      base: "white",
      "@media (prefers-color-scheme: dark)": "black"
    }
  }
});

Features

Some features are already implemented in Vanilla Extract, but we're assuming a first-time reader.

Instead, we've attached an emoji to make it easier to distinguish.

  • Vanilla Extract: :cupcake:
  • Mincho: :icecream:

1. CSS Module :cupcake:

We need to have a hash value to solve the problem of overlapping class names.
Vanilla Extract's style() is already doing a good job.

Code:

const myCss = css({
  color: "blue",
  backgroundColor: "#EEEEEE"
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  color: blue;
  background-color: #EEEEEE;
}

Identifiers can be changed with settings.

2. Unitless Properties :cupcake:

Unitless Properties is convenient because it reduces unnecessary string representations.

Code:

export const myCss = css({
  // cast to pixels
  padding: 10,
  marginTop: 25,

  // unitless properties
  flexGrow: 1,
  opacity: 0.5
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  padding: 10px;
  margin-top: 25px;

  flex-grow: 1;
  opacity: 0.5;
}

3. Vendor Prefixes :cupcake:

Vendor Prefixes is convenient because it reduces unnecessary string representations.

Code:

export const myCss = css({
  WebkitTapHighlightColor: "rgba(0, 0, 0, 0)"
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

4. Fallback Styles :cupcake:

Fallback Styles is convenient because it reduces unnecessary properties.

Code:

export const myCss = css({
  // In Firefox and IE the "overflow: overlay" will be
  // ignored and the "overflow: auto" will be applied
  overflow: ["auto", "overlay"]
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  overflow: auto;
  overflow: overlay;
}

5. Merge Values :icecream:

Inspired by the Less's Merge properties, this feature allows you to composition long split values.

  • If they end in $, they are joined by a comma
  • if they end in _, they are joined by a whitespace

Code:

export const myCss = css({
  boxShadow$: ["inset 0 0 10px #555", "0 0 20px black"],
  transform_: ["scale(2)", "rotate(15deg)"]
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  boxShadow: inset 0 0 10px #555, 0 0 20px black;
  transform: scale(2) rotate(15deg);
}

For use with Fallback Styles, use a double array.
It's automatically composited.

Code:

export const myCss = css({
  transform_: [
    // Apply to all
    "scale(2)",

    //  Fallback style
    ["rotate(28.64deg)", "rotate(0.5rad)"]
  ]
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  transform: scale(2) rotate(28.64deg);
  transform: scale(2) rotate(0.5rad);
}

6. Simply Important :icecream:

Inspired by the Tailwind's Important modifier, If ! is at the end of the value, treat it as !important.

Code:

export const myCss = css({
  color: "red!"
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  color: red !important;
}

7. CSS Variables :icecream:

Unlike Vanilla Extract's CSS Variables, it is supported at the top level.
Inspired by the SASS Variable, You can use $ like you would a variable.

The conversion to prefix and kebab-case happens automatically.

Code:

export const myCss = css({
  $myCssVariable: "purple",
  color: "$myCssVariable",
  backgroundColor: "$myOtherVariable(red)"
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  --my-css-variable: purple;
  color: var(--my-css-variable);
  background-color: var(--my-other-variable, red);
}

8. Simple Pseudo Selectors :icecream:

Simple Pseudo Selectors is convenient because these are the elements you typically use with "&", so keep it.

Inspired by the Panda CSS's Conditional Styles, _ is used as :.
However, no other classes or attributes are added, it's a simple conversion.
camelCase also convert to kebab-case.

Code:

export const myCss = css({
  _hover: {
    color: "pink"
  },
  _firstOfType: {
    color: "blue"
  },
  __before: {
    content: ""
  }
});

Compiled:

.[FILE_NAME]_myCSS__[HASH]:hover {
  color: pink;
}

.[FILE_NAME]_myCSS__[HASH]:first-of-type {
  color: blue;
}

.[FILE_NAME]_myCSS__[HASH]::before {
  content: "";
}

9. Simple Attribute Selectors :icecream:

Allow toplevel attribute selector to prevent deep nesting. It would be nice to be able to autocomplete HTML attributes.

If the start is [ without & treat it as attribute selectors. It is a continuation of Simple Pseudo Selectors.

Code:

export const myCss = css({
  "[disabled]": {
    color: "red"
  },
  `[href^="https://"][href$=".org"]`: {
    color: "blue"
  }
});

Compiled:

.[FILE_NAME]_myCSS__[HASH][disabled] {
  color: red;
}

.[FILE_NAME]_myCSS__[HASH][href^="https://"][href$=".org"] {
  color: blue;
}

10. Complex Selectors :cupcake: / :icecream:

Unlike Vanilla Extract's Complex Selectors, it is supported at the top level.
I want to reduce nesting as much as possible.

Exception values for all properties are treated as complex selectors.

Code:

export const myCss = css({
 "&:hover:not(:active)": {
    border: "2px solid aquamarine"
  },
  "nav li > &": {
    textDecoration: "underline"
  }
});

Compiled:

.[FILE_NAME]_myCSS__[HASH]:hover:not(:active) {
  border: 2px solid aquamarine;
}

nav li > .[FILE_NAME]_myCSS__[HASH] {
  text-decoration: underline;
}

[!WARNING] Constraints like circular reference still apply.

Complex Selectors - Reference constraints

That it inherits all of Vanilla Extract's constraints.

const invalid = css({
  // ❌ ERROR: Targetting `a[href]`
  "& a[href]": {...},

  // ❌ ERROR: Targetting `.otherClass`
  "& ~ div > .otherClass": {...}
});

// Also Invalid example:
export const child = css({});
export const parent = css({
  // ❌ ERROR: Targetting `child` from `parent`
  [`& ${child}`]: {...}
});

// Valid example:
export const parent = css({});
export const child = css({
  [`${parent} &`]: {...}
});

Complex Selectors - Circular reference

As above, Circular reference is the same.

export const child = css({
  background: "blue",
  get selectors() {
    return {
      [`${parent} &`]: {
        color: 'red'
      }
    };
  }
});

export const parent = css({
  background: "yellow",
  selectors: {
    [`&:has(${child})`]: {
      padding: 10
    }
  }
});

11. At-Rules :cupcake: / :icecream:

Allows nesting, like Vanilla Extract's Media Queries, and also allows top-levels.

Code:

export const myCss = css({
  // Nested
  "@media": {
    "screen and (min-width: 768px)": {
      padding: 10
    },
    "(prefers-reduced-motion)": {
      transitionProperty: "color"
    }
  },

  // Top level
  "@supports (display: grid)": {
    display: "grid"
  }
});

Compiled:

@media screen and (min-width: 768px) {
  .[FILE_NAME]_myCSS__[HASH] {
    padding: 10px;
  }
}

@media (prefers-reduced-motion) {
  .[FILE_NAME]_myCSS__[HASH] {
    transition-property: color;
  }
}

@supports (display: grid) {
  .[FILE_NAME]_myCSS__[HASH] {
    display: grid;
  }
}

12. Anonymous At-Rules :icecream:

Inspired by the Griffel's Keyframes, Makes @keyframes or @font-face writable inline.

fontFamily$ is used as special case of the Merge Values rule.

Code:

export const myCss = css({
  // Keyframes
  animationName: {
    "0%": { transform: "rotate(0deg)" },
    "100%": { transform: "rotate(360deg)" }
  },
  animationDuration: "3s",

  // Fontface
  fontFamily: {
    src: "local('Comic Sans MS')"
  },
  // Fontface with multiple
  fontfamily$: [{ src: "local('Noto Sans')" }, { src: "local('Gentium')" }]
});

Compiled:

@keyframes [FILE_NAME]_myCSSKeyframes__[HASH] {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

@font-face {
  src: local("Comic Sans MS");
  font-family: "[FILE_NAME]_myCSSFontFace1__[HASH]";
}
@font-face {
  src: local("Noto Sans");
  font-family: "[FILE_NAME]_myCSSFontFace2__[HASH]";
}
@font-face {
  src: local("Gentium");
  font-family: "[FILE_NAME]_myCSSFontFace3__[HASH]";
}

.[FILE_NAME]_myCSS__[HASH] {
  animation-name: [FILE_NAME]_myCSSKeyframes__[HASH];
  animation-duration: 3s;

  font-family: [FILE_NAME]_myCSSFontFace1__[HASH];
  font-family: [FILE_NAME]_myCSSFontFace2__[HASH], [FILE_NAME]_myCSSFontFace3__[HASH];
}

13. Nested Properties :icecream:

Inspired by the SCSS's nested properties, this feature allows nesting for property names.

Reduce redundancy and make your context stand out.

Uppercase it to distinguish it from Property based condition. Vendor Prefixes exists only in Top level, while Nested Properties exists only in nesting, so you can tell them apart.

Code:

export const myCss = css({
  transition: {
    Property: "font-size",
    Duration: "4s",
    Delay: "2s"
  }
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  transition-property: font-size;
  transition-duration: 4s;
  transition-delay: 2s;
}

14. Property based condition :icecream:

Inspired by the Panda CSS, You can apply properties based on selectors or at-rules.

The default properties refer to base.

export const myCss = css({
  color: {
    base: "red",
    _hover: "green",
    "[disabled]": "blue",
    "nav li > &": "black",
    "@media (prefers-color-scheme: dark)": "white"
  }
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  color: red;
}

.[FILE_NAME]_myCSS__[HASH]:hover {
  color: green;
}

.[FILE_NAME]_myCSS__[HASH][disabled] {
  color: blue;
}

nav li > .[FILE_NAME]_myCSS__[HASH] {
  color: black;
}

@media (prefers-color-scheme: dark) {
  .[FILE_NAME]_myCSS__[HASH] {
    color: red;
  }
}

15. Nested Selectors :icecream:

Inspired by the SCSS's nested selectors, this feature allows nesting for selectors.

It works with Simple Pseudo Selectors and Complex Selectors.

export const myCss = css({
  "nav li > &": {
    color: "red",
    _hover: {
      color: "green"
    },
    "&:hover:not(:active)": {
      color: "blue"
    },
    ":root[dir=rtl] &": {
      color: "black"
    }
  }
});

Compiled:

nav li > .[FILE_NAME]_myCSS__[HASH] {
  color: red;
}

nav li > .[FILE_NAME]_myCSS__[HASH]:hover {
  color: green;
}

nav li > .[FILE_NAME]_myCSS__[HASH][disabled]:hover:not(:active) {
  color: blue;
}

:root[dir=rtl] nav li > .[FILE_NAME]_myCSS__[HASH] {
  color: black;
}

16. Nested At-Rules :icecream:

Like Nested Selectors, but they are hoisted and combined into a AND rule.

Depending on the Ar-Rules keyword, the combining syntax is slightly different.
(Unlike @media, @supports, and @container, @layer is displayed like parent.child.)

Code:

export const myCss = css({
  "nav li > &": {
    color: "red",

    "@media (prefers-color-scheme: dark)": {
      "@media": {
        "(prefers-reduced-motion)": {
          color: "green"
        },
        "(min-width: 900px)": {
          color: "blue"
        }
      }
    },

    "@layer framework": {
      "@layer": {
        "layout": {
          color: "black"
        },
        "utilities": {
          color: "white"
        }
      }
    }
  }
});

Compiled:

nav li > .[FILE_NAME]_myCSS__[HASH] {
  color: red;
}

@media (prefers-color-scheme: dark) and (prefers-reduced-motion) {
  nav li > .[FILE_NAME]_myCSS__[HASH] {
    color: green;
  }
}

@media (prefers-color-scheme: dark) and (min-width: 900px) {
  nav li > .[FILE_NAME]_myCSS__[HASH] {
    color: blue;
  }
}


@layer framework.layout {
  nav li > .[FILE_NAME]_myCSS__[HASH] {
    color: blue;
  }
}

@layer framework.utilities {
  nav li > .[FILE_NAME]_myCSS__[HASH] {
    color: blue;
  }
}

It can be used with Property based condition.

Code:

export const myCss = css({
  "nav li > &": {
    color: {
      base: "red",
      "@media (prefers-color-scheme: dark)": {
        "@media (prefers-reduced-motion)": "green",
        "@media (min-width: 900px)": "blue"
      },
      "@layer framework": {
        "@layer": {
          "layout": "black",
          "utilities": "white"
        }
      }
    }
  }
});

17. Property Reference :icecream:

Inspired by the Stylus's property lookup, this feature can be used to refer to a property value.

Code:

export const myCss = css({
  width: "50px",
  height: "@width",
  margin: "calc(@width / 2)"
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  width: 50px
  height: 50px;
  margin: calc(50px / 2);
}

When used alone, like "@flexGrow", you can use the literal value it refers to.

Code:

export const myCss = css({
  flexGrow: 1,
  flexShrink: "@flexGrow"
});

Compiled:

.[FILE_NAME]_myCSS__[HASH] {
  flex-grow: 1;
  flex-shrink: 1;
}

18. Variants Reference :icecream:

Inspired by the JSS plugin nested, this feature can be reference a local rule.

Use the % symbol.

Code:

export const myCss = cssVariant({
  primary: {
    color: "red",
    ":has(%secondary)": {
      color: "blue",
    }
  },
  secondary: {
    color: "black",
    "%primary &":{
      color: "white"
    }
  }
});

Compiled:

.[FILE_NAME]_myCSS_primary__[HASH] {
  color: red;
}

.[FILE_NAME]_myCSS_primary__[HASH]:has(.[FILE_NAME]_myCSS_secondary__[HASH]) {
  color: blue;
}

.[FILE_NAME]_myCSS_secondary__[HASH] {
  color: black;
}

.[FILE_NAME]_myCSS_primary__[HASH] .[FILE_NAME]_myCSS_secondary__[HASH] {
  color: white;
}

19. CSS Composition :cupcake:

Vanilla Extract's composition is well enough made, so keep it.

Code:

const base = css({ padding: 12 });
const primary = css([base, { background: "blue" }]);
const secondary = css([base, { background: "aqua" }]);

Compiled:

.[FILE_NAME]_base__[HASH] {
  padding: 12px;
}

.[FILE_NAME]_base__[HASH] {
  background: blue;
}

.[FILE_NAME]_base__[HASH] {
  background: aqua;
}

Contributing

We welcome contributions! Please see our Contributing Guide for more details.

License

This project is licensed under the MIT License.