@n3e/styled 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Nicholas Makhija
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,518 @@
1
+ # styled
2
+
3
+ A high performance and easy to use CSS-in-JS library with support for server-side rendering. Built similar to other popular libraries, with a familiar API, providing a great overall developer experience.
4
+
5
+ Styles are defined using Object Literal syntax, meaning instead of writing properties in `kebab-case` like regular CSS, they are in `camelCase` eg: `max-width` would be `maxWidth`. Don't worry, Styled provides autocompletion and type checking for CSS properties and values thanks to [CSSType](https://github.com/frenic/csstype#readme).
6
+
7
+ There is also a way to define global styles using CSS, more on that later.
8
+
9
+ ## Installation
10
+
11
+
12
+ ```shell
13
+ # npm
14
+ npm install @n3e/styled
15
+
16
+ # yarn
17
+ yarn add @n3e/styled
18
+
19
+ #pnpm
20
+ pnpm add @n3e/styled
21
+
22
+ #bun
23
+ bun add @n3e/styled
24
+ ```
25
+
26
+ ## Getting started
27
+
28
+ Let's start by building a simple button.
29
+
30
+ ```tsx
31
+ import styled from '@n3e/styled';
32
+
33
+ const buttonAccent = 'hotpink';
34
+
35
+ const Button = styled.button({
36
+ appearance: 'none',
37
+ backgroundColor: buttonAccent,
38
+ border: `2px solid ${buttonAccent}`,
39
+ borderRadius: '4px',
40
+ fontSize: '0.875rem',
41
+ lineHeight: '1.5rem',
42
+ margin: 0,
43
+ padding: '0 6px',
44
+ transition: '0.2s linear'
45
+ });
46
+
47
+ const Composition = () => (
48
+ <Button>
49
+ I'm a button
50
+ </Button>
51
+ );
52
+ ```
53
+
54
+ ## Pseudo
55
+
56
+ Styled comes with a [helper](docs/api.md#style-helper) for writing pseudo styles, below is an example of how to use `pseudo-classes`.
57
+
58
+ ```tsx
59
+ const Button = styled.button({
60
+ // button base styles
61
+
62
+ [style.hover]: {
63
+ backgroundColor: 'rgba(255, 105, 180, 0.7)'
64
+ },
65
+
66
+ // pseudo class function
67
+ [style.not(style.disabled)]: {
68
+ backgroundColor: 'purple',
69
+ borderColor: 'purple',
70
+ color: 'white'
71
+ },
72
+
73
+ // you can write it yourself, if preferred
74
+ ':hover': {
75
+ backgroundColor: 'rgba(255, 105, 180, 0.7)'
76
+ },
77
+
78
+ ':not(:disabled)': {
79
+ backgroundColor: 'purple',
80
+ borderColor: 'purple',
81
+ color: 'white'
82
+ }
83
+ });
84
+
85
+ const Composition = () => (
86
+ <Button>
87
+ I'm a button
88
+ </Button>
89
+ );
90
+ ```
91
+
92
+ Similarly `pseudo-element` styles are defined as shown below.
93
+
94
+ ```tsx
95
+ const PrefixedUsingBefore = styled.div({
96
+ [style.before]: {
97
+ content: '$'
98
+ },
99
+
100
+ // the same can be done manually
101
+ '::before': {
102
+ content: '$'
103
+ }
104
+ });
105
+
106
+ const Composition = () => (
107
+ <PrefixedUsingBefore>dollar bucks</PrefixedUsingBefore>
108
+ );
109
+ ```
110
+
111
+ ## Combinator
112
+
113
+ ### Descendant combinator
114
+
115
+ ```tsx
116
+ const Paragraph = styled.p({
117
+ margin: 0,
118
+ padding: 0
119
+ });
120
+
121
+ const RichText = styled.div({
122
+ [style.selector('p')]: {
123
+ margin: '0 0 24px'
124
+ },
125
+
126
+ // target a Styled Component
127
+ [style.selector(Paragraph)]: {
128
+ margin: '0 0 24px'
129
+ }
130
+ });
131
+
132
+ const Composition = () => (
133
+ <RichText>
134
+ <p>Para one text<p>
135
+ <Paragraph>Para two text<Paragraph>
136
+ </RichText>
137
+ );
138
+ ```
139
+
140
+ ### Child combinator (>)
141
+
142
+
143
+ ```tsx
144
+ const Image = styled.img({
145
+ display: 'block',
146
+ maxWith: '100%'
147
+ });
148
+
149
+ const Paragraph = styled.p({
150
+ // paragraph styles
151
+ });
152
+
153
+ const RichText = styled.div({
154
+ [style.selector('> img')]: {
155
+ border: '2px solid yellow'
156
+ },
157
+
158
+ // target a Styled Component
159
+ [style.selector(`> ${Image}`)]: {
160
+ border: '2px solid yellow'
161
+ }
162
+ });
163
+
164
+ const Composition = () => (
165
+ <RichText>
166
+ <Image src='path/to/image' />
167
+ <Paragraph>Para one text<Paragraph>
168
+ <Paragraph>Para two text<Paragraph>
169
+ </RichText>
170
+ );
171
+ ```
172
+
173
+ ### Sibling combinators (~ or +)
174
+
175
+ ```tsx
176
+ const Label = styled.label({
177
+ display: 'inline-block',
178
+ cursor: 'pointer',
179
+ verticalAlign: 'top',
180
+ });
181
+
182
+ const Checkbox = styled.input({
183
+ [style.checked]: {
184
+ [style.selector('+ label')]: {
185
+ fontWeight: 'bold'
186
+ }
187
+
188
+ // target a Styled Component
189
+ [style.selector(`+ ${Label}`)]: {
190
+ fontWeight: 'bold'
191
+ }
192
+ }
193
+ });
194
+
195
+
196
+ const Composition = () => (
197
+ <>
198
+ <Checkbox type='checkbox' {...otherProps} />
199
+ <Label>Checkbox label</Label>
200
+ <>
201
+ );
202
+ ```
203
+
204
+ ## Attribute selectors
205
+
206
+ You can event create selectors based on attributes and yes, Styled does comes with a [helper](docs/api.md#attribute-selector) for creating these.
207
+
208
+ ```tsx
209
+ const Link = styled.a({
210
+ // link base styles
211
+
212
+ [style.attribute('href').startsWith('http', 'https')]: {
213
+ // some attribute based styles
214
+ }
215
+
216
+ // you need to use Styled's combinator helper `or` to create
217
+ // nested selector lists (ie: comma separated selector)
218
+ [style.or(
219
+ '[href^="http"]',
220
+ '[href^="https"]'
221
+ )]: {
222
+ // some attribute based styles
223
+ },
224
+
225
+ // or have to duplicate style definitions
226
+ '[href^="http"]': { /* some attribute based styles */ },
227
+ '[href^="https"]': { /* some attribute based styles */ }
228
+ });
229
+
230
+ const Composition = () => (
231
+ <Link href='#'>
232
+ I'm a Link
233
+ </Link>
234
+ );
235
+ ```
236
+
237
+ ## At-rules
238
+
239
+ Nested at-rules such as `@media`, `@supports` and even `@container` are declared as demonstrated below.
240
+
241
+ ```tsx
242
+ const Container = styled.div({
243
+ // container base styles
244
+
245
+ '@media screen and (min-width: 576px)': {
246
+ maxWidth: '540px'
247
+ },
248
+
249
+ '@media screen and (min-width: 768px)': {
250
+ maxWidth: '720px'
251
+ },
252
+
253
+ // you can even nest at-rules
254
+ '@supports (display: flex)': {
255
+ display: 'flex',
256
+ flexDirection: 'column',
257
+
258
+ '@media screen and (min-width: 768px)': {
259
+ flexDirection: 'row'
260
+ }
261
+ }
262
+ });
263
+ ```
264
+
265
+ For examples of `@keyframes` and `@font-face`, refer to [API](docs/api.md#global-styles) section.
266
+
267
+ Styled also exposes a [helper](docs/api.md#media-query-builder) for building media queries.
268
+
269
+ ## Prop based styles
270
+
271
+ Property matching or pattern matching can be achieved by calling the `prop` method on the `style` helper.
272
+
273
+ ```tsx
274
+ const Link = styled.a({
275
+ display: 'inline-block',
276
+ textTransform: 'uppercase',
277
+ cursor: 'pointer',
278
+
279
+ [style.prop('isActive')]: {
280
+ color: 'hotpink'
281
+ textDecoration: 'none'
282
+ }
283
+ });
284
+
285
+ const Composition = () => (
286
+ <Link href='#' isActive>
287
+ I'm a Link
288
+ </Link>
289
+ );
290
+ ```
291
+
292
+ You can even specify a function that takes the prop being matched as its only argument and also type the props for the same.
293
+
294
+ ```tsx
295
+ type SVGProps = {
296
+ width?: number;
297
+ height?: number;
298
+ };
299
+
300
+ const Svg = styled.svg<SVGProps>({
301
+ display: 'inline-block',
302
+ stroke: 'transparent',
303
+ fill: 'currentColor',
304
+ verticalAlign: 'middle',
305
+ pointerEvents: 'none',
306
+ cursor: 'inherit',
307
+
308
+ [style.prop('width')]: (width: number) => ({
309
+ width: `${width}px`
310
+ }),
311
+
312
+ [style.prop('height')]: (height: number) => ({
313
+ height: `${height}px`
314
+ })
315
+ });
316
+
317
+ const Composition = () => (
318
+ <Svg width={36} height={36} />
319
+ );
320
+ ```
321
+
322
+
323
+ > **IMPORTANT!**
324
+ >
325
+ > In order to avoid maintaining a whitelist of props that are not meant to be passed down to the underlying HTML tag, all props used in any of the pattern matching functions will be treated as transient props and will **NOT** flow downward.
326
+ >
327
+ > Thus in the example above `width` and `height` props will not be passed to the underlying `svg` tag, so ensure styles are defined to handle that.
328
+
329
+ To match against multiple properties, just use one of `all`, `any` or `not` methods on the `style.props` helper.
330
+
331
+ ```typescript
332
+ type CheckboxUIProps = {
333
+ isChecked?: boolean;
334
+ isDisabled?: boolean;
335
+ };
336
+
337
+ const CheckboxUI = styled.span<CheckboxUIProps>({
338
+ [style.props.all('isChecked', 'isDisabled')]: {
339
+ opacity: 0.7
340
+ }
341
+ });
342
+ ```
343
+
344
+ In the above example, opacity styles will only be applied when both `isChecked` and `isDisabled` props are supplied to the `CheckBoxUI` component.
345
+
346
+ When using a function for matching multiple props, be sure to pass in **all** props or the props object if not destructuring.
347
+
348
+ ```typescript
349
+ type ButtonProps = {
350
+ borderColour?: string;
351
+ borderStyle?: 'solid' | 'dashed' | 'none';
352
+ };
353
+
354
+ const Button = styled.button<ButtonProps>({
355
+ // button base styles
356
+
357
+ [style.props.any('borderColour', 'borderStyle')]: ({
358
+ borderColour,
359
+ borderStyle
360
+ }: ButtonProps) => ({
361
+ borderColor: borderColour || 'purple',
362
+ borderStyle: borderStyle || 'solid'
363
+ })
364
+ });
365
+ ```
366
+
367
+ ## Generics
368
+
369
+ Not to confuse with Typescript's Generics, there is a mechanism for defining Components that are _"generic"_, basically a Higher Order Component that takes a `Component` as its only argument.
370
+
371
+ To define a Generic component, simply call the `generic` method as shown below.
372
+
373
+ ```typescript
374
+ type SizeProp = {
375
+ size?: number;
376
+ };
377
+
378
+ const GenericSize = styled.generic<SizeProp>({
379
+ boxSizing: 'border-box',
380
+
381
+ [style.prop('size')]: (size: number) => ({
382
+ fontSize: `${size / 16}rem`,
383
+ lineHeight: `${(size / 16) * 1.5}rem`
384
+ })
385
+ });
386
+ ```
387
+
388
+ Then wrap a component with it as shown below.
389
+
390
+ ```tsx
391
+ const Heading = styled.h1({
392
+ // some styles
393
+ });
394
+
395
+ const Wrapped = GenericSize(Heading);
396
+
397
+ const Composition = () => (
398
+ <Wrapped size={64}>
399
+ Heading Component wrapped
400
+ </Wrapped>
401
+ );
402
+ ```
403
+
404
+ Generic components will pass all props received down to the wrapped component. Ensure that the `className` prop is passed down to the underlying DOM node when using custom components.
405
+
406
+ ```tsx
407
+ type CustomHeadingProps = {
408
+ children?: React.ReactNode;
409
+ className?: string;
410
+ };
411
+
412
+ const CustomHeading = ({
413
+ children,
414
+ className
415
+ }) => <h2 className={className}>{ children }</h2>;
416
+
417
+ const Wrapped = GenericSize(CustomHeading);
418
+
419
+ const Composition = () => (
420
+ <Wrapped size={48}>
421
+ CustomHeading Component wrapped
422
+ </Wrapped>
423
+ );
424
+ ```
425
+
426
+ To avoid nesting when trying to use multiple generic components, simply call the `extend` method.
427
+
428
+ ```typescript
429
+ const GenericContentParadigm = styled.generic({
430
+ margin: '0 0 1.5rem',
431
+ [style.lastChild]: {
432
+ marginBottom: 0
433
+ }
434
+ });
435
+
436
+ const GenericHugeText = styled.generic({
437
+ fontSize: '4.5rem',
438
+ lineHeight: '5rem'
439
+ });
440
+
441
+ const PageHeading = styled.h1({
442
+ color: '#333',
443
+ fontWeight: 400,
444
+ padding: 0
445
+ }).extend(
446
+ GenericContentParadigm,
447
+ GenericHugeText
448
+ );
449
+ ```
450
+
451
+ You can also extend from other Styled Components and even plain object literal styles. The `extend` method performs a deep merge, so nested styles containing duplicate keys will be combined.
452
+
453
+ ```typescript
454
+ const GenericClearFloat = styled.generic({
455
+ float: 'left',
456
+ [style.after]: {
457
+ content: '',
458
+ clear: 'both',
459
+ display: 'block'
460
+ }
461
+ });
462
+
463
+ const Danger = styled.span({
464
+ color: 'red',
465
+ [style.after]: {
466
+ content: '!'
467
+ }
468
+ });
469
+
470
+ const ExtendMayhem = styled.div({
471
+ color: 'blue',
472
+ [style.hover]: {
473
+ textDecoration: 'underline',
474
+ [style.after]: {
475
+ color: 'pink',
476
+ cursor: 'pointer'
477
+ }
478
+ }
479
+ }).extend(
480
+ Danger,
481
+ GenericClearFloat,
482
+ {
483
+ [style.hover]: {
484
+ textDecoration: 'none',
485
+ [style.after]: {
486
+ cursor: 'default'
487
+ }
488
+ }
489
+ }
490
+ );
491
+ ```
492
+
493
+ The resulting object styles for `<ExtendMayhem>` will be
494
+
495
+ ```typescript
496
+ {
497
+ float: 'left',
498
+ color: 'red',
499
+ [style.after]: {
500
+ content: '',
501
+ clear: 'both',
502
+ display: 'block'
503
+ },
504
+ [style.hover]: {
505
+ textDecoration: 'none',
506
+ [style.after]: {
507
+ color: 'pink',
508
+ cursor: 'default'
509
+ }
510
+ }
511
+ }
512
+ ```
513
+
514
+ ## More docs
515
+
516
+ * [Advanced](docs/advanced.md) - server-side rendering, using refs, experimental features etc.
517
+ * [API](docs/api.md) - style helper, media query builder, global styles etc.
518
+ * [SolidJS](docs/solidjs.md) - as the name suggest, things to know for SolidJS users
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@n3e/styled",
3
+ "version": "1.0.0",
4
+ "description": "CSS-in-JS library with server-side rendering support that can be used with react or solidJS",
5
+ "module": "react/esm/index.js",
6
+ "main": "react/cjs/index.js",
7
+ "types": "react/esm/index.d.ts",
8
+ "files": [
9
+ "react",
10
+ "solid"
11
+ ],
12
+ "scripts": {
13
+ "build": "NODE_ENV=production run-s clean test dev",
14
+ "clean": "rimraf react && rimraf solid",
15
+ "dev": "run-s --silent clean make-bundle",
16
+ "make-bundle": "rollup --config --exports named",
17
+ "test": "NODE_ENV=test jest"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/nicholasmakhija/styled-js.git"
22
+ },
23
+ "keywords": [
24
+ "css",
25
+ "css-in-js",
26
+ "javascript",
27
+ "js",
28
+ "react",
29
+ "reactive",
30
+ "sheet",
31
+ "solid",
32
+ "solid-js",
33
+ "styled",
34
+ "style",
35
+ "sheet",
36
+ "styles",
37
+ "styling",
38
+ "component"
39
+ ],
40
+ "author": "Nicholas Makhija",
41
+ "license": "MIT",
42
+ "bugs": {
43
+ "url": "https://github.com/nicholasmakhija/styled-js/issues"
44
+ },
45
+ "homepage": "https://github.com/nicholasmakhija/styled-js#readme",
46
+ "devDependencies": {
47
+ "@babel/preset-env": "^7.22.4",
48
+ "@rollup/plugin-commonjs": "^24.0.1",
49
+ "@rollup/plugin-node-resolve": "^15.0.2",
50
+ "@rollup/plugin-replace": "^5.0.2",
51
+ "@rollup/plugin-terser": "^0.4.0",
52
+ "@types/jest": "^29.5.12",
53
+ "@types/node": "^18.15.11",
54
+ "@types/prop-types": "^15.7.5",
55
+ "@types/react": "^18.0.33",
56
+ "@types/react-dom": "^18.0.11",
57
+ "jest": "^29.5.0",
58
+ "jest-environment-jsdom": "^29.6.1",
59
+ "npm-run-all": "^4.1.5",
60
+ "prop-types": "^15.7.2",
61
+ "react-dom": "^17.0.2",
62
+ "rimraf": "^4.4.1",
63
+ "rollup": "^3.20.2",
64
+ "rollup-plugin-copy": "^3.5.0",
65
+ "rollup-plugin-gzip": "^3.1.0",
66
+ "yarn": "^1.22.19"
67
+ },
68
+ "dependencies": {
69
+ "csstype": "^3.1.2",
70
+ "react": "^17.0.2",
71
+ "solid-js": "^1.8.15"
72
+ }
73
+ }