Skip to content

Super JSS — Atomic CSS‑in‑JS for Angular 20

npm version bundle size license

Super JavaScript Stylesheets (SJSS) is a tiny, runtime styling library for Angular 20. It generates atomic CSS as you use it, supports responsive breakpoints and theming, and gives you a minimal, ergonomic API.

  • âš¡ Angular‑native: built on Signals
  • 🎯 Atomic CSS generation: only what you use
  • 📱 Responsive + theming: xs…xxl breakpoints and palette tokens
  • 🎨 Pseudo‑selectors: &:hover, &:focus, etc.
  • 🧩 Ready‑made building blocks: <sj-box>, <sj-card>, <sj-button>

Install

npm install super-jss

Quick start

This minimal Hero shows inline [sj] styles, a one‑line theme update, and a reactive breakpoint log.

import { Component, effect, inject } from '@angular/core';
import { SJ_BASE_COMPONENTS_IMPORTS, SjThemeService, sj } from 'super-jss';

@Component({
  standalone: true,
  selector: 'app-hero',
  imports: [SJ_BASE_COMPONENTS_IMPORTS],
  template: `
    <sj-host
      [sj]="[
        sj.display(sj.display.options.flex),
        sj.flexDirection({ xs: sj.flexDirection.options.column, md: sj.flexDirection.options.row }),
        sj.justifyContent(sj.justifyContent.options.center),
        sj.alignItems(sj.alignItems.options.center),
        sj.gap({ xs: 0.5, md: 1 }),
        sj.p(2),
        sj.bg(sj.bg.options.light.light)
      ]"
    >
      <sj-box [sj]="[ sj.p(1), sj.brad(0.5), sj.bg(sj.bg.options.primary.main), sj.c(sj.c.options.primary.contrast) ]">
        <h1 [sj]="[ sj.m(0) ]">Hello SJSS</h1>
      </sj-box>

      <sj-button
        [sj]="[
          sj.p(2),
          sj.bg('primary.main'),
          sj.c('white'),
          sj.hover([ sj.backgroundColor(sj.bg.options.primary.dark) ])
        ]"
        (click)="updatePrimaryColor()"
      >
        Update Primary
      </sj-button>
    </sj-host>
  `,
})
export class HeroComponent {
  readonly theme = inject(SjThemeService);
  readonly sj = sj;

  // Log the current breakpoint reactively
  private _bp = effect(() => console.log('breakpoint:', this.theme.breakpoint()));

  // One‑liner theme update for primary color
  updatePrimaryColor() {
    this.theme.setTheme({
      palette: { primary: { ...this.theme.sjTheme().palette.primary, main: '#4e3149ff' } } as any,
    });
  }
}

Key ideas

  • camelCase CSS properties: backgroundColor, justifyContent, borderRadius
  • Responsive objects: { xs: 1, md: 2 }
  • Theme tokens: sj.palette.primary.main, sj.palette.light.dark
  • Pseudo‑selectors: sj.hover({ ... }), sj.focus({ ... })

Minimal API surface (v1)

The root API gives you two things:

1) Any CSS property function 2) A few curated shorthands with .options

import { sj } from 'super-jss';

// CSS properties
sj.backgroundColor('primary.main');
sj.padding({ xs: 1, md: 2 });
sj.width(sj.width.options.fitContent);
sj.position(sj.position.options.absolute);

// Shorthands (popular)
sj.p(2);         // padding
sj.m({ md: 1 }); // margin
sj.bg('primary.main');
sj.c('primary.contrast');

// Discoverable options for common props
sj.display.options;         // { flex, grid, block, inline, inlineBlock, contents, none }
sj.flexDirection.options;   // { row, rowReverse, column, columnReverse }
sj.justifyContent.options;  // { flexStart, flexEnd, center, spaceBetween, spaceAround, spaceEvenly }
sj.alignItems.options;      // { flexStart, flexEnd, center, stretch, baseline }
sj.width.options;           // { auto, fitContent, maxContent, minContent }
sj.height.options;          // { auto, fitContent, maxContent, minContent }
sj.position.options;        // { static, relative, absolute, fixed, sticky }

// Tokens available at root
sj.palette.primary.main;
sj.breakpoints.md;

// Composition helpers
sj.compose(
  sj.display('flex'),
  sj.justifyContent('center'),
  sj.active({ transform: 'scale(0.98)' })
);

Components and blueprints

Ship simple styles fast using built‑ins:

import { SJ_BASE_COMPONENTS_IMPORTS, sj } from 'super-jss';

@Component({
  standalone: true,
  imports: [SJ_BASE_COMPONENTS_IMPORTS],
  template: `
    <sj-box [sj]="box">Content</sj-box>
    <sj-card [variant]="'elevated'" [sj]="{ p: 1, gap: 1 }">Card content</sj-card>
    <sj-button [variant]="'containedPrimary'" [sj]="{ w: 'fit-content' }">Click</sj-button>
  `,
})
export class DemoComponent {
  box = [sj.p(1), sj.bg('light.light')];
}

Blueprints are also callable:

sj.sjCard();                 // default card
sj.sjCard.outlined();        // outlined
sj.sjCard.elevated();        // elevated
sj.sjCard.variants.info();   // variants attached under sjCard/sjButton

Responsive examples

<div
  [sj]="[
    sj.display('flex'),
    sj.flexDirection({ xs: 'column', md: 'row' }),
    sj.gap({ xs: 0.5, md: 1 }),
  ]"
></div>

FAQ

Q: Why this approach vs. utility frameworks or huge token bags? A: SJSS generates only the atomic CSS you actually use, at runtime. No prebuilt megabundle. You write plain CSS properties, enhanced with .options for discoverability. Compared to utility-first CSS, you: (1) keep styling colocated with your component logic, (2) get type‑safe, theme‑aware values, and (3) ship less. Compared to heavy design-token bags, we keep only the essentials at root: sj.palette and sj.breakpoints.

Q: How do I maintain theming long-term? A: Use semantic palette tokens everywhere ('primary.main', 'light.dark'). Centralize your theme in one place (theme service/config). Because styles reference semantic tokens, swapping a palette or adjusting contrast cascades automatically without touching components. Prefer semantic tokens over raw hex in app code.

Q: What’s the responsive story? A: Every property supports responsive objects: { xs, sm, md, lg, xl, xxl }. This keeps responsive intent in one place per style. Example: sj.flexDirection({ xs: 'column', md: 'row' }) and sj.gap({ xs: 0.5, md: 1 }).

Q: Is it SSR‑friendly and fast? A: Yes. The library avoids direct DOM access during import and only generates minimal, deterministic atomic classes at runtime. Styles are memoized, deduped, and composed; pseudo‑selectors are compiled into atomic rules and reused. Result: tiny CSS, predictable ordering, and quick first paint.

Q: How do I discover valid values fast? A: Use .options on common props: sj.display.options, sj.flexDirection.options, sj.justifyContent.options, sj.alignItems.options, sj.width.options, sj.height.options, sj.position.options. Your IDE will autocomplete ergonomic aliases like spaceBetween, flexStart, etc.

Q: Can I mix literal values with tokens? A: Absolutely. Use literals where convenient ('fit-content', '1fr 2fr', 600) and tokens for theme consistency ('primary.main', 'neutral.contrast').

Q: How do I center with flex quickly? A: sj.display(sj.display.options.flex), sj.justifyContent(sj.justifyContent.options.center), and sj.alignItems(sj.alignItems.options.center). That’s it.

💖 Support

If you find Super JSS useful, consider supporting its development:

📬 Contact

For inquiries, feedback, or issues, reach out at ricardo.santoyo@hotmail.com.

License

MIT © Ricardo Santoyo