@teacss/preset-standard 0.5.1 → 0.6.0

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/README.md CHANGED
@@ -12,7 +12,7 @@ bun add @teacss/preset-standard
12
12
  ```
13
13
 
14
14
  ```css
15
- @preset "standard";
15
+ @presets "standard";
16
16
  @source "./src/**/*.{ts,tsx}";
17
17
  @teacss;
18
18
  ```
@@ -50,11 +50,11 @@ Preflight emits low-specificity reset rules and theme variables. Set
50
50
  reset while limiting variables to referenced keys (including the reset's sans
51
51
  and monospace families).
52
52
 
53
- Selecting `@preset "standard"` also normalizes headings, links, lists, media,
53
+ Selecting `@presets "standard"` also normalizes headings, links, lists, media,
54
54
  tables, horizontal rules, code typography, and form-control typography. Headings
55
55
  inherit size and weight; links inherit color and decoration; lists lose their
56
56
  markers; images and videos fit their container without losing aspect ratio.
57
- Use utilities or Shortcuts' `prose` family to author their visible presentation.
57
+ Use utilities or Official's `prose` family to author their visible presentation.
58
58
 
59
59
  Small text, sub/sup, titled abbreviations, and progress have consistent baselines.
60
60
  Dialogs and popovers retain native auto-margin centering after the universal
@@ -64,43 +64,173 @@ appearance. Fieldsets use `min-inline-size: 0` so their boxes can shrink inside
64
64
  narrow containers; oversized descendants still need responsive sizing.
65
65
  Grouped options in `select[multiple]` and `select[size]` retain a 20px
66
66
  inline-start indent, following text direction and allowing utility overrides.
67
- Hidden elements remain hidden despite media/summary display resets, while
68
- `hidden="until-found"` (case-insensitive) retains native discovery behavior.
69
- Explicit display utilities can still override this low-specificity baseline.
67
+ The reset enforces native hiding with two global rules:
68
+
69
+ ```css
70
+ :where([hidden]:not([hidden="until-found" i])) { display: none !important; }
71
+ :where([hidden="until-found" i]) { display: revert !important; }
72
+ ```
73
+
74
+ Ordinary display utilities, component CSS and inline styles cannot reveal an
75
+ element with `hidden`; remove the attribute to show it. Explicit `!important`
76
+ declarations still follow the CSS cascade. `until-found` restores the tag's
77
+ native display, with discovery depending on the tag and browser. Prefer `div`
78
+ or `section` containers for discoverable content:
79
+
80
+ ```html
81
+ <section hidden="until-found">Searchable content</section>
82
+ ```
83
+
84
+ These rules run in both default and on-demand preflight. With preflight disabled,
85
+ the application supplies its own hiding rules.
70
86
 
71
87
  Native control appearance, focus outlines, disabled state, and disclosure
72
88
  behavior remain intact. Marker-free semantic lists should use `role="list"`
73
89
  for Safari/VoiceOver. The reset adds no brand palette or global motion policy.
74
- Element-local `reset-*` shortcuts remain in the optional Shortcuts preset;
90
+ Element-local `reset-*` shortcuts belong to the optional Official preset;
75
91
  Standard does not register those classes.
76
92
 
77
- CSS entries declare named variants with `@custom theme <name> {}`. Each emits
78
- zero-specificity variable overrides and a dynamic `theme-<name>` class.
79
- Preset-aware merging treats those classes as one opaque family.
93
+ ### Role palettes and mode regions
80
94
 
81
- ## Class merging
95
+ Primary creates emphasis. Neutral builds the interface. Semantic colors communicate state.
82
96
 
83
- ```ts
84
- import { cn, pluginStandard } from "@teacss/preset-standard/merge";
97
+ Standard provides seven 12-step roles: primary → gray, neutral → sage,
98
+ focused → neutral, success → jade, warning → amber, failure → red, general → blue. Use
99
+ `bg-color:primary-200 text-color:primary-950` directly. Neither Standard nor
100
+ Official adds background/hover/foreground aliases; role steps already describe
101
+ those uses. Keep palette selection in central configuration.
85
102
 
86
- cn("p:4 d:flex", "p:8"); // "d:flex p:8"
87
- cn("p:4", "p:invalid"); // "p:invalid"
103
+ The role order is primary, neutral, then the states: focused, success, warning,
104
+ failure, general. Focused follows the resolved neutral scale at each step when
105
+ no focused selection applies, including when `@focused` is omitted. Declaring
106
+ `@focused` only enables choices; it does not change this fallback. A configured
107
+ `data-css-focused` selection overrides it. Empty, unknown or undeclared selections
108
+ return to the region’s neutral palette; `theme.rolePalettes.focused: []` clears
109
+ independent choices. Application `@custom` overrides still take precedence in
110
+ the base mapping.
111
+
112
+ Declare selectable physical palettes in the CSS entry:
113
+
114
+ ```css
115
+ @presets "standard,official";
116
+ @primary "gray,gold,red,blue";
117
+ @neutral "sage,slate";
118
+ @focused "gray,blue";
119
+ @success "jade,green";
120
+ @warning "amber,orange";
121
+ @failure "red,ruby";
122
+ @general "blue,cyan";
123
+ @teacss;
124
+ ```
125
+
126
+ Lists add available selections, not defaults. Repeated declarations/imports union
127
+ and sort each role's names. Sources must be lowercase kebab-case physical palettes
128
+ with all 12 base steps and no direct or transitive role references in any mode.
129
+ Programmatic `theme.rolePalettes` has seven optional arrays; later arrays replace
130
+ earlier arrays per role, and `[]` clears that role. It is global configuration,
131
+ not a scale or a mode field. No DOM scanning is involved.
132
+
133
+ ```html
134
+ <html data-css-primary="gray" data-css-neutral="sage" data-css-mode="dark">
135
+ <body>
136
+ <section data-css-primary="red" data-css-mode="light">
137
+ <p>Red / sage / light</p>
138
+ <article data-css-neutral="slate">Gray / slate / dark</article>
139
+ </section>
140
+ </body>
141
+ </html>
142
+ ```
143
+
144
+ Any of the seven `data-css-<role>` attributes, `data-css-mode`, or configured mode
145
+ alias starts a region. Each of its eight dimensions uses the same selection order:
146
+ **own attribute → root HTML attribute → configured default**. Intermediate
147
+ regions are ignored. Plain descendants inherit until the next region. Empty,
148
+ unknown, or undeclared values explicitly select the configured default and block
149
+ root fallback. Removing the last marker rejoins the enclosing region. Runtime
150
+ attribute changes need no regeneration. `data-css-theme` has no TeaCSS meaning.
151
+
152
+ `@dark` / `@light` test the final styled target's region; pseudo-elements use their
153
+ host. No explicit mode activates neither condition. `defaultMode` names the base
154
+ palette; `@os-dark` / `@os-light` remain independent media queries.
155
+ `modeSelectors: { dark: ".night", light: ".day" }` adds aliases; explicit mode
156
+ attributes win, both aliases select neutral mode, and neither alias is implicit.
157
+ Programmatic mode names preserve case, spaces and punctuation; empty names, NUL
158
+ and unpaired surrogates are rejected.
159
+
160
+ Global `@custom` sets scales and colors; `@custom dark` changes colors only.
161
+ Whole-theme variants are unsupported. `@custom theme ...` and `theme.themes`
162
+ are errors. Palette selection never changes spacing, radii or shadow templates.
163
+
164
+ ### Relationship markers
165
+
166
+ `data-css-group` enables anonymous ancestor conditions such as `@group-hover`;
167
+ `data-css-peer` enables preceding-sibling conditions such as `@peer-checked`.
168
+ Named values are ASCII-whitespace-separated lists: `data-css-group="card panel"`
169
+ also enables `@group-hover/card` and `@group-hover/panel`. Presence always
170
+ matches anonymous conditions; an empty value enables only anonymous ones.
171
+ Names are case-sensitive. Group remains descendant matching, peer uses `~`.
172
+
173
+ ## Root language conditions
174
+
175
+ ```html
176
+ <html lang="zh">
177
+ <body><p lang="en" class="p:4 p:10@lang-zh">...</p></body>
178
+ </html>
88
179
  ```
89
180
 
90
- The `/merge` entry also exports `createStandardMerger()`. Merge uses token
91
- shape and does not validate values. Combine preset plugins for a wider merger:
181
+ `@lang-<tag>` compares the complete root `html:root[lang]` value, ignoring ASCII
182
+ case without trimming. Here `@lang-zh` matches and `@lang-en` does not.
183
+ `zh`, `zh-CN`, and `zh-Hans` are independent: `@lang-zh` does not match a root
184
+ value of `zh-CN`. Local `lang`, theme, and mode regions do not override the root
185
+ language.
186
+ Root elements, descendants, targets, and pseudo-elements share this condition.
187
+
188
+ Use BCP 47 tags: `zh`, `zh-CN`, `zh-Hans`, `zh-Hans-CN`, `es-419`,
189
+ `de-1996`, and `en-u-ca-gregory`. Extlang, private-use (`x-project`), and
190
+ grandfathered (`i-klingon`) forms are also supported. Validation checks structure,
191
+ not registry membership; it does not canonicalize aliases or infer parent tags.
192
+ Tag case is unrestricted: `@lang-zh-CN` and `@lang-zh-cn` match identically.
193
+ Non-ASCII characters, malformed subtags, underscores, whitespace, wildcards,
194
+ and escapes are unmatched. The lowercase `lang-` prefix is reserved: rename custom
195
+ breakpoint keys using it, including invalid language forms. Existing `@max-<name>`
196
+ syntax is unchanged.
197
+
198
+ `@!lang-en` also matches missing/empty root language. Conditions compose with
199
+ states, relations, queries, modes, targets, shortcuts, and `@apply` using AND.
200
+ Conflicting languages never match; repetition follows ordinary suffix behavior.
201
+ The language guard contributes `0,1,0` specificity before existing wrappers;
202
+ `space-*` / `divide-*` keep their zero-specificity selectors.
203
+
204
+ Changing root `lang` updates generated CSS without runtime listeners or
205
+ regeneration, including with preflight disabled. Keep complete tokens in scanned
206
+ sources or safelists; do not escape `@` in HTML/JSX class strings. Each HTML iframe
207
+ uses its own root; propagation across Shadow DOM or independent XML documents
208
+ is unsupported. Applications own translation, `dir`, and SSR's initial language.
209
+
210
+ ## Class merging
92
211
 
93
212
  ```ts
94
- import { createMerger } from "@teacss/classes";
95
- import { pluginIcon } from "@teacss/preset-icons/merge";
96
- import { pluginStandard } from "@teacss/preset-standard/merge";
213
+ import { cn } from "@teacss/classes";
97
214
 
98
- const cn = createMerger({ plugins: [pluginStandard, pluginIcon] });
215
+ cn("p:4 d:flex", "p:8"); // "d:flex p:8"
216
+ cn("p:4", "p:invalid"); // "p:invalid"
217
+ cn("p:2@lang-en", "p:4@lang-en"); // "p:4@lang-en"
218
+ cn("p:2@lang-ZH", "p:4@lang-zh"); // "p:4@lang-zh"
99
219
  ```
100
220
 
221
+ `cn` uses fixed Standard and Icons rules. Install and import `@teacss/classes`
222
+ directly for runtime class composition.
223
+ Merge uses prefix shape and ordered conditions, ignoring ASCII language-tag
224
+ case while preserving each winning token's spelling. It does not validate values
225
+ or condition support: malformed suffixes can merge but emit no CSS.
226
+ Unknown declarations merge
227
+ only by their own prefix; application shortcuts have no cross-prefix footprint.
228
+
101
229
  Native shorthands are emitted before their component overrides. For example,
102
230
  `cn("border:[1px_solid_red]", "border-t-width:4")` retains both classes and
103
231
  produces a 4px top border. Reversing those arguments leaves only the shorthand.
232
+ The same ordering covers place/gap/columns, text families and corner shapes;
233
+ explicit display and overflow can override `line-clamp`'s corresponding declarations.
104
234
 
105
235
  ## Value and composition boundaries
106
236
 
@@ -108,23 +238,34 @@ produces a 4px top border. Reversing those arguments leaves only the shorthand.
108
238
  `border-a-width:[1px_2px]`; physical x/y and single-edge forms accept one.
109
239
  - `[…]` keeps structurally valid arbitrary CSS, including
110
240
  `animation-name:[var(--name)]`. It does not guarantee browser support.
241
+ - Custom-variable values and `font-palette` use the shared structural checks;
242
+ failed bracket/variable decoding cannot fall back to unchecked raw CSS.
243
+ - A configured `DEFAULT` key references its unsuffixed variable: `p:DEFAULT`
244
+ reads `--spacing`, including through negative spacing. Applications supply
245
+ these scale variables when preflight is disabled.
246
+ - Named shadows use global templates and support local color overrides.
247
+ `shadow:$shadow-card` reads a complete raw variable without recoloring;
248
+ `box-shadow:[…]` replaces the whole composed surface.
249
+ - Named animations quote their decoded CSS names to avoid shorthand keyword
250
+ ambiguity. Numeric zero iteration counts remain zero.
111
251
  - Bare background/mask sizes cannot be negative.
112
252
  - Border-spacing axis variables do not inherit into nested tables.
113
253
  - Gradient properties are registered on demand, retaining shared fallbacks
114
254
  and neutral additive-mask layers without registering unrelated families.
115
255
 
116
- ## Bare utilities
256
+ ## Explicit activators
117
257
 
118
- The closed built-in bare-token inventory is:
258
+ Standard utilities all use `property:value`. The following activators accept
259
+ exactly `on`, with ordinary importance and suffix conditions:
119
260
 
120
261
  ```txt
121
- mask-g-linear mask-g-radial mask-g-conic
122
- divide-x divide-y
123
- space-x-reverse space-y-reverse space-a-reverse space-c-reverse
262
+ mask-g-linear:on mask-g-radial:on mask-g-conic:on
263
+ space-x-reverse:on space-y-reverse:on space-a-reverse:on space-c-reverse:on
124
264
  ```
125
265
 
126
- All other Standard utilities use `property:value`. Values are prefix-specific
127
- and may support theme references, arbitrary values, CSS-wide keywords, `!`, and
128
- trailing conditions.
266
+ Use `divide-x-width:1` / `divide-y-width:1` for 1px child dividers. The former
267
+ nine bare spellings emit nothing; bare names are reserved for registered
268
+ application or Official shortcuts. Removing an activator restores its default;
269
+ `off`, `true`, variable, arbitrary, and CSS-wide values are unsupported.
129
270
 
130
271
  Pre-1.0. Keep tests and documentation aligned when changing vocabulary.
package/dist/index.d.ts CHANGED
@@ -64,6 +64,7 @@ declare function transformerCompileClass(options?: CompileClassOptions): SourceC
64
64
  type ColorPalette = Record<string, Record<string, string>>;
65
65
  /** Semantic color roles emitted as `--color-<role>` variables. */
66
66
  type SemanticColors = Record<string, string | number>;
67
+ type ColorRole = "primary" | "neutral" | "focused" | "success" | "warning" | "failure" | "general";
67
68
  interface ThemeAnimation {
68
69
  keyframes?: Record<string, string>;
69
70
  durations?: Record<string, string>;
@@ -74,8 +75,10 @@ interface ThemeAnimation {
74
75
  interface Theme {
75
76
  /** Named color palette; each color is a 12-step `100...950` scale. */
76
77
  colors?: ColorPalette;
77
- /** Semantic color roles such as `background`, `foreground`, `border`, and `emphasis`. */
78
+ /** Application semantic colors. Presets supply an empty namespace. */
78
79
  semanticColors?: SemanticColors;
80
+ /** Physical palettes available through each data-css-<role> attribute. Lists replace per role. */
81
+ rolePalettes?: Partial<Record<ColorRole, readonly string[]>>;
79
82
  breakpoint?: Record<string, string>;
80
83
  container?: Record<string, string>;
81
84
  spacing?: Record<string, string>;
@@ -100,8 +103,8 @@ interface Theme {
100
103
  /**
101
104
  * Non-default mode color overrides. `theme.colors` and
102
105
  * `theme.semanticColors` are the default mode; each entry here (e.g. `dark`)
103
- * overrides color variables under the mode's class ancestor (`.<mode>`, or the
104
- * configured `dark`/`light` selector). Only declare values that differ. OS
106
+ * contributes to the complete mapping for its `data-css-mode` region (or a
107
+ * configured `dark`/`light` alias). Only declare values that differ. OS
105
108
  * preference uses `@os-dark` / `@os-light`.
106
109
  */
107
110
  modes?: Record<string, {
@@ -110,38 +113,19 @@ interface Theme {
110
113
  }>;
111
114
  /** The mode whose palette is `theme.colors` (need not appear in `modes`). @default "light" */
112
115
  defaultMode?: string;
113
- /**
114
- * Named theme variants (`@custom theme <name>`): subtree-scoped variable
115
- * overrides emitted under `:where(.theme-<name>)` after the base and mode
116
- * blocks. The bare `theme-<name>` class activates one. A variant may only
117
- * override values the base theme declares — `@teacss/config` enforces that
118
- * contract for entry-declared variants.
119
- */
120
- themes?: Record<string, ThemeVariant>;
121
- /** Root selector(s) used when emitting preflight CSS custom properties */
116
+ /** Additional declaration points inside every mode/palette region; roots always receive the complete mapping. */
122
117
  preflightRoot?: Arrayable<string>;
123
118
  /** Additional CSS custom properties merged after the default theme-derived preflight variables */
124
119
  preflightBase?: Record<string, string | number>;
125
120
  }
126
- /**
127
- * One named theme variant: the scalar scales plus color-only mode overrides
128
- * (entry-authored variants carry only `dark`). Structural records, mode
129
- * bookkeeping, and preflight options stay top-level-theme-only.
130
- */
131
- interface ThemeVariant extends Omit<Theme, "animation" | "modes" | "defaultMode" | "themes" | "preflightRoot" | "preflightBase"> {
132
- modes?: Record<string, {
133
- colors?: ColorPalette;
134
- semanticColors?: SemanticColors;
135
- }>;
136
- }
137
121
  type CustomRule = Rule<Theme>;
138
- /** Ancestor selectors for the class-triggered `dark` / `light` modes. @default `.dark` / `.light` */
122
+ /** Optional region-marker aliases for `dark` / `light`; canonical `data-css-mode` takes precedence. */
139
123
  interface ModeSelectors {
140
124
  dark?: string;
141
125
  light?: string;
142
126
  }
143
127
  interface PresetStandardOptions extends PresetOptions {
144
- /** Override the `dark` / `light` ancestor selectors, e.g. `{ dark: "[data-theme=dark]" }`. */
128
+ /** Add `dark` / `light` region aliases, e.g. `{ dark: ".night" }`; no aliases are enabled by default. */
145
129
  modeSelectors?: ModeSelectors;
146
130
  /**
147
131
  * Container sizes for the `@container-*` conditions, merged over the default