Skip to content

Subwaytime/tscss

Repository files navigation

tscss (TypeScript-powered CSS utilities)

Write TypeScript functions in your CSS. Compose reusable directives that generate declarations, selectors, animations, and more at build time.

Features

• ⚡ Zero Runtime — Compiles to CSS at build time via lightningcss. No client JavaScript added.
• 🖊️ CSS Native — Feels like writing plain CSS.
• 🎨 Rich Helpers — Built-in backgrounds, colors, math, transforms, borders, and layout helpers.
• 🔌 Extensible — Custom directives with defineHelper. Schema validation, aliases, and root-level CSS generation.
• 🏗️ Vite Plugin — Drop-in integration with HMR, @import inlining, and automatic directive loading.
• 🛡️ Schema Validated — Runtime argument validation with valibot. Clear error messages on misuse.
• 🏷️ Custom Properties — Emit --var declarations and @property rules automatically.

Installation

bun add @subwaytime/tscss
# or
npm install @subwaytime/tscss
# or
pnpm add @subwaytime/tscss
# or
yarn add @subwaytime/tscss

Quick Start

Add the plugin to your vite.config.ts:

import { defineConfig } from 'vite';
import { tscss } from '@subwaytime/tscss/vite';

export default defineConfig({
  plugins: [
    tscss(),
  ],
});

Now use tscss directives in your CSS:

.test {
  @paper(50px);
  color: @mix(red, blue, 50);
  border-radius: @perfectBorder(inner, 1rem, 0.25rem);
}

The plugin automatically inlines @import statements, resolves custom directives, and provides HMR for directive files.

Built-in Helpers

Backgrounds

.test {
  @paper(50px);           /* blueprint grid */
  @grid(20px);            /* line grid */
  @dotPattern(10px);      /* dot pattern */
  @checkerboard(20px);    /* checkerboard */
  @verticalLines(20px);   /* vertical stripes */
  @horizontalLines(20px); /* horizontal stripes */
}

Colors

.test {
  color: @mix(red, blue, 50);
  color: @lighten(#333, 20);
  color: @darken(#fff, 10);
  color: @opacity(red, 50);
  color: @complement(red);
  color: @black(#ff0000, 30);
  color: @white(#ff0000, 30);
  color: @tint(#333, 20);
  color: @saturate(red, 50);
  color: @grayscale(#ff0000);
}

Math & Transforms

.test {
  font-size: @pxToRem(16px);
  width: @lerp(0px, 100px, 0.5);
  margin: @negate(1rem);
  @ratio(100px, 200px);
  color: @toOklch(#ff0000);
  color: @toOklab(#ff0000);
  color: @toRgb(#ff0000);
  color: @toHsl(#ff0000);
  color: @toHwb(#ff0000);
}

Borders & Layout

.btn {
  @perfectBorder(inner, 1rem, 0.25rem);
}

.grid {
  @autoGrid(200px, 3);
}

Directive Factories

cssVar — Custom Properties

import { cssVar } from '@subwaytime/tscss/directives';

const primary = cssVar('primary');
// @primary(blue) → --primary: blue

const size = cssVar({
  name: 'size',
  transform: (v) => `calc(${v} / 16px * 1rem)`,
});
// @size(16px) → --size: calc(16px / 16px * 1rem)

const typedColor = cssVar({
  name: 'primary',
  typed: { syntax: '<color>', inherits: false, initialValue: 'blue' },
});
// @primary(red) → @property --primary { ... } + --primary: red

declaration — Fixed Property

const bg = declaration('background', (args) =>
  `linear-gradient(${args[0]}, transparent)`
);
// .test { @bg(red); } → background: linear-gradient(red, transparent);

selector — Selector Variants

const hover = selector(() => '&:hover');
// .btn { @hover() { color: red; } } → .btn:hover { color: red; }

media — Responsive Queries

const mobile = media('(max-width: 768px)');
// @mobile { padding: 1rem; } → @media (max-width: 768px) { padding: 1rem; }

keyframes — Animations

const fadeIn = keyframes('fadeIn', {
  '0%': { opacity: '0' },
  '100%': { opacity: '1' },
});
// animation: @fadeIn 0.3s ease → @keyframes fadeIn { ... }

fontFace — Font Loading

const myFont = fontFace({ src: "url('/font.woff2')", family: 'MyFont' });
// @myFont → @font-face { ... } + font-family: MyFont;

Custom Directives

import { defineHelper } from '@subwaytime/tscss/directives';

const hero = defineHelper({
  property: 'background',
  value: (args) => `linear-gradient(${args[0] || 'red'}, blue)`,
});
// .test { @hero; } → background: linear-gradient(red, blue);

Custom directives support schema validation, aliases, spawn for root-level CSS generation (e.g., @keyframes, @font-face), and .value composition.

Bare Arguments

All helpers accept natural CSS syntax without quotes:

.test {
  color: @mix(red, blue, 50);
  color: @mix('red', 'blue', '50');
  width: @lerp(0px, 100px, 0.5);
  background: @black(var(--origin), 30);
}

Custom Directive Loading

Load custom directives from a directory:

import { defineConfig } from 'vite';
import { tscss } from '@subwaytime/tscss/vite';

export default defineConfig({
  plugins: [
    tscss({
      directives: './src/css-directives',
      include: /\.css$/,
      exclude: /node_modules/,
    }),
  ],
});

The plugin loads .ts and .js files from the specified directory and makes them available as directives in your CSS.

Development

bun install

bun test

bun run bundle

License

MIT © 2026-PRESENT Leon Langer

About

TypeScript-powered CSS utilities

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors