Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

21 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

zares-css

⚑ Zero-runtime Atomic Rust CSS Engine (Static)

Build-time compiler Β· Zero runtime overhead Β· Type-safe variants Β· RSC-ready

npm license Rust Node tests bundle


zares-css adalah library styling React yang menggabungkan DX styled-components dengan performa Tailwind CSS v4 β€” dikompilasi oleh engine Rust. Tulis komponen sekali dengan tw.button({ variants }), Rust extract dan optimasi seluruh CSS di build time.

Perbandingan:

zares-css styled-components Tailwind biasa Panda CSS
Build-time CSS βœ… ❌ runtime inject βœ… βœ…
Runtime JS ~0 ~15KB ~0 ~0
Variants API βœ… type-safe terbatas ❌ βœ…
SSR / RSC βœ… zero config ⚠️ ServerStyleSheet βœ… manual βœ…
Hydration mismatch βœ… tidak ada ⚠️ hash drift βœ… βœ…
DevTools readable βœ… ❌ sc-abc123 βœ… βœ…
Engine πŸ¦€ Rust JS JS JS
TypeScript βœ… full inference partial βœ… βœ…

Instalasi

npm install zares-css
npx tw setup

npx tw setup mendeteksi bundler (Next.js / Vite / Rspack), meng-inject plugin ke config, dan membuat tailwind-styled.config.json secara otomatis.


API

1. Template Literal

API paling sederhana β€” satu tag, satu string kelas.

import { tw } from "zares-css"

const Button = tw.button`
  inline-flex items-center rounded-lg px-4 py-2
  bg-blue-600 text-white font-medium
  hover:bg-blue-700 transition
`

<Button onClick={handleClick}>Klik saya</Button>

2. Object Config (direkomendasikan)

API utama β€” mendukung variants, states, sub, compoundVariants, container, dan lebih. Semua di-resolve Rust di build time.

const Button = tw.button({
  base: "inline-flex items-center rounded-lg font-medium transition-all",
  variants: {
    intent: {
      primary:   "bg-indigo-600 text-white hover:bg-indigo-700",
      secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300",
      danger:    "bg-red-600 text-white hover:bg-red-700",
      ghost:     "text-gray-600 hover:bg-gray-100",
    },
    size: {
      sm: "px-3 py-1.5 text-xs",
      md: "px-4 py-2 text-sm",
      lg: "px-5 py-2.5 text-base",
    },
  },
  defaultVariants: { intent: "primary", size: "md" },
  compoundVariants: [
    // intent=primary + size=lg β†’ tambah shadow
    { intent: "primary", size: "lg", class: "shadow-md shadow-indigo-200" },
  ],
})

// TypeScript tahu props yang valid β€” autocomplete penuh
<Button intent="primary" size="lg">Submit</Button>
<Button intent="danger">Hapus</Button>
<Button intent="invalid" />  // ❌ Type error

3. Sub-Components

Definisi slot anak langsung di config. Format "tag:name" untuk kontrol tag HTML β€” penting untuk SEO dan aksesibilitas.

const Card = tw.article({
  base: "rounded-2xl border border-gray-200 bg-white shadow-sm overflow-hidden",
  sub: {
    // "tag:name" β†’ render tag HTML, akses via Card.name
    "header:header": "px-6 pt-5 pb-0 flex items-start justify-between",
    "h2:title":      "text-base font-semibold text-gray-900",
    "section:body":  "px-6 py-4 text-sm text-gray-500 leading-relaxed",
    "footer:footer": "px-6 pb-5 pt-0 flex items-center gap-2",
    "img:image":     "w-full aspect-video object-cover",
    // tanpa tag β†’ render <span> (default)
    badge:           "rounded-full px-2.5 py-0.5 text-xs font-semibold bg-indigo-100 text-indigo-700",
  },
})

// Penggunaan
<Card>
  <Card.header>
    <Card.title>Judul Card</Card.title>
    <Card.badge>New</Card.badge>
  </Card.header>
  <Card.body>Konten card di sini.</Card.body>
  <Card.footer>
    <Button size="sm">Detail</Button>
  </Card.footer>
</Card>

Tag prefix di-strip otomatis dari TypeScript inference β€” Card.title bukan Card["h2:title"].


3.5. Dynamic Values β€” ${...} (Mode 2)

Nilai arbitrary yang gak diketahui saat build time (warna dari props, theme runtime, dll) bisa ditulis pakai template placeholder ${...} di dalam kelas Tailwind bracket (prefix-[${expr}]). Rust engine mendeteksi ini otomatis β€” tanpa hint manual (liveToken, setToken) β€” dan generate CSS Variable saat build:

const Card = tw.div({
  base: `rounded-xl shadow-sm p-6 bg-[${bgColor}]`,
  sub: {
    header: { tag: "div", base: `text-lg font-bold text-[${titleColor}]` },
  },
})

Di-compile jadi:

.tw-Card-bgColor { background-color: var(--Card-bgColor, transparent); }
.tw-Card-header-titleColor { color: var(--Card-header-titleColor, inherit); }

Pemakaian β€” langsung lewat props, gak perlu style={} atau hint manual:

<Card bgColor={userColor} titleColor={titleColor} />

Nama prop-nya persis nama variable di dalam ${...} (bgColor, titleColor). Di balik layar, komponen yang di-generate otomatis nge-destructure prop itu, jadiin CSS custom property di style root element (di-merge sama style yang kamu kasih sendiri), terus di-delete dari props sebelum nyampe ke elemen DOM β€” jadi gak ada warning "unknown DOM attribute".

CSS custom property itu inherit ke bawah lewat DOM tree secara native (bagian dari spek CSS), jadi walau prop kayak titleColor cuma di-set sekali di komponen paling luar (<Card titleColor={...}>), nilainya tetep nyampe ke .tw-Card-header-titleColor yang dipakai di Card.header β€” asalkan Card.header di-render sebagai children DOM beneran (<Card><Card.header>...</Card.header></Card>), bukan dirender terpisah di luar <Card>.

Kalau kamu tetep butuh style={} manual (misal buat set CSS var yang gak berasal dari ${...} di definisi tw.object), itu masih jalan seperti biasa β€” dua-duanya bisa dipakai bareng, style kamu di-merge, bukan di-timpa.

Catatan penamaan prop: kalau nama variable yang sama (${x}) dipakai di beberapa tempat berbeda (base dan sub.header misalnya), itu jadi satu prop yang sama β€” nge-drive beberapa CSS Variable sekaligus. Ini simplifikasi yang disengaja, bukan bug β€” kalau kamu tulis ${x} dua kali, dianggap memang mau nilai yang sama.

⚠️ Ambiguitas prefix text- β€” dan cara ngatasinnya (sesuai dokumentasi resmi Tailwind):

text- di Tailwind punya dua arti tergantung isinya β€” warna (text-red-500) atau ukuran font (text-lg). Untuk token dinamis, text-[${expr}] (bentuk polos) selalu ditafsirkan sebagai color, bukan font-size.

Tailwind sendiri punya jawaban resmi buat ini β€” section "Resolving ambiguities" β€” pakai CSS data-type hint dengan parentheses, dan engine ini ngedukung persis sintaks itu:

base: `text-(length:${fontSize}) text-(color:${textColor})`
// β†’ font-size: var(--Comp-fontSize, inherit)
// β†’ color: var(--Comp-textColor, inherit)

(Bentuk bracket versi Tailwind v3 lama, text-[length:${x}], juga didukung buat back-compat.)

Buat CSS property yang Tailwind emang gak punya utility-nya sama sekali, pakai arbitrary property syntax ([property:${x}]) β€” beda dari hint di atas, ini bukan buat disambiguasi tapi buat property yang bener-bener di luar Tailwind:

base: `[mask-type:${maskType}]`

Prefix lain yang belum punya pemetaan eksplisit (di luar bg, text, border, fill, stroke, p/px/py/pt/pb/pl/pr, m/mx/my, w, h, gap, rounded, opacity, z) akan tetap menghasilkan CSS Variable, tapi dengan property: unset kalau ditulis pakai bentuk prefix-[${x}] polos β€” pakai bentuk hint atau [property:${x}] buat hasil yang pasti.

Prefix→property mapping-nya sengaja hardcoded manual (bukan diturunkan dari Lightning CSS atau parser CSS generik manapun — Tailwind bukan bagian dari spesifikasi CSS, jadi gak ada parser yang bisa "menebak" bg = background-color tanpa tabel referensi). Ini pola yang sama dipakai di seluruh engine (lihat tw_property_map di jalur atomic CSS).

πŸ“– Baca lengkapnya (cara kerja, tabel prefix, semua batasan) di docs/DYNAMIC_PROPS.md.

Bukan untuk dikira sama dengan desimal spacing statis. Bracket [...] di section ini murni penanda "nilai ini dari runtime/props, generate CSS Variable" β€” beda dari arbitrary value bracket biasa. Kelas desimal statis seperti w-1.5, p-2.5, gap-0.5 tidak butuh bracket sama sekali; itu utility Tailwind v4 biasa (continuous spacing scale, n * 0.25rem) yang diresolve langsung lewat theme_resolver.rs::resolve_spacing, gak lewat jalur dynamic props ini.


4. cv() β€” Class Variant Function

Untuk styling non-komponen (className string) β€” berguna di utility functions, dynamic class lists, dll.

import { cv } from "zares-css"

const badge = cv({
  base: "inline-flex items-center gap-1.5 rounded-full font-medium",
  variants: {
    color: {
      gray:   "bg-gray-100 text-gray-700",
      blue:   "bg-blue-100 text-blue-700",
      green:  "bg-green-100 text-green-700",
      red:    "bg-red-100 text-red-700",
    },
    size: {
      sm: "px-2 py-0.5 text-[10px]",
      md: "px-2.5 py-0.5 text-xs",
      lg: "px-3 py-1 text-sm",
    },
  },
  defaultVariants: { color: "gray", size: "md" },
})

// Returns string className, bukan komponen
<span className={badge({ color: "blue", size: "lg" })}>Active</span>

// Merge className tambahan
<span className={badge({ color: "red", className: "opacity-75" })}>Error</span>

5. states β€” Boolean Props

Boolean props yang di-resolve via Rust bitmask lookup table. Tidak ada string comparison, tidak ada kondisional di render path.

const Button = tw.button({
  base: "inline-flex items-center px-4 py-2 rounded-lg font-medium",
  variants: {
    intent: { primary: "bg-indigo-600 text-white", ghost: "text-gray-600" },
  },
  defaultVariants: { intent: "primary" },
  states: {
    loading:   "opacity-60 cursor-wait pointer-events-none",
    fullWidth: "w-full",
    disabled:  "opacity-50 cursor-not-allowed",
  },
})

// Boolean props langsung β€” tidak perlu className kondisional
<Button loading>Memproses...</Button>
<Button fullWidth>Submit</Button>
<Button loading fullWidth>Loading full width</Button>

Maksimal 16 states per komponen (2¹⁢ kombinasi pre-generated di build time).


6. state β€” CSS Data-Attribute (Zero JS State)

Untuk toggle style tanpa React re-render β€” cocok untuk animasi dan transisi.

const Dropdown = tw.div({
  base: "overflow-hidden transition-all duration-200",
  state: {
    open: {
      true:  "max-h-96 opacity-100",
      false: "max-h-0 opacity-0",
    },
  },
})

// Set data attribute langsung β€” tidak butuh setState
dropdownRef.current?.setAttribute("data-open", "true")

// Atau via React state
<Dropdown data-open={isOpen.toString()}>
  {children}
</Dropdown>

7. .extend() β€” Inheritance

Extend komponen yang sudah ada tanpa duplikasi class.

// Template literal extend
const PrimaryButton = Button.extend`
  bg-indigo-600 text-white hover:bg-indigo-700
`

// Object config extend β€” tambah variant sekaligus
const BigDangerButton = Button.extend({
  classes:  "text-lg px-8 shadow-lg",
  variants: { loading: { true: "animate-pulse" } },
  defaultVariants: { intent: "danger" },
})

8. container β€” Container Queries

Responsive berdasarkan ukuran container parent, bukan viewport.

const Card = tw.div({
  base: "p-4 flex flex-col",
  container: {
    sm: "flex-col",   // @container (min-width: 320px)
    md: "flex-row",   // @container (min-width: 640px)
    lg: "grid-cols-3",// @container (min-width: 1024px)
  },
  containerName: "card", // opsional β€” named container
})

// Wrapper wajib punya @container
const CardWrapper = tw.div`@container`

<CardWrapper>
  <Card>{/* responsive berdasarkan lebar CardWrapper */}</Card>
</CardWrapper>

Breakpoint default: xs=240px, sm=320px, md=640px, lg=1024px, xl=1280px, 2xl=1536px.


9. server. β€” Server Components Only

Komponen yang di-enforce hanya boleh render di server. Dev warning otomatis jika render di browser.

import { server } from "zares-css"

// Sama persis API-nya dengan tw β€” tapi compiler enforce server-only
const PageHeader = server.header({
  base: "w-full border-b px-6 py-4 bg-white",
  sub: {
    "h1:title": "text-2xl font-bold",
    "p:subtitle": "text-sm text-gray-500",
  },
})

const AvatarRoot = server.div({
  base: "relative inline-flex rounded-full overflow-hidden",
  variants: {
    size: {
      sm: "h-8 w-8",
      md: "h-10 w-10",
      lg: "h-14 w-14",
    },
  },
  defaultVariants: { size: "md" },
})

10. createStyledSystem() β€” Design System Factory

Untuk design system dengan token terpusat. Token di-inject sebagai CSS custom properties --sys-{group}-{name}.

import { createStyledSystem } from "zares-css"

const ui = createStyledSystem({
  tokens: {
    colors: {
      primary: "#6366f1",
      danger:  "#ef4444",
      muted:   "#6b7280",
    },
    radius: {
      base: "0.5rem",
      full: "9999px",
    },
  },
  components: {
    button: {
      tag: "button",
      base: "inline-flex items-center font-medium transition-colors",
      variants: {
        intent: {
          primary: "bg-[var(--sys-colors-primary)] text-white",
          danger:  "bg-[var(--sys-colors-danger)] text-white",
          ghost:   "bg-transparent text-current hover:bg-black/5",
        },
        size: {
          sm: "h-8 px-3 text-sm",
          md: "h-10 px-4 text-base",
          lg: "h-12 px-6 text-lg",
        },
      },
      defaultVariants: { intent: "primary", size: "md" },
    },
  },
})

// Komponen dari sistem
const Button = ui.button()

// Token reference β€” "var(--sys-colors-primary)"
const primaryVar = ui.token("colors.primary")

// Update token runtime
ui.setTokens({ colors: { primary: "#8b5cf6" } })

11. liveToken() β€” Live Design Tokens

Token yang bisa diupdate runtime dan subscribe ke perubahannya.

import { liveToken, tokenVar, createUseTokens } from "zares-css"

// Deklarasi token
const tokens = liveToken({
  primary: "#6366f1",
  surface: "#ffffff",
  text:    "#111827",
})

// CSS variable reference β€” dipakai di className
const Card = tw.div({
  base: `
    bg-[${tokenVar(tokens.surface)}]
    text-[${tokenVar(tokens.text)}]
    border-[${tokenVar(tokens.primary)}]
  `,
})

// Hook untuk subscribe token di React
const useTokens = createUseTokens(tokens)

function ThemePanel() {
  const { primary } = useTokens()
  return <div style={{ color: primary }}>Current primary: {primary}</div>
}

// Update token langsung β€” semua subscriber re-render
tokens.primary.set("#8b5cf6")

12. cn(), cx(), twMerge

Utility untuk merge dan deduplicate Tailwind classes.

import { cn, cx, twMerge } from "zares-css"

// cn β€” merge dengan dedup (alias twMerge)
cn("px-4 py-2", isActive && "bg-blue-500", className)

// cx β€” conditional class join (tanpa dedup)
cx("base-class", { "active-class": isActive, "disabled-class": !enabled })

// twMerge β€” eksplisit Tailwind conflict resolution
twMerge("px-4 px-8")  // β†’ "px-8" (konflik di-resolve, yang terakhir menang)

Setup

Next.js

next.config.ts:

import { withTailwindStyled } from "zares-css/next"
import type { NextConfig } from "next"

const nextConfig: NextConfig = {}

export default withTailwindStyled({
  // routeCss: true β€” generate css-manifest.json yang dibutuhkan TwCssInjector.
  // Tanpa ini, TwCssInjector diam-diam return kosong (manifest tidak ada).
  routeCss: true,
})(nextConfig)

layout.tsx:

import { TwCssInjector } from "zares-css/runtime-css"

export default function RootLayout({ children }) {
  return (
    <html lang="id">
      <head>
        {/*
         * TwCssInjector β€” opsional tapi direkomendasikan untuk production.
         *
         * Cara kerja:
         *   1. withTailwindStyled({ routeCss: true }) emit css-manifest.json
         *      ke .next/static/css/tw/ saat build
         *   2. Per request, TwCssInjector baca manifest di server dan inject CSS
         *      route-specific langsung sebagai <style> inline di HTML
         *
         * Tanpa TwCssInjector:
         *   CSS tetap jalan via globals.css β€” semua route dapat satu bundle
         *   CSS gabungan yang di-load browser via <link>.
         *
         * Dengan TwCssInjector:
         *   Hanya CSS yang dipakai route itu yang di-inline di HTML β†’
         *   tidak ada extra HTTP request, tidak ada FOUC, streaming-friendly.
         *
         * Kalau manifest belum ada (dev cold start), komponen ini
         * diam-diam return kosong β€” tidak breaking.
         */}
        <TwCssInjector />
      </head>
      <body>{children}</body>
    </html>
  )
}

globals.css:

@import "tailwindcss";

:root {
  --background: #f5f7fb;
  --foreground: #111827;
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --font-sans: var(--font-geist-sans);
}

Vite

// vite.config.ts
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import { tailwindStyled } from "zares-css/vite"

export default defineConfig({
  plugins: [react(), tailwindStyled()],
})

Rspack

// rspack.config.js
import { tailwindStyled } from "zares-css/rspack"

---

## Theme Management

Tailwind-styled-v4 automatic mengelola CSS custom properties via `@theme inline` directive. Compiler Rust pre-generate semua CSS state rules di build time β€” zero runtime overhead.

### Setup Tema

**globals.css β€” Define CSS Variables:**
```css
@import "tailwindcss";

:root {
  --background: #f5f7fb;
  --foreground: #111827;
  --surface: #ffffff;
  --accent: #2563eb;
}

[data-theme="dark"] {
  --background: #070b16;
  --foreground: #e5e7eb;
  --surface: #0f172a;
  --accent: #60a5fa;
}

/* Bridge to Tailwind */
@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-surface: var(--surface);
  --color-accent: var(--accent);
}

ThemeProvider.tsx β€” Runtime Toggle:

"use client";

import { ReactNode, useEffect, useState, createContext, useContext } from "react";

const STORAGE_KEY = "app-theme";

function applyTheme(theme: "light" | "dark") {
  document.documentElement.setAttribute("data-theme", theme);
}

const ThemeContext = createContext<{
  theme: "light" | "dark";
  setTheme: (theme: "light" | "dark") => void;
} | null>(null);

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setThemeState] = useState<"light" | "dark">("light");
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    const stored = localStorage.getItem(STORAGE_KEY) || "light";
    setThemeState(stored as "light" | "dark");
    applyTheme(stored as "light" | "dark");
    setMounted(true);
  }, []);

  const setTheme = (newTheme: "light" | "dark") => {
    localStorage.setItem(STORAGE_KEY, newTheme);
    setThemeState(newTheme);
    applyTheme(newTheme);
  };

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {mounted ? children : null}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error("useTheme must be inside ThemeProvider");
  return context;
}

layout.tsx β€” Wrap App:

import { ThemeProvider } from "@/components/ThemeProvider";

export default function RootLayout({ children }) {
  return (
    <html lang="id">
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

Gunakan di Komponen

import { useTheme } from "@/components/ThemeProvider";
import { tw } from "zares-css";

const ThemeButton = tw.button`
  px-4 py-2 rounded-lg
  bg-[var(--accent)] text-white
  hover:opacity-80 transition
`;

export function ThemeToggle() {
  const { theme, setTheme } = useTheme();

  return (
    <ThemeButton onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      {theme === "light" ? "πŸŒ™ Dark" : "β˜€οΈ Light"}
    </ThemeButton>
  );
}

Mengapa Ini Berbeda

Tailwind-styled-v4 tidak butuh library theme khusus β€” compiler Rust handle CSS optimization:

  1. Build-time state extraction: Compiler scan 81 file, extract 182 komponen, generate 20 state rules
  2. CSS custom properties: Tailwind bridge variabel ke design system via @theme inline
  3. Zero runtime: Theme toggle hanya set data-theme attribute β€” CSS change instant
  4. localStorage + system preference: ThemeProvider handle persistence dan auto-sync

Hasil di .next/tw-classes/_tw-state-static.css (auto-generated):

/* Button component state rules β€” pre-generated di build time */
.tw-s-b35937[data-disabled="true"] { opacity: 50%; cursor: not-allowed; }
.tw-s-b35937[data-loading="true"] { opacity: 60%; cursor: wait; }

/* State selectors menggunakan CSS variables */
.tw-s-93c530[data-copied="true"] { 
  background-color: var(--color-emerald-500);
  color: var(--color-white);
}

Semua state rules di-generate Rust saat build β€” tidak ada string comparison atau kondisional di runtime! πŸš€

export default { plugins: [tailwindStyled()], }


---

## CLI

```bash
npx tw setup       # Setup otomatis: detect bundler, patch config, pre-warm cache
npx tw preflight   # Verifikasi setup
npx tw audit       # Analisis workspace β€” unused classes, missing variants
npx tw benchmark   # Benchmark performa scanner + compiler

DevTools

// Tambahkan ke layout untuk inspeksi komponen di browser
import { TwDevTools } from "zares-css/devtools"

// Atau pakai dynamic import untuk Next.js (ssr: false wajib)
import dynamic from "next/dynamic"
const DevTools = dynamic(
  () => import("zares-css/devtools").then(m => ({ default: m.TwDevTools })),
  { ssr: false }
)

DevTools menampilkan: daftar komponen terdaftar, resolved classes per variant, state registry, container registry, dan live token values.


TypeScript

Semua API fully typed β€” tidak ada any di public API.

// Variant type inference otomatis dari config
const Button = tw.button({
  variants: {
    intent: { primary: "...", ghost: "...", danger: "..." },
    size:   { sm: "...", md: "...", lg: "..." },
  },
  defaultVariants: { intent: "primary", size: "md" },
})

type ButtonProps = React.ComponentProps<typeof Button>
// β†’ { intent?: "primary" | "ghost" | "danger", size?: "sm" | "md" | "lg", ... }

// Sub-component inference β€” tag prefix otomatis di-strip
const Card = tw.article({
  sub: {
    "header:header":  "...",  // β†’ Card.header (renders <header>)
    "h2:title":       "...",  // β†’ Card.title  (renders <h2>)
    "section:body":   "...",  // β†’ Card.body   (renders <section>)
    badge:            "...",  // β†’ Card.badge  (renders <span>)
  },
})

Card.header  // βœ… autocomplete
Card.title   // βœ…
Card.xyz     // ❌ TypeScript error

// .withSub<>() untuk template literal β€” strict mode manual
const Nav = tw.nav`
  flex items-center gap-4
`.withSub<"logo" | "links" | "actions">()

Nav.logo     // βœ…
Nav.unknown  // ❌ TypeScript error

Benchmark

Diukur di Node.js 22, Rust 1.75, M1 MacBook Pro.

Operasi zares-css Tailwind CSS (JS) Speedup
Scan 1000 file 0.8 ms ~340 ms ~425Γ—
Compile 500 class 0.02 ms ~1.2 ms ~60Γ—
Parse class string 0.010 ms ~0.8 ms ~80Γ—
Cache read/write 0.009 ms ~0.5 ms ~55Γ—
Watch mode rebuild < 5 ms ~85 ms ~17Γ—

Environment Variables

Variable Default Deskripsi
TWS_LOG_LEVEL info debug|info|warn|error|silent
TWS_DEBUG_SCANNER 0 1 = aktifkan scanner debug logs
TWS_NO_NATIVE β€” 1 = disable native module (fallback JS)
TWS_NO_RUST β€” 1 = disable Rust, gunakan JS fallback

Arsitektur

zares-css/
β”‚
β”œβ”€β”€ native/                     # πŸ¦€ Rust engine (NAPI-RS)
β”‚   β”œβ”€β”€ src/domain/             # Core logic: variants, CSS generation, theme
β”‚   β”œβ”€β”€ src/application/        # Parser, scanner, resolver, variant system
β”‚   └── src/infrastructure/     # 11 NAPI bridge modules, cache backends
β”‚
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ domain/
β”‚   β”‚   β”œβ”€β”€ core/               # tw, cv, cn, cx β€” core API
β”‚   β”‚   β”œβ”€β”€ compiler/           # Tailwind v4 + LightningCSS pipeline
β”‚   β”‚   β”œβ”€β”€ scanner/            # File scanner (Rust-backed, ~425Γ— faster)
β”‚   β”‚   β”œβ”€β”€ theme/              # Theme token resolution
β”‚   β”‚   β”œβ”€β”€ shared/             # Types, utilities, generated schemas
β”‚   β”‚   └── runtime-css/        # Browser-safe CSS runtime (batched inject)
β”‚   β”‚
β”‚   β”œβ”€β”€ presentation/
β”‚   β”‚   β”œβ”€β”€ next/               # Next.js plugin (withTailwindStyled)
β”‚   β”‚   β”œβ”€β”€ vite/               # Vite plugin
β”‚   β”‚   └── rspack/             # Rspack plugin
β”‚   β”‚
β”‚   └── infrastructure/
β”‚       └── cli/                # CLI (tw setup, audit, benchmark)
β”‚
└── examples/
    └── next-js-app/            # Demo app: Next.js 16 + React 19

NAPI Bridge Modules (11 modul terpisah):

Module Fungsi
napi_bridge_parsing.rs Class parsing (6 fungsi)
napi_bridge_css.rs CSS generation (7 fungsi)
napi_bridge_theme.rs Theme resolution (7 fungsi)
napi_bridge_cache.rs Cache management (6 fungsi)
napi_bridge_redis.rs Redis distributed cache (17 fungsi)
napi_bridge_analysis.rs Performance metrics (5 fungsi)
napi_bridge_watch.rs File watching (9 fungsi)
napi_bridge_types.rs Type definitions
napi_bridge_marshalling.rs JSON I/O
napi_bridge_errors.rs Error handling
napi_bridge.rs Facade (re-export semua)

πŸͺ„ Build-Time Magic: Pelajari Lebih Lanjut

Tailwind-styled-v4 melakukan serangkaian operasi sophisticated di build time. Baca dokumentasi untuk understand:

  • .next-MAGIC-EXPLAINED.md β€” Complete breakdown dari semua yang terjadi di .next/tw-classes/

    • Phase 1-5 workflow
    • Rust engine scanning (425Γ— lebih cepat)
    • State rule pre-generation
    • Route attribution & CSS splitting
    • Component hash determinism
  • BUILD_TIME_FLOW_DIAGRAM.md β€” Visual flowchart & architecture

    • Complete flow dari npm run dev hingga browser
    • File dependency graph
    • Key decision points & tradeoffs
    • Performance comparison
  • BUILD_ARTIFACTS_BREAKDOWN.md β€” Apa yang actually di-generate

    • _initial-scan.css (3500 lines)
    • _tw-state-static.css (20 pre-generated rules)
    • css-manifest.json (route attribution)
    • Statistics & examples

Highlight: Engine melakukan ~370ms work di build time β†’ runtime zero overhead ✨


Development

git clone https://github.com/Dictionar32/zares.git
cd zares

npm install

# Build Rust binary dulu, baru packages
npm run build:rust
npm run build:packages

# Full build
npm run build

# Test
npm run test:all

# Dev mode (watch)
npm run dev

# Benchmark
npm run bench

Requirements: Node.js 20+, Rust 1.75+ (untuk build dari source)


Build-Time Magic Documentation

Tailwind-styled-v4 performs 18+ layers of build-time optimization yang menghasilkan zero runtime overhead. Dokumentasi lengkap tersedia:

  • Quick Overview (5 min): MAGIC_QUICK_REFERENCE.md
  • Architecture Flow (15 min): BUILD_TIME_FLOW_DIAGRAM.md
  • Technical Deep Dive (30 min): .next-MAGIC-EXPLAINED.md
  • Entire .next/ Folder (30 min): COMPLETE_NEXT_FOLDER_MAGIC.md
  • Real Files Breakdown (20 min): BUILD_ARTIFACTS_BREAKDOWN.md
  • All 18 Layers Explained (45 min): COMPLETE_MAGIC_LAYERS_NEXTJS_APP.md ⭐

Steering File (for future agents): .kiro/steering/build-time-magic.md

Untuk development workflows dan advanced patterns, lihat:

  • PROPER_THEME_ARCHITECTURE.md β€” Theme setup guide
  • ARIA_VS_VARIANTS_CLARIFICATION.md β€” Accessibility patterns
  • FINAL_THEME_SOLUTION.md β€” Complete theme solution
  • docs/WAVE5_INTEGRATION_GUIDE.md β€” Wave 5 integration

Build-Time Magic Documentation

Tailwind-styled-v4 performs 18+ layers of build-time optimization yang menghasilkan zero runtime overhead. Dokumentasi lengkap tersedia di docs/ folder:

πŸ“š Quick Navigation

Main Documentation Folder:

  • docs/README_BUILD_TIME_MAGIC.md - Main entry point
  • docs/DOCUMENTATION_INDEX.md - Complete navigation guide
  • docs/build-time-magic/ - 18 layers documentation (6 files)
  • docs/theme-architecture/ - Theme setup patterns
  • docs/accessibility/ - ARIA & semantic components

πŸš€ Start Reading

  1. 5-Minute Overview: docs/build-time-magic/01-QUICK_REFERENCE.md
  2. 15-Minute Architecture: docs/build-time-magic/02-FLOW_DIAGRAM.md
  3. 45-Minute Complete: docs/build-time-magic/06-ALL_18_LAYERS.md ⭐

All in: docs/build-time-magic/ folder with 6 comprehensive files.


Contributing

PR dan issue sangat welcome. Prioritas saat ini:

  • Pre-built binary untuk macOS arm64, x64, Linux, Windows
  • Docs websi te (VitePress)
  • Vue & Svelte adapter yang lebih matang
  • Plugin API public docs

License

MIT Β© Dictionar32

Releases

Packages

Contributors

Languages