next-style 1.1.5 → 1.1.6
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 +196 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -155,6 +155,202 @@ css({
|
|
|
155
155
|
|
|
156
156
|
---
|
|
157
157
|
|
|
158
|
+
## Relation Selector API
|
|
159
|
+
|
|
160
|
+
`next-style` provides a **relation-based selector API** that allows you to define styles based on the state of one element affecting another element, without manually writing complex CSS selectors.
|
|
161
|
+
|
|
162
|
+
This API is designed to be:
|
|
163
|
+
- Declarative and readable
|
|
164
|
+
- Type-safe (no string selectors)
|
|
165
|
+
- Fully compatible with runtime CSS-in-JS
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### Basic Usage
|
|
170
|
+
|
|
171
|
+
``` ts
|
|
172
|
+
const { css, when } = new NextStyle()
|
|
173
|
+
|
|
174
|
+
const container = css({})
|
|
175
|
+
const button = css({})
|
|
176
|
+
|
|
177
|
+
when(container)
|
|
178
|
+
.hover()
|
|
179
|
+
.adjacent(button, {
|
|
180
|
+
backgroundColor: "red"
|
|
181
|
+
})
|
|
182
|
+
```
|
|
183
|
+
Equivalent CSS:
|
|
184
|
+
|
|
185
|
+
``` css
|
|
186
|
+
.container:hover + .button {
|
|
187
|
+
background-color: red;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Supported States
|
|
194
|
+
|
|
195
|
+
You can define relationships based on the following pseudo states:
|
|
196
|
+
|
|
197
|
+
- `hover()` → `:hover`
|
|
198
|
+
- `focus()` → `:focus`
|
|
199
|
+
- `active()` → `:active`
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
|
|
203
|
+
``` ts
|
|
204
|
+
when(input)
|
|
205
|
+
.focus()
|
|
206
|
+
.sibling(label, {
|
|
207
|
+
color: "blue"
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
Equivalent CSS:
|
|
211
|
+
|
|
212
|
+
``` css
|
|
213
|
+
input:focus ~ label {
|
|
214
|
+
color: blue;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Supported Relationships
|
|
221
|
+
|
|
222
|
+
### Adjacent Sibling (`+`)
|
|
223
|
+
|
|
224
|
+
Applies styles to the **immediately following sibling**.
|
|
225
|
+
|
|
226
|
+
``` ts
|
|
227
|
+
when(div)
|
|
228
|
+
.hover()
|
|
229
|
+
.adjacent(p, {
|
|
230
|
+
color: "red"
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
CSS equivalent:
|
|
234
|
+
|
|
235
|
+
``` css
|
|
236
|
+
div:hover + p {
|
|
237
|
+
color: red;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### General Sibling (`~`)
|
|
244
|
+
|
|
245
|
+
Applies styles to **all following siblings**.
|
|
246
|
+
|
|
247
|
+
``` ts
|
|
248
|
+
when(div)
|
|
249
|
+
.hover()
|
|
250
|
+
.sibling(p, {
|
|
251
|
+
color: "red"
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
CSS equivalent:
|
|
255
|
+
|
|
256
|
+
``` css
|
|
257
|
+
div:hover ~ p {
|
|
258
|
+
color: red;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### Child (`>`)
|
|
265
|
+
|
|
266
|
+
Applies styles to **direct children only**.
|
|
267
|
+
|
|
268
|
+
``` ts
|
|
269
|
+
when(card)
|
|
270
|
+
.hover()
|
|
271
|
+
.child(icon, {
|
|
272
|
+
transform: "scale(1.1)"
|
|
273
|
+
})
|
|
274
|
+
```
|
|
275
|
+
CSS equivalent:
|
|
276
|
+
|
|
277
|
+
``` css
|
|
278
|
+
card:hover > icon {
|
|
279
|
+
transform: scale(1.1);
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
### Descendant (space)
|
|
286
|
+
|
|
287
|
+
Applies styles to **any nested element**.
|
|
288
|
+
|
|
289
|
+
``` ts
|
|
290
|
+
when(menu)
|
|
291
|
+
.hover()
|
|
292
|
+
.descendant(item, {
|
|
293
|
+
backgroundColor: "#eee"
|
|
294
|
+
})
|
|
295
|
+
```
|
|
296
|
+
CSS equivalent:
|
|
297
|
+
|
|
298
|
+
``` css
|
|
299
|
+
menu:hover item {
|
|
300
|
+
background-color: #eee;
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Why Use `when()` Instead of `global()`?
|
|
307
|
+
|
|
308
|
+
While `global()` allows full control over raw selectors, `when()` provides:
|
|
309
|
+
|
|
310
|
+
- Clear intent and better readability
|
|
311
|
+
- No need to manually concatenate class names
|
|
312
|
+
- Safer refactoring (class tokens, not strings)
|
|
313
|
+
- IDE autocomplete and JSDoc support
|
|
314
|
+
|
|
315
|
+
Comparison:
|
|
316
|
+
|
|
317
|
+
``` ts
|
|
318
|
+
// global selector
|
|
319
|
+
global(`.${a}:hover + .${b}`, { color: "red" })
|
|
320
|
+
|
|
321
|
+
// relation API
|
|
322
|
+
when(a).hover().adjacent(b, { color: "red" })
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Notes
|
|
328
|
+
|
|
329
|
+
- `when()` works with class names generated by `css()`
|
|
330
|
+
- Relation rules are injected via the same internal pipeline as `global()`
|
|
331
|
+
- Media queries and nested styles are fully supported inside relation styles
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Example With Media Query
|
|
336
|
+
|
|
337
|
+
``` ts
|
|
338
|
+
when(container)
|
|
339
|
+
.hover()
|
|
340
|
+
.sibling(text, {
|
|
341
|
+
color: "red",
|
|
342
|
+
_md: {
|
|
343
|
+
color: "blue"
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
This API is intentionally minimal and composable, allowing you to express complex UI relationships without leaking CSS selector syntax into your application code.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
158
354
|
## Responsive Media Queries
|
|
159
355
|
|
|
160
356
|
Built-in breakpoints:
|
package/dist/index.d.ts
CHANGED
|
@@ -24,12 +24,73 @@ type FontFaceObject = {
|
|
|
24
24
|
fontDisplay?: string;
|
|
25
25
|
unicodeRange?: string;
|
|
26
26
|
};
|
|
27
|
+
type PseudoState = "hover" | "focus" | "active";
|
|
28
|
+
declare class RelationBuilder {
|
|
29
|
+
private ns;
|
|
30
|
+
private source;
|
|
31
|
+
private pseudo?;
|
|
32
|
+
constructor(ns: NextStyle, source: string, pseudo?: PseudoState | undefined);
|
|
33
|
+
/** :hover */
|
|
34
|
+
hover(): RelationBuilder;
|
|
35
|
+
/** :focus */
|
|
36
|
+
focus(): RelationBuilder;
|
|
37
|
+
/** :active */
|
|
38
|
+
active(): RelationBuilder;
|
|
39
|
+
/**
|
|
40
|
+
* Adjacent sibling selector
|
|
41
|
+
*
|
|
42
|
+
* CSS:
|
|
43
|
+
* div:hover + p
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* when(div).hover().adjacent(p, { color: "red" })
|
|
47
|
+
*/
|
|
48
|
+
adjacent(target: string, style: NextStyleProperties): void;
|
|
49
|
+
/**
|
|
50
|
+
* Child selector
|
|
51
|
+
*
|
|
52
|
+
* CSS:
|
|
53
|
+
* div:hover > p
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* when(div).hover().child(p, { color: "red" })
|
|
57
|
+
*/
|
|
58
|
+
child(target: string, style: NextStyleProperties): void;
|
|
59
|
+
/**
|
|
60
|
+
* General sibling selector
|
|
61
|
+
*
|
|
62
|
+
* CSS:
|
|
63
|
+
* div:hover ~ p
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* when(div).hover().sibling(p, { color: "red" })
|
|
67
|
+
*/
|
|
68
|
+
sibling(target: string, style: NextStyleProperties): void;
|
|
69
|
+
/**
|
|
70
|
+
* Descendant selector
|
|
71
|
+
*
|
|
72
|
+
* CSS:
|
|
73
|
+
* div:hover p
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* when(div).hover().descendant(p, { color: "red" })
|
|
77
|
+
*/
|
|
78
|
+
descendant(target: string, style: NextStyleProperties): void;
|
|
79
|
+
private emit;
|
|
80
|
+
}
|
|
27
81
|
export declare class NextStyle {
|
|
28
82
|
private prefix;
|
|
29
83
|
private rules;
|
|
30
84
|
constructor(prefix?: string);
|
|
31
85
|
css: (style: NextStyleProperties) => string;
|
|
32
86
|
global: (selector: string, style: NextStyleProperties) => void;
|
|
87
|
+
/**
|
|
88
|
+
* Relation selector API
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* when(container).hover().sibling(text)
|
|
92
|
+
*/
|
|
93
|
+
when: (source: string) => RelationBuilder;
|
|
33
94
|
keyframes: (frames: KeyframesObject) => string;
|
|
34
95
|
fontFace: (font: FontFaceObject) => void;
|
|
35
96
|
toTextCss: () => string | 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="",base="";for(let key in style){let value=style[key];if(value==null||typeof value==="object"||key.startsWith("_"))continue;base+=`${toKebabCase(key)}:${value};`}if(base){let rule=`${ctx.selector}{${base}}`;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 NextStyle{prefix;rules=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}`;if(!this.rules.has(key)){let raw=serializeNested(style,{selector}),cssText=postcssTransform(raw);this.rules.set(key,cssText)}};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 props="",frame=frames[step];for(let prop in frame)props+=`${toKebabCase(prop)}:${frame[prop]};`;body+=`${step}{${props}}`}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 body="";for(let prop in font)body+=`${toKebabCase(prop)}:${font[prop]};`;let cssText=postcssTransform(`@font-face{${body}}`);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="",base="";for(let key in style){let value=style[key];if(value==null||typeof value==="object"||key.startsWith("_"))continue;base+=`${toKebabCase(key)}:${value};`}if(base){let rule=`${ctx.selector}{${base}}`;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;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}`;if(!this.rules.has(key)){let raw=serializeNested(style,{selector}),cssText=postcssTransform(raw);this.rules.set(key,cssText)}};when=(source)=>{return 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 props="",frame=frames[step];for(let prop in frame)props+=`${toKebabCase(prop)}:${frame[prop]};`;body+=`${step}{${props}}`}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 body="";for(let prop in font)body+=`${toKebabCase(prop)}:${font[prop]};`;let cssText=postcssTransform(`@font-face{${body}}`);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.
|
|
3
|
+
"version": "1.1.6",
|
|
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",
|
|
@@ -62,4 +62,4 @@
|
|
|
62
62
|
"publishConfig": {
|
|
63
63
|
"access": "public"
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|