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

typique

v0.1.1

Published

Bundler-agnostic, zero-runtime CSS-in-TS — powered by a TypeScript plugin

Readme

Typique

Typique (pronounced /ti'pik/) is a framework- and bundler-agnostic, zero-runtime CSS-in-TS library powered by a TypeScript plugin. It generates readable, unique class names directly as completion items in your editor. Styles exist only as types, so they vanish cleanly from your final build.

Example

import type { Css } from 'typique'
import { space } from './my-const'

const titleClass = 'title' satisfies Css<{
  fontSize: '1.3rem'
  fontWeight: '300'
  padding: `calc(2 * ${typeof space}px)`
  '&:hover': {
    fontWeight: '500'
  }
}>

Anatomy:

  • titleClass follows the configurable naming convention: Typique auto-completes a readable yet unique class name in the initializer
  • 'title' is a class name suggested by Typique
  • satisfies Css<{...}> is where you declare your styles as a type.
  • & is the parent selector, interpreted according to the CSS nesting spec.

Why Typique

  • No bundler hell — ever. Requires no extra bundler or framework configuration.
  • Fast by design. Reuses data TypeScript already computes for your editor.
  • Framework-agnostic. Runs natively in .ts and .tsx; other file types (Vue, Svelte, JS) can import styles from .ts files.
  • Colocation by default. Define styles anywhere — even inside loops or functions — as long as you’re in TypeScript.
  • Feels like real CSS. Supports natural nesting (compatible with the CSS Nesting spec) and clean object syntax.
  • Zero effort SSR / RSC. Works seamlessly because it emits plain CSS, not runtime code.
  • Transparent naming. Class and variable names are readable, customizable, and visible right in your source — no magic.
  • Easy to migrate away. Generated CSS is clean, formatted, and source-ready.

Documentation

  • This README — continue reading for quick setup and basic examples
  • Framework Integration — how to use Typique in files which are not compiled by the TypeScript compiler
  • Composing Class Names — how to configure the variables naming conventions, class names patterns, and how to make them unique across multiple independent projects
  • Plugin Description — how the Typique plugin interacts with the editor, how it affects the performance
  • Configuration — complete plugin parameters reference
  • CLI — command-line interface reference

Setup

1. Install Typique

npm i typique

2. Add the TypeScript plugin

In your tsconfig.json:

"compilerOptions": {
  "plugins": [
    {
      "name": "typique/ts-plugin"
    }
  ]
}

If you're using VS Code, make sure to select the Workspace TypeScript version: Command Palette → Select TypeScript Version → Use Workspace Version.

3. Write some styles

Name your constants ...Class and ...Var to instruct Typique to suggest completion items in the constant initializers. Constants names are configurable.

import type { Css, Var } from 'typique'

const sizeVar = '--size' satisfies Var
const roundButtonClass = 'round-button' satisfies Css<{
  [sizeVar]: 32
  borderRadius: `calc(${sizeVar} / 2)`
  height: `var(${sizeVar})`
  width: `var(${sizeVar})`
}>

As you type in the opening quote in the constants initializer, you'll see the class names suggested by Typique. In WebStorm, you might need to invoke the explicit completion (Ctrl+Space) to see the suggestions.

The suggested class names are guaranteed to be unique within a project.

4. Import the generated CSS into your app

By default, Typique outputs a single CSS file named typique-output.css in your project root. Import it into your HTML template or entry point:

<html>
<head>
  ...
  <link href="./typique-output.css" rel="stylesheet">
</head>
...
</html>

You can change the output file name via the plugin configuration.

5. Add a build step

Run the following command to build the CSS file from the command line:

npx typique --projectFile ./index.ts --tsserver ./path/to/tsserver.js -- ...ts-args
  • --projectFile ./index.ts (required) — any TypeScript (.ts or .tsx) file in your project. It’s used to bootstrap the TypeScript project and initialize the Typique plugin. Common choices are your root component or application entry point. Relative paths are resolved against the current working directory. Note: don't specify here tsconfig.json, it will likely not work. See below on specifying tsconfig.json.
  • --tsserver ./path/to/tsserver.js (optional) — path to the TypeScript server executable. If not set, the script invokes import.meta.resolve('typescript/lib/tsserver.js') to discover the file.
  • ...ts-args (optional) — any valid TS server command line arguments, e.g. logging verbosity and logfile.

Example

This is how it can look like for Next.JS project with verbose logging enabled:

npx typique --projectFile ./app/layout.tsx -- --logVerbosity verbose --logFile ./tsserver.log

Specifying a custom tsconfig.json

Unlike tsc, the tsserver.js unfortunately doesn't allow specifying a custom tsconfig.json file: it locates the config file internally, when it opens the file specified by --projectFile. Usually it's the first tsconfig.json file up the directory hierarchy, which includes the specified --projectFile.

If you need a custom tsconfig.json, you may use the following workaround:

  1. Replace the original tsconfig.json with your custom file;
  2. Run npx typique;
  3. Restore the original tsconfig.json.

More examples

You can also check examples in the tests directory.

Sharing constants between CSS and runtime

Because CSS is defined as types, the task comes down to converting constants to types. The following language features can perform this for you:

typeof operator

const unit = 4
const spacedClass = 'spaced' satisfies Css<{
  padding: typeof unit // Type is 4, rendered as 4px
}>

String interpolation

This works for both types and values.

const unit = 4
const padding = `${typeof unit}em` // Type is `4em`
type Padding = `${typeof unit}em` // Same, type is `4em`

Note: the + operator produces the string and not a constant type. Make sure to always use interpolation instead.

Computed properties

This is useful for CSS vars explained below.

const paddingVar = '--padding' satisfies Var
const spacedClass = 'spaced' satisfies Css<{
  [paddingVar]: 4
}>

Arithmetic operations

TypeScript doesn't directly support arithmetic operations on types, so it's easier to use CSS calc() function:

const unit = 4
const spacedClass = 'spaced' satisfies Css<{
  padding: `calc(${typeof unit}px * 2)`
}>

It's planned to introduce the precalculation of calc() with only constants.

Scoped classnames, React and TSX

Styles can appear in any place in the file, not only at the top-level: Typique recognizes any ... satisfies Css<{...}> expression as a CSS declaration. These expression can appear, for example, in functions, object literals, TSX properties etc.

export function Button() {
  return <button className={ 'button' satisfies Css<{
    border: 'none'
    padding: `calc(${typeof unit}px * 2)`
  }> }>
    Click me
  </button>
}

However, to provide classnames as completion items, Typique tries to recognize the following patterns:

If you define CSS in some exotic place which Typique doesn't recognize, you can proceed without the completion item. Once you complete ... satisfies Css<{...}>, Typique will validate the name and suggest the correct one in case of issues.

Nesting

The nested rules are interpreted as per the emerging CSS Nesting Module specification. Currently Typique downlevels the nested CSS rules to plain objects; the support for native nesting is planned.

const fancy = 'fancy' satisfies Css<{
  color: 'teal'
  '@media (max-width: 600px)': {
    color: 'cyan'
    '&:active': {
      color: 'magenta'
    }
  }
}>

Output:

.fancy {
  color: teal;
}
@media (max-width: 600px) {
  .fancy {
    color: cyan;
  }
  .fancy:active {
    color: magenta;
  }
}

Multiple classnames

Array and object notations are supported.

Array notation

const [rootClass, largeClass, boldClass, smallClass] =
  ['root', 'large', 'bold', 'small'] satisfies Css<{
    padding: '1rem'
    '&.$1': {
      padding: '1.3rem'
      '&.$2': {
        fontWeight: '700'
      }
    }
    '&.$3': {
      padding: '0.5rem'
      '&.$2': {
        fontWeight: '600'
      }
    }
  }>

Typique checks that all classnames are referenced, and that all references are valid.

It's possible to also reference the root classname with $0 which can be useful in the nested levels to back-reference the root:

const largeClass = 'large' satisfies Css<{
  div: {
    padding: '0.4em'
    '&.$0': {
      fontSize: '1.3em'
    }
  }
}>

Object notation

This notation is especially useful to define styles based on component props. More on that in React examples.

const buttonClasses = {
  r: 'button-r',
  b: 'button-b',
  sz: {
    lg: 'button-sz-lg',
    sm: 'button-sz-sm',
  }
} satisfies Css<{
  padding: '1rem'
  '&.$sz$lg': {
    padding: '1.3rem'
    '&.$b': {
      fontWeight: '700'
    }
  }
  '&.$sz$sm': {
    padding: '0.5rem'
    '&.$b': {
      fontWeight: '600'
    }
  }
}>

Root non-object properties (padding: '1rem' here) are associated with the first defined classname property (r: 'button-r'). It can be also directly referenced with .$r. Like with array notation, all references and classnames are checked.

Global CSS

Styles not containing non-object properties on the top-level and $-references are output as is, resulting in global CSS:

[] satisfies Css<{
  body {
    margin: 0
  }
  '.hidden': {
    display: 'none'
  }
  '@font-face': {
    fontFamily: 'Open Sans'
    src: 'open-sans.woff'
  }
}>

Typique outputs this CSS as is. You can also mix local and global classnames:

const flexClass = 'flex-0' satisfies Css<{
  display: 'flex'
  '&.hidden': {
    display: 'none'
  }
}>

This outputs:

.flex-0 {
  display: flex;
}
.flex-0.hidden {
  display: none;
}

CSS variables and theming

Typique assumes theming with CSS-variables. Similar to classes, you can declare single variables, arrays and objects of them. To make sure the type is inferred as a constant, not just string, add as const after the array or object initializer. Finally, satisfies Var signals Typique to check if it's unique among other variables also marked this way. You may think of satisfies Var as a mark of a "managed variable".

import type {Css, Var} from 'typique'

const wVar = '--w' satisfies Var
const [bgColorVar, spaceVar] = ['--bg-color', '--space'] as const satisfies Var
const themeVars = {
  bgColor: '--theme-bg-color',
  space: '--theme-space'
} as const satisfies Var

[] satisfies Css<{
  body: {
    [wVar]: '100%'
    [themeVars.bgColor]: '#ffffff'
    [themeVars.space]: '4px'
  }
  '@media (prefers-color-scheme: dark)': {
    body: {
      [themeVars.bgColor]: '#303030'
    }
  }
}>

Just like classnames, completion items are shown for names which follow the configured pattern varNameRegex/cssVars, which is by default Vars?([Nn]ames?)$. There are also configs to define the generated variable name. See ComposingClassNames.

Referencing any identifier

You can use $-references to reference any identifier (not just class names). This is useful for things like keyframes and layers, which are otherwise global:

const [buttonClass,] = ['button', 'cn'] satisfies Css<{
  animation: '$1 0.3s ease-in-out'
  '@keyframes $1': {
    from: {
      opacity: 0
    }
    to: {
      opacity: 1
    }
  }
}>

The explicitly ignored name (comma after buttonClass) instructs Typique to suggest the 2-places completion item inside ['']. You can also bind it to a variable if you need it in the runtime — in this case the left-hand side would be const [buttonClass, fadeInKeyframes].

Fallbacks

Use tuple notation to assign multiple values to the same property.

const c = 'c' satisfies Css<{
  color: ['magenta', 'oklch(0.7 0.35 328)']
}>

Reusing and templating CSS rule objects

Like any other TypeScript types, CSS objects can be defined as named aliases and reused multiple times; they can also be generic. Here are some examples how to use this in common patterns.

Dark theme

import type {Css, Var} from 'typique'

declare const [bgColorVar, nameVar]: Var<['--bgColor', '--name']>

type Light<Name extends string = '🖥️'> = {
  [bgColor]: '#fff'
  [name]: `"${Name}"`
}
type Dark<Name extends string = '🖥️'> = {
  [bgColor]: '#444'
  [name]: `"${Name}"`
}

const [lightClass, darkClass] = ['light', 'dark'] satisfies Css<{
  body: Light
  '@media (prefers-color-scheme: dark)': {
    body: Dark
  }
  'body.$0': Light<'☀️'>
  'body.$1': Dark<'🌙'>
}>

Classnames refactoring (planned)

Because classnames remain constants in the source code, they may get inconsistent as the project grows. Tools for project-wide classnames refactoring are planned.