teacss 0.2.0-alpha.8 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,7 @@
1
- MIT License
1
+ # MIT License
2
2
 
3
- Copyright (c) 2022 Billgo
3
+ Copyright (c) 2021-PRESENT Anthony Fu <https://github.com/antfu>
4
+ Copyright (c) 2026-PRESENT Billgo <hi@billgo.me>
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,3 +1,363 @@
1
- # Teacss
1
+ # TeaCSS
2
2
 
3
- An unocss preset
3
+ **An LLM-friendly atomic CSS toolkit.**
4
+
5
+ Readable utilities for humans. Predictable syntax for AI agents.
6
+
7
+ ## Purpose
8
+
9
+ `teacss` is the application-facing package. Its root entry provides the
10
+ official `cn` and `recipe` runtime helpers plus Standard preset constant
11
+ tables, while its subpaths expose the engine and official presets.
12
+
13
+ Use it for normal TeaCSS applications. Preset authors, custom integration
14
+ authors, and tooling authors should import the narrower `@teacss/*` packages
15
+ that own those extension APIs; the root entry intentionally does not expose
16
+ low-level factories such as `createDefiner`.
17
+
18
+ The base installation contains the runtime class helpers, engine core, and
19
+ official presets. Build integrations are separate packages, so install and
20
+ import the one owned by the selected build boundary directly:
21
+
22
+ | Build boundary | Additional installation | Import |
23
+ | --- | --- | --- |
24
+ | Vite | `bun add -d @teacss/vite vite` | `@teacss/vite` |
25
+ | Astro | `bun add -d @teacss/astro astro vite` | `@teacss/astro` |
26
+ | Rsbuild | `bun add -d @teacss/rsbuild @rsbuild/core` | `@teacss/rsbuild` |
27
+ | PostCSS | `bun add -d @teacss/postcss postcss` | `@teacss/postcss` |
28
+ | Bun Build | `bun add -d @teacss/bun` | `@teacss/bun` |
29
+
30
+ The standalone CLI is intentionally separate because `teacss` has no CLI
31
+ subpath or executable:
32
+
33
+ ```sh
34
+ bun add -d @teacss/cli
35
+ ```
36
+
37
+ TeaCSS is an atomic CSS toolkit designed around a simple idea: utility classes should read like CSS declarations. Its primary audience is large language models, so every language and design tradeoff is judged first by whether a model can reliably predict the output. Instead of compressing meaning into short aliases, TeaCSS uses an explicit `property:value` syntax that remains easy for developers to read and easy for AI agents to generate, validate, and reason about. Predictability takes precedence over brevity.
38
+
39
+ ## Philosophy
40
+
41
+ TeaCSS keeps three principles in view:
42
+
43
+ * **Predictability over brevity.** A model should be able to infer the emitted CSS from the source token.
44
+ * **Clarity over cleverness.** Classes should be readable first, compact only when that does not hide intent.
45
+ * **One model over exceptions.** State, responsive behavior, relations, and arbitrary selectors all use the same trailing `@` condition axis. Write `p:4@hover`, never `hover:p:4`.
46
+
47
+ ## Usage
48
+
49
+ Install or update the optional TeaCSS skill:
50
+
51
+ ```sh
52
+ skills add teacss/skills --skill teacss
53
+ skills update teacss
54
+ ```
55
+
56
+ Restart Codex after installing the skill.
57
+
58
+ Install the application package and the integration used by the project. For
59
+ Vite:
60
+
61
+ ```sh
62
+ bun add teacss
63
+ bun add -d @teacss/vite vite
64
+ ```
65
+
66
+ Use the Vite integration:
67
+
68
+ ```ts
69
+ import { pluginTeacss } from "@teacss/vite";
70
+
71
+ export default {
72
+ plugins: [pluginTeacss()],
73
+ };
74
+ ```
75
+
76
+ Create a TeaCSS entry at `index.css` or `src/index.css`:
77
+
78
+ ```css
79
+ @preset "standard";
80
+ @source "./src/**/*.{ts,tsx}";
81
+ @teacss;
82
+ ```
83
+
84
+ Add `@preset "icons";` when the app uses the official icon vocabulary.
85
+ Add `@preset "articles";` for the fixed semantic-descendant utility
86
+ `article:base`:
87
+
88
+ ```html
89
+ <article
90
+ class="article:base max-inline-size:65ch m-a:auto font-size:3x text-color:$color-foreground"
91
+ >
92
+ <h1>Title</h1>
93
+ <p>Readable long-form content.</p>
94
+ </article>
95
+ ```
96
+
97
+ The utility defaults to TeaCSS's shared `shortcuts` layer
98
+ (`LAYER_SHORTCUTS`). Under the default layer order, ordinary utilities in the
99
+ `utilities` layer emit later. The preset does not create an article-specific
100
+ layer.
101
+
102
+ The articles factory is available only from its explicit subpath:
103
+
104
+ ```ts
105
+ import presetArticles, { presetArticles as namedPresetArticles } from "teacss/preset-articles";
106
+ ```
107
+
108
+ The root `teacss` entry does not re-export it.
109
+
110
+ Import the generated stylesheet once from your app entry:
111
+
112
+ ```ts
113
+ import "virtual:teacss.css";
114
+ ```
115
+
116
+ Then write TeaCSS utilities in your markup:
117
+
118
+ ```html
119
+ <button class="p:4 bg-color:red-500 text-color:white bg-color:red-600@hover">
120
+ Submit
121
+ </button>
122
+ ```
123
+
124
+ The root entry also re-exports the Standard preset's constant tables for
125
+ programmatic use:
126
+
127
+ ```ts
128
+ import { breakpointKeywords, primaryColors } from "teacss";
129
+ ```
130
+
131
+ These are the same exports as `@teacss/preset-standard/constants`; there is no
132
+ separate `teacss/constants` subpath.
133
+
134
+ Grouped utilities can share the same condition:
135
+
136
+ ```html
137
+ <button class="{p:4;text-color:white;bg-color:red-500}@hover">
138
+ Submit
139
+ </button>
140
+ ```
141
+
142
+ ## Runtime Recipes
143
+
144
+ The application entry exports `recipe`, bound once to its exact official
145
+ `cn`:
146
+
147
+ ```ts
148
+ import { recipe, type ClassProp, type VariantProps } from "teacss";
149
+
150
+ const badge = recipe({
151
+ className: "d:inline-flex align-items:center rd:full p-x:2",
152
+ variants: {
153
+ tone: {
154
+ neutral: "bg-color:gray-100 text-color:gray-900",
155
+ accent: "bg-color:blue-500 text-color:white",
156
+ },
157
+ },
158
+ compoundVariants: [
159
+ {
160
+ tone: "accent",
161
+ className: "font-weight:700",
162
+ },
163
+ ],
164
+ });
165
+
166
+ badge({
167
+ tone: "accent",
168
+ className: "p-x:3",
169
+ }); // string
170
+
171
+ type BadgeProps = VariantProps<typeof badge> & ClassProp;
172
+
173
+ function badgeClassName({ className, ...variants }: BadgeProps) {
174
+ return badge({ ...variants, className });
175
+ }
176
+ ```
177
+
178
+ A primitive string top-level `className` value selects the single-element form.
179
+ Its variant choices and compound outputs are strings; the recipe call accepts
180
+ variants plus caller `className` and returns the merged string directly.
181
+
182
+ Use a non-empty object top-level `className` value for multiple elements:
183
+
184
+ ```ts
185
+ const button = recipe({
186
+ className: {
187
+ container: "d:inline-flex align-items:center p:2 p:4@md",
188
+ icon: "inline-size:4x",
189
+ },
190
+ variants: {
191
+ size: {
192
+ small: {
193
+ container: "p:2",
194
+ icon: "inline-size:3x",
195
+ },
196
+ large: {
197
+ container: "p:4",
198
+ icon: "inline-size:5x",
199
+ },
200
+ },
201
+ disabled: {
202
+ true: {
203
+ container: "opacity:50 pointer-events:none",
204
+ },
205
+ },
206
+ },
207
+ defaultVariants: {
208
+ size: "small",
209
+ disabled: false,
210
+ },
211
+ compoundVariants: [
212
+ {
213
+ size: "large",
214
+ disabled: true,
215
+ className: {
216
+ container: "font-weight:700",
217
+ icon: "inline-size:6x",
218
+ },
219
+ },
220
+ {
221
+ disabled: true,
222
+ classKeys: ["container", "icon"],
223
+ className: "opacity:80",
224
+ },
225
+ ],
226
+ });
227
+
228
+ const classes = button({
229
+ size: "large",
230
+ });
231
+
232
+ classes.container({
233
+ disabled: true,
234
+ className: "p:6",
235
+ });
236
+ classes.icon();
237
+
238
+ type ButtonProps = VariantProps<typeof button> & ClassProp;
239
+
240
+ function buttonClassName({ className, ...variants }: ButtonProps) {
241
+ return button(variants).container({ className });
242
+ }
243
+ ```
244
+
245
+ There is no distinguished or required `root` key in an object-form top-level
246
+ `className`; every declared key is an ordinary named class. Variant choices are
247
+ always target maps. A compound either supplies a non-empty `className` target
248
+ map, or pairs a primitive-string `className` with a non-empty `classKeys` array
249
+ to broadcast that class to several effective named classes. `classKeys` may
250
+ name declared or inherited classes and must be dense, unique, and free of
251
+ unknown names. The two compound forms are mutually exclusive, and matching
252
+ entries contribute in declaration order. A recipe call returns a readonly
253
+ resolver for every name.
254
+ Recipe-level selections are captured once. A resolver selection is local to
255
+ that invocation, compounds are re-evaluated for it, and `className` appends only
256
+ to that resolver. The one-sided `"true"` choice makes `disabled`
257
+ boolean-capable: boolean `false` is a branchless state that contributes no
258
+ fragment but can still drive defaults and compounds. Responsive behavior
259
+ remains suffix-only inside static values, such as `p:4@md`; recipe props do not
260
+ accept breakpoint objects.
261
+
262
+ Every definition without `extend` requires its own `className` field. An
263
+ extending child may omit `className` to inherit the parent's string or object
264
+ mode, base fragments, and existing named classes. If a child declares
265
+ `className`, it must keep that mode; an object child may add new names:
266
+
267
+ ```ts
268
+ const primaryButton = recipe({
269
+ extend: button,
270
+ variants: {
271
+ emphasis: {
272
+ strong: {
273
+ container: "font-weight:700",
274
+ },
275
+ },
276
+ },
277
+ });
278
+
279
+ const strongBadge = recipe({
280
+ extend: badge,
281
+ className: "font-weight:700",
282
+ });
283
+ ```
284
+
285
+ `classKeys` is reserved as a variant name, while `parts`, `styles`, and
286
+ `styleKeys` are available as ordinary variant names. Nested
287
+ `compoundVariants[].classKeys` is supported; the retired top-level definition
288
+ field `styles` and former nested `parts` selector are not compatibility aliases.
289
+
290
+ The repeated `className` spelling is separated by boundary:
291
+ `definition.className` supplies base classes and selects string or object mode,
292
+ `compoundVariants[].className` supplies a compound output, and caller
293
+ `className` is accepted by a single-element recipe call or one named resolver.
294
+ A multi-element recipe call itself accepts shared selections only.
295
+
296
+ `@teacss/classes` exports the factory rather than a pre-bound definer. This
297
+ integration creates its own `recipe` from the official `cn`; custom
298
+ integrations follow the same boundary with their own merger:
299
+
300
+ ```ts
301
+ import { createDefiner, createMerger } from "@teacss/classes";
302
+
303
+ const merge = createMerger({ conflicts, plugins });
304
+ export const recipe = createDefiner({ merge });
305
+ ```
306
+
307
+ `createDefiner` and its type-only `RecipeDefiner` return type are intentionally
308
+ available only from `@teacss/classes`; the named return type keeps declarations
309
+ for exported integration-owned definers compact. Normal recipe libraries
310
+ peer-depend on and externalize `teacss`. A custom integration peer-depends on
311
+ `@teacss/classes` and exports the exact definer used for extendable parents.
312
+ Consumers deduplicate the owning integration. Recreating a definer, loading
313
+ another runtime copy or version, or resolving the parent and definer from
314
+ different provider-package instances breaks extension identity even if the
315
+ declarations and merger are compatible. Deduplicating only `@teacss/classes`
316
+ is insufficient for a duplicated custom-definer provider. `VariantProps`
317
+ extraction does not need runtime identity; `extend` does.
318
+
319
+ ## Runtime Class Merging
320
+
321
+ Use the application runtime merger for component class names. It understands the
322
+ standard vocabulary and canonical `icon:<collection>-<icon-name>` utilities:
323
+
324
+ ```ts
325
+ import { cn } from "teacss";
326
+
327
+ cn("p:4 d:flex", "p:8"); // "d:flex p:8"
328
+ cn("p:4", "p:invalid"); // "p:invalid"
329
+ cn("reset:li", "reset:ul"); // "reset:ul"
330
+ cn("d:block", "reset:li"); // "d:block reset:li"
331
+ ```
332
+
333
+ The default `cn` is one direct merger composed from `pluginStandard` and
334
+ `pluginIcon`; it does not chain preset-local mergers. Resolution is based
335
+ on parsed shape, conditions, importance, and declared footprints. It does not
336
+ validate values, so an unsupported winner may emit no CSS. The supported
337
+ `reset:*` profiles apply to the current element: headings (`reset:h1` through
338
+ `reset:h6`), links and controls
339
+ (`reset:a|button|select|input-text|textarea`), and lists
340
+ (`reset:li|ol|ul|menu`). `reset:input-text` is only for text-like inputs; there
341
+ is no generic `reset:input`. Profiles replace one another as a single opaque
342
+ family and never delete an unrelated utility. Document-root and shadow-host
343
+ defaults come from the standard preset's automatic preflight.
344
+
345
+ Generic class merging is a separate package for custom conflict metadata:
346
+
347
+ ```sh
348
+ bun add @teacss/classes
349
+ ```
350
+
351
+ ```ts
352
+ import { createMerger } from "@teacss/classes";
353
+ ```
354
+
355
+ ## Status
356
+
357
+ TeaCSS is currently pre-1.0 and under active development.
358
+
359
+ Syntax, configuration, package boundaries, and public APIs may change before the first stable release.
360
+
361
+ ## License
362
+
363
+ MIT. See [LICENSE](LICENSE).
package/dist/core.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Preset, Rule, UserConfig, Variant, createGenerator } from "@teacss/core";
2
+ export { type Preset, type Rule, type UserConfig, type Variant, createGenerator };
package/dist/core.js ADDED
@@ -0,0 +1 @@
1
+ import{createGenerator as e}from"@teacss/core";export{e as createGenerator};
package/dist/index.d.ts CHANGED
@@ -1,39 +1,7 @@
1
- export { default as cn } from 'classnames';
2
- import * as CLSX from 'clsx';
3
- import { clsx } from 'clsx';
4
-
5
- type ClassValue = CLSX.ClassValue;
6
- type ClassProp = {
7
- class: ClassValue;
8
- className?: never;
9
- } | {
10
- class?: never;
11
- className: ClassValue;
12
- } | {
13
- class?: never;
14
- className?: never;
15
- };
16
- type OmitUndefined<T> = T extends undefined ? never : T;
17
- type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
18
-
19
- declare function cxm(...inputs: ClassValue[]): string;
20
- type VariantProps<Component extends (...args: any) => any> = Omit<OmitUndefined<Parameters<Component>[0]>, "class" | "className">;
21
- type CxOptions = Parameters<typeof clsx>;
22
- type CxReturn = ReturnType<typeof clsx>;
23
- declare const cx: typeof clsx;
24
- type ConfigSchema = Record<string, Record<string, ClassValue>>;
25
- type ConfigVariants<T extends ConfigSchema> = {
26
- [Variant in keyof T]?: StringToBoolean<keyof T[Variant]> | null | undefined;
27
- };
28
- type ConfigVariantsMulti<T extends ConfigSchema> = {
29
- [Variant in keyof T]?: StringToBoolean<keyof T[Variant]> | StringToBoolean<keyof T[Variant]>[] | undefined;
30
- };
31
- type Config<T> = T extends ConfigSchema ? {
32
- variants?: T;
33
- defaultVariants?: ConfigVariants<T>;
34
- compoundVariants?: (T extends ConfigSchema ? (ConfigVariants<T> | ConfigVariantsMulti<T>) & ClassProp : ClassProp)[];
35
- } : never;
36
- type Props<T> = T extends ConfigSchema ? ConfigVariants<T> & ClassProp : ClassProp;
37
- declare const cva: <T>(base?: ClassValue, config?: Config<T>) => (props?: Props<T>) => string;
38
-
39
- export { type CxOptions, type CxReturn, type VariantProps, cva, cx, cxm };
1
+ import { ClassProp, Merger, RecipeDefiner, VariantProps } from "@teacss/classes";
2
+ export * from "@teacss/preset-standard/constants";
3
+ //#region src/index.d.ts
4
+ declare const cn: Merger;
5
+ declare const recipe: RecipeDefiner;
6
+ //#endregion
7
+ export { type ClassProp, type VariantProps, cn, recipe };