next-style 1.1.9 → 1.2.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/dist/index.d.ts CHANGED
@@ -1,7 +1,23 @@
1
1
  import type { Properties } from "csstype";
2
2
  import type { DetailedReactHTMLElement } from "react";
3
3
  /**
4
- * Style object supported by next-style
4
+ * Style object used by NextStyle.
5
+ *
6
+ * Supports:
7
+ * - Standard CSS properties (camelCase)
8
+ * - Pseudo states via `_hover`, `_focus`, `_active`
9
+ * - Responsive media queries via `_sm` ~ `_xxl`
10
+ *
11
+ * Example:
12
+ * ```ts
13
+ * const button = css({
14
+ * padding: "8px 16px",
15
+ * backgroundColor: "black",
16
+ * color: "white",
17
+ * _hover: { opacity: 0.8 },
18
+ * _md: { padding: "12px 20px" }
19
+ * })
20
+ * ```
5
21
  */
6
22
  export type NextStyleProperties = {
7
23
  [K in keyof Properties<string | number>]?: Properties<string | number>[K];
@@ -28,42 +44,252 @@ type FontFaceObject = {
28
44
  unicodeRange?: string;
29
45
  };
30
46
  type PseudoState = "hover" | "focus" | "active";
47
+ /**
48
+ * Builder for defining relations starting from a generated class name.
49
+ *
50
+ * This builder should only be created via `NextStyle.when(...)`.
51
+ */
31
52
  declare class RelationBuilder {
32
53
  private ns;
33
54
  private source;
34
55
  private pseudo?;
56
+ /**
57
+ * @internal
58
+ * @param ns NextStyle instance
59
+ * @param source Source class name generated by `css()`
60
+ * @param pseudo Optional pseudo state applied to the source
61
+ */
35
62
  constructor(ns: NextStyle, source: string, pseudo?: PseudoState | undefined);
63
+ /**
64
+ * Apply `:hover` pseudo state to the source selector.
65
+ *
66
+ * All subsequent relations will be scoped under `:hover`.
67
+ *
68
+ * @returns RelationBuilder
69
+ *
70
+ * Example:
71
+ * ```ts
72
+ * when(card)
73
+ * .hover()
74
+ * .child(icon, { opacity: 1 })
75
+ * ```
76
+ */
36
77
  hover(): RelationBuilder;
78
+ /**
79
+ * Apply `:focus` pseudo state to the source selector.
80
+ *
81
+ * @returns RelationBuilder
82
+ */
37
83
  focus(): RelationBuilder;
84
+ /**
85
+ * Apply `:active` pseudo state to the source selector.
86
+ *
87
+ * @returns RelationBuilder
88
+ */
38
89
  active(): RelationBuilder;
90
+ /**
91
+ * Define styles for an adjacent sibling selector.
92
+ *
93
+ * CSS equivalent:
94
+ * `.source + .target`
95
+ *
96
+ * @param target Target class name generated by `css()`
97
+ * @param style Style applied to the target element
98
+ *
99
+ * Example:
100
+ * ```ts
101
+ * when(input)
102
+ * .focus()
103
+ * .adjacent(label, { color: "red" })
104
+ * ```
105
+ */
39
106
  adjacent(target: string, style: NextStyleProperties): void;
107
+ /**
108
+ * Define styles for a direct child selector.
109
+ *
110
+ * CSS equivalent:
111
+ * `.source > .target`
112
+ *
113
+ * @param target Target class name generated by `css()`
114
+ * @param style Style applied to the target element
115
+ */
40
116
  child(target: string, style: NextStyleProperties): void;
117
+ /**
118
+ * Define styles for a general sibling selector.
119
+ *
120
+ * CSS equivalent:
121
+ * `.source ~ .target`
122
+ *
123
+ * @param target Target class name generated by `css()`
124
+ * @param style Style applied to the target element
125
+ */
41
126
  sibling(target: string, style: NextStyleProperties): void;
127
+ /**
128
+ * Define styles for a descendant selector.
129
+ *
130
+ * CSS equivalent:
131
+ * `.source .target`
132
+ *
133
+ * @param target Target class name generated by `css()`
134
+ * @param style Style applied to the target element
135
+ */
42
136
  descendant(target: string, style: NextStyleProperties): void;
137
+ /**
138
+ * @internal
139
+ * Emit a global relational rule.
140
+ */
43
141
  private emit;
44
142
  }
45
143
  /**
46
- * NextStyle runtime CSS-in-JS engine
144
+ * NextStyle
145
+ *
146
+ * Lightweight runtime CSS-in-JS engine with deterministic class names.
147
+ *
148
+ * Features:
149
+ * - Scoped class generation
150
+ * - Nested pseudo selectors
151
+ * - Responsive media queries
152
+ * - Global styles
153
+ * - Keyframes and font-face support
154
+ *
155
+ * Example:
156
+ * ```ts
157
+ * const ns = new NextStyle("app")
158
+ * ```
47
159
  */
48
160
  export declare class NextStyle {
49
161
  private prefix;
50
162
  private rules;
51
- /** store merged global style objects */
52
163
  private globalStore;
53
164
  constructor(prefix?: string);
165
+ /**
166
+ * Generate a scoped class name from a style object.
167
+ *
168
+ * The returned value is a branded class name and is intended
169
+ * to be used with `when(...)` and relational APIs.
170
+ *
171
+ * @param style Style definition object
172
+ * @returns Scoped class name
173
+ *
174
+ * Example:
175
+ * ```ts
176
+ * const button = css({ padding: 8 })
177
+ * ```
178
+ */
54
179
  css: (style: NextStyleProperties) => string;
55
180
  /**
56
- * Global style with property-level merge
181
+ * Define a global CSS selector.
182
+ *
183
+ * Styles are merged at property level instead of overwritten.
184
+ *
185
+ * @param selector CSS selector
186
+ * @param style Style definition
187
+ *
188
+ * Example:
189
+ * ```ts
190
+ * global("body", {
191
+ * fontFamily: "Kanit, sans-serif"
192
+ * })
193
+ * ```
57
194
  */
58
195
  global: (selector: string, style: NextStyleProperties) => void;
59
196
  /**
60
197
  * Apply default browser reset styles
198
+ * ```css
199
+ * html, body {
200
+ * max-width: 100vw;
201
+ * overflow-x: hidden;
202
+ * }
203
+ * body {
204
+ * color: "black";
205
+ * background: "white";
206
+ * -moz-osx-font-smoothing: grayscale;
207
+ * -webkit-font-smoothing: antialiased;
208
+ * font-family: "Arial, Helvetica, sans-serif"
209
+ * }
210
+ * *,
211
+ * *::before,
212
+ * *::after {
213
+ * margin: 0;
214
+ * padding: 0;
215
+ * box-sizing: border-box;
216
+ * }
217
+ * a {
218
+ * color: inherit;
219
+ * text-decoration: none
220
+ * }
221
+ * ```
222
+ */
223
+ resetStyle: () => this;
224
+ /**
225
+ * Create relational selectors based on a class generated by `css(...)`.
226
+ *
227
+ * ⚠️ This method only accepts class names returned from `css`.
228
+ *
229
+ * @param source Class name generated by `css`
230
+ *
231
+ * Example:
232
+ * ```ts
233
+ * const card = css({ ... })
234
+ *
235
+ * when(card)
236
+ * .hover()
237
+ * .child("icon", { opacity: 1 })
238
+ * ```
61
239
  */
62
- resetBrowserStyle: () => void;
63
240
  when: (source: string) => RelationBuilder;
241
+ /**
242
+ * Register keyframes animation.
243
+ *
244
+ * @param frames Keyframes definition
245
+ * @returns Generated animation name
246
+ *
247
+ * Example:
248
+ * ```ts
249
+ * const fadeIn = keyframes({
250
+ * from: { opacity: 0 },
251
+ * to: { opacity: 1 }
252
+ * })
253
+ * ```
254
+ */
64
255
  keyframes: (frames: KeyframesObject) => string;
256
+ /**
257
+ * Register a `@font-face` rule.
258
+ *
259
+ * Each unique definition is injected only once.
260
+ *
261
+ * @param font Font-face definition
262
+ *
263
+ * Example:
264
+ * ```ts
265
+ * fontFace({
266
+ * fontFamily: "Kanit",
267
+ * src: "url(/kanit.woff2) format('woff2')",
268
+ * fontWeight: 400
269
+ * })
270
+ * ```
271
+ */
65
272
  fontFace: (font: FontFaceObject) => void;
273
+ /**
274
+ * Serialize all generated CSS into a single string.
275
+ *
276
+ * Intended for:
277
+ * - Manual `<style>` injection
278
+ * - SSR environments
279
+ *
280
+ * @returns CSS text or `null` if no styles exist
281
+ */
66
282
  toTextCss: () => string | null;
283
+ /**
284
+ * React component that injects generated CSS into a `<style>` tag.
285
+ *
286
+ * Returns `null` if no styles are registered.
287
+ *
288
+ * Example:
289
+ * ```tsx
290
+ * <StyleProvider />
291
+ * ```
292
+ */
67
293
  StyleProvider: () => DetailedReactHTMLElement<{
68
294
  children: string;
69
295
  }, HTMLStyleElement> | null;
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import postcss from"postcss";import{createElement}from"react";import autoprefixer from"autoprefixer";var processor=postcss([autoprefixer({overrideBrowserslist:[">0.2%","not dead","not op_mini all"]})]),postcssCache=new Map;function postcssTransform(cssText){let cached=postcssCache.get(cssText);if(cached)return cached;let result=processor.process(cssText,{from:void 0}).css;return postcssCache.set(cssText,result),result}function stableStringify(value){if(value==null||typeof value!=="object")return JSON.stringify(value);if(Array.isArray(value))return`[${value.map(stableStringify).join(",")}]`;return`{${Object.keys(value).sort().map((k)=>`"${k}":${stableStringify(value[k])}`).join(",")}}`}function createHashName(seed){let hash=BigInt("0xcbf29ce484222325"),prime=BigInt("0x100000001b3");for(let i=0;i<seed.length;i++)hash^=BigInt(seed.charCodeAt(i)),hash*=prime,hash&=BigInt("0xffffffffffffffff");return hash.toString(36).slice(0,9)}function toKebabCase(prop){return prop.replace(/[A-Z]/g,(m)=>`-${m.toLowerCase()}`)}var MEDIA_MAP={_sm:"(min-width:640px)",_md:"(min-width:768px)",_lg:"(min-width:1024px)",_xl:"(min-width:1280px)",_xxl:"(min-width:1536px)"};function mergeMedia(parent,current){if(!parent)return current;if(!current)return parent;return`${parent} and ${current}`}function serializeNested(style,ctx){let css="",declarations=[];for(let key in style){let value=style[key];if(value==null||typeof value==="object"||key.startsWith("_"))continue;declarations.push(`${toKebabCase(key)}:${value}`)}if(declarations.length){let rule=`${ctx.selector}{${declarations.join(";")}}`;css+=ctx.media?`@media ${ctx.media}{${rule}}`:rule}for(let pseudo of["_hover","_focus","_active"]){let value=style[pseudo];if(!value)continue;css+=serializeNested(value,{selector:`${ctx.selector}:${pseudo.slice(1)}`,media:ctx.media})}for(let key in MEDIA_MAP){let mediaKey=key,value=style[mediaKey];if(!value)continue;css+=serializeNested(value,{selector:ctx.selector,media:mergeMedia(ctx.media,MEDIA_MAP[mediaKey])})}return css}class RelationBuilder{ns;source;pseudo;constructor(ns,source,pseudo){this.ns=ns;this.source=source;this.pseudo=pseudo}hover(){return new RelationBuilder(this.ns,this.source,"hover")}focus(){return new RelationBuilder(this.ns,this.source,"focus")}active(){return new RelationBuilder(this.ns,this.source,"active")}adjacent(target,style){this.emit("+",target,style)}child(target,style){this.emit(">",target,style)}sibling(target,style){this.emit("~",target,style)}descendant(target,style){this.emit(" ",target,style)}emit(combinator,target,style){let pseudo=this.pseudo?`:${this.pseudo}`:"",selector=`.${this.source}${pseudo}${combinator}.${target}`;this.ns.global(selector,style)}}class NextStyle{prefix;rules=new Map;globalStore=new Map;constructor(prefix="next"){this.prefix=prefix}css=(style)=>{let seed=stableStringify(style),hash=createHashName(seed),className=`${this.prefix}_${hash}`,key=`class:${className}`;if(!this.rules.has(key)){let raw=serializeNested(style,{selector:`.${className}`}),cssText=postcssTransform(raw);this.rules.set(key,cssText)}return className};global=(selector,style)=>{let key=`global:${selector}`,merged={...this.globalStore.get(key)??{},...style};this.globalStore.set(key,merged);let raw=serializeNested(merged,{selector}),cssText=postcssTransform(raw);if(this.rules.has(key))this.rules.delete(key);this.rules.set(key,cssText)};resetBrowserStyle=()=>{this.global("html,body",{maxWidth:"100vw",overflowX:"hidden"}),this.global("body",{color:"black",background:"white",fontFamily:"Arial, Helvetica, sans-serif",WebkitFontSmoothing:"antialiased",MozOsxFontSmoothing:"grayscale"}),this.global("*",{boxSizing:"border-box",padding:0,margin:0}),this.global("a",{color:"inherit",textDecoration:"none"})};when=(source)=>new RelationBuilder(this,source);keyframes=(frames)=>{let seed=stableStringify(frames),hash=createHashName(seed),name=`${this.prefix}_${hash}`,key=`@keyframes:${name}`;if(!this.rules.has(key)){let body="";for(let step in frames){let declarations=[],frame=frames[step];for(let prop in frame)declarations.push(`${toKebabCase(prop)}:${frame[prop]}`);body+=`${step}{ ${declarations.join(";")} }`}let cssText=postcssTransform(`@keyframes ${name}{ ${body} }`);this.rules.set(key,cssText)}return name};fontFace=(font)=>{let seed=stableStringify(font),key=`@font-face:${createHashName(seed)}`;if(!this.rules.has(key)){let declarations=[];for(let prop in font)declarations.push(`${toKebabCase(prop)}:${font[prop]}`);let cssText=postcssTransform(`@font-face{ ${declarations.join(";")} }`);this.rules.set(key,cssText)}};toTextCss=()=>{if(this.rules.size===0)return null;let cssText="";for(let rule of this.rules.values())cssText+=rule+`
1
+ import postcss from"postcss";import{createElement}from"react";import autoprefixer from"autoprefixer";var processor=postcss([autoprefixer({overrideBrowserslist:[">0.2%","not dead","not op_mini all"]})]),postcssCache=new Map;function postcssTransform(cssText){let cached=postcssCache.get(cssText);if(cached)return cached;let result=processor.process(cssText,{from:void 0}).css;return postcssCache.set(cssText,result),result}function stableStringify(value){if(value==null||typeof value!=="object")return JSON.stringify(value);if(Array.isArray(value))return`[${value.map(stableStringify).join(",")}]`;return`{${Object.keys(value).sort().map((k)=>`"${k}":${stableStringify(value[k])}`).join(",")}}`}function createHashName(seed){let hash=BigInt("0xcbf29ce484222325"),prime=BigInt("0x100000001b3");for(let i=0;i<seed.length;i++)hash^=BigInt(seed.charCodeAt(i)),hash*=prime,hash&=BigInt("0xffffffffffffffff");return hash.toString(36).slice(0,9)}function toKebabCase(prop){return prop.replace(/[A-Z]/g,(m)=>`-${m.toLowerCase()}`)}var MEDIA_MAP={_sm:"(min-width:640px)",_md:"(min-width:768px)",_lg:"(min-width:1024px)",_xl:"(min-width:1280px)",_xxl:"(min-width:1536px)"};function mergeMedia(parent,current){if(!parent)return current;if(!current)return parent;return`${parent} and ${current}`}function serializeNested(style,ctx){let css="",declarations=[];for(let key in style){let value=style[key];if(value==null||typeof value==="object"||key.startsWith("_"))continue;declarations.push(`${toKebabCase(key)}:${value}`)}if(declarations.length){let rule=`${ctx.selector}{${declarations.join(";")}}`;css+=ctx.media?`@media ${ctx.media}{${rule}}`:rule}for(let pseudo of["_hover","_focus","_active"]){let value=style[pseudo];if(!value)continue;css+=serializeNested(value,{selector:`${ctx.selector}:${pseudo.slice(1)}`,media:ctx.media})}for(let key in MEDIA_MAP){let mediaKey=key,value=style[mediaKey];if(!value)continue;css+=serializeNested(value,{selector:ctx.selector,media:mergeMedia(ctx.media,MEDIA_MAP[mediaKey])})}return css}class RelationBuilder{ns;source;pseudo;constructor(ns,source,pseudo){this.ns=ns;this.source=source;this.pseudo=pseudo}hover(){return new RelationBuilder(this.ns,this.source,"hover")}focus(){return new RelationBuilder(this.ns,this.source,"focus")}active(){return new RelationBuilder(this.ns,this.source,"active")}adjacent(target,style){this.emit("+",target,style)}child(target,style){this.emit(">",target,style)}sibling(target,style){this.emit("~",target,style)}descendant(target,style){this.emit(" ",target,style)}emit(combinator,target,style){let pseudo=this.pseudo?`:${this.pseudo}`:"",selector=`.${this.source}${pseudo}${combinator}.${target}`;this.ns.global(selector,style)}}class NextStyle{prefix;rules=new Map;globalStore=new Map;constructor(prefix="next"){this.prefix=prefix}css=(style)=>{let seed=stableStringify(style),hash=createHashName(seed),className=`${this.prefix}_${hash}`,key=`class:${className}`;if(!this.rules.has(key)){let raw=serializeNested(style,{selector:`.${className}`}),cssText=postcssTransform(raw);this.rules.set(key,cssText)}return className};global=(selector,style)=>{let key=`global:${selector}`,merged={...this.globalStore.get(key)??{},...style};this.globalStore.set(key,merged);let raw=serializeNested(merged,{selector}),cssText=postcssTransform(raw);if(this.rules.has(key))this.rules.delete(key);this.rules.set(key,cssText)};resetStyle=()=>{return this.global("html,body",{maxWidth:"100vw",overflowX:"hidden"}),this.global("body",{color:"black",background:"white",MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontFamily:"Arial, Helvetica, sans-serif"}),this.global("*,*::before,*::after",{margin:0,padding:0,boxSizing:"border-box"}),this.global("a",{color:"inherit",textDecoration:"none"}),this};when=(source)=>new RelationBuilder(this,source);keyframes=(frames)=>{let seed=stableStringify(frames),hash=createHashName(seed),name=`${this.prefix}_${hash}`,key=`@keyframes:${name}`;if(!this.rules.has(key)){let body="";for(let step in frames){let declarations=[],frame=frames[step];for(let prop in frame)declarations.push(`${toKebabCase(prop)}:${frame[prop]}`);body+=`${step}{ ${declarations.join(";")} }`}let cssText=postcssTransform(`@keyframes ${name}{ ${body} }`);this.rules.set(key,cssText)}return name};fontFace=(font)=>{let seed=stableStringify(font),key=`@font-face:${createHashName(seed)}`;if(!this.rules.has(key)){let declarations=[];for(let prop in font)declarations.push(`${toKebabCase(prop)}:${font[prop]}`);let cssText=postcssTransform(`@font-face{ ${declarations.join(";")} }`);this.rules.set(key,cssText)}};toTextCss=()=>{if(this.rules.size===0)return null;let cssText="";for(let rule of this.rules.values())cssText+=rule+`
2
2
  `;return cssText};StyleProvider=()=>{if(this.rules.size===0)return null;let cssText="";for(let rule of this.rules.values())cssText+=rule+`
3
3
  `;return createElement("style",{children:cssText})}}export{NextStyle};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-style",
3
- "version": "1.1.9",
3
+ "version": "1.2.0",
4
4
  "description": "Lightweight runtime CSS-in-JS engine with nested pseudo selectors, media queries, global styles, keyframes, and font-face support.",
5
5
  "author": {
6
6
  "name": "kingslimes",