Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

csz

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

csz - npm Package Compare versions

Comparing version 0.1.3 to 1.0.0

stylis.js

122

index.js

@@ -1,102 +0,46 @@

// Keep track of which styles have been added
// (keyed by hash of a rule set or file path)
import stylis from './stylis.js'
const cache = {}
const hash = () =>
Math.random()
.toString(36)
.replace('0.', '')
// The global stylesheet that rules get added to
const sheet = document.createElement('style')
document.head.appendChild(sheet)
export default (strings, ...values) => {
const none = hash => `.${hash}{display:none}`
const hide = hash => (sheet.innerHTML = none(hash) + sheet.innerHTML)
const show = hash => (sheet.innerHTML = sheet.innerHTML.replace(none(hash), ''))
// ---------------------------------------
// A file path was provided
// ---------------------------------------
if (strings[0].startsWith('/')) {
// Use the file name as the uid
const className = 'csz-' + hash(strings[0])
if(!cache[className]) {
fetch(strings[0]).then(res => res.text()).then(str => {
cache[className] = true
// Prefix every rule in the file with the uid and append style
sheet.innerHTML += str.replace(/(^[^@\s}]*\s*{)/gm, `\n.${className} $1`)
})
}
return className
}
// ---------------------------------------
// A rule set was supplied
// ---------------------------------------
// Zip constant string parts with any interpolated dynamic values
const str = strings.reduce((acc, string, i) => acc += string + (values[i] == null ? '' : values[i]), '')
// Use a hash of the ruleset as the uid
const className = 'csz-' + hash(str)
if(!cache[className]) {
cache[className] = true
// Prefix the rule set with the uid and append style
sheet.innerHTML += `\n.${className} { ${str} }`
}
return className
const process = key => hash => rules => {
if (key.startsWith('/')) show(hash)
sheet.innerHTML += (cache[key] = {
hash,
rules: stylis()(`.${hash}`, rules),
}).rules
}
// ---------------------------------------
// Hashing functions
// ---------------------------------------
export default (strings, ...values) => {
const key = strings[0].startsWith('/')
? strings[0]
: strings.reduce(
(acc, string, i) =>
(acc += string + (values[i] == null ? '' : values[i])),
''
)
function pad (hash, len) {
while (hash.length < len) {
hash = '0' + hash;
}
return hash;
}
if (cache[key]) return cache[key].hash
function fold (hash, text) {
var i;
var chr;
var len;
if (text.length === 0) {
return hash;
}
for (i = 0, len = text.length; i < len; i++) {
chr = text.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash < 0 ? hash * -2 : hash;
}
const className = 'csz-' + hash()
const append = process(key)(className)
function foldObject (hash, o, seen) {
return Object.keys(o).sort().reduce(foldKey, hash);
function foldKey (hash, key) {
return foldValue(hash, o[key], key, seen);
}
}
if (key.startsWith('/')) {
hide(className)
fetch(key)
.then(res => res.text())
.then(append)
} else append(key)
function foldValue (input, value, key, seen) {
var hash = fold(fold(fold(input, key), toString(value)), typeof value);
if (value === null) {
return fold(hash, 'null');
}
if (value === undefined) {
return fold(hash, 'undefined');
}
if (typeof value === 'object') {
if (seen.indexOf(value) !== -1) {
return fold(hash, '[Circular]' + key);
}
seen.push(value);
return foldObject(hash, value, seen);
}
return fold(hash, value.toString());
return className
}
function toString (o) {
return Object.prototype.toString.call(o);
}
function hash (o) {
return pad(foldValue(0, o, '', []).toString(16), 8);
}
{
"name": "csz",
"version": "0.1.3",
"description": "Super lightweight CSS module like behavior at runtime",
"version": "1.0.0",
"description": "Runtime CSS modules with SASS like preprocessing",
"main": "index.js",

@@ -9,2 +9,3 @@ "keywords": [

"modules",
"preprocessor",
"runtime",

@@ -11,0 +12,0 @@ "tagged template",

# csz
> Super lightweight CSS module like behavior at runtime
A rudimentary, framework agnostic css-in-js solution that takes styles from a tagged template literal and hashes them to create a unique key which is used as a class name when appending the ruleset to a global stylesheet in the document head at runtime - no build step necessary. It also provides means of importing styles dynamically (an experimental feature) so that you can write your rules in `.css` files as per usual.
> Runtime CSS modules with SASS like preprocessing
A framework agnostic css-in-js solution that uses [stylis](https://github.com/thysultan/stylis.js) to parse styles from tagged template literals; appending processed rulesets (scoped under unique class names) to a global stylesheet in the document head. This all happens at runtime - no build step required.
Importing styles dynamically is also supported (currently an experimental feature), so you can write your rules in `.css` files as per usual.
## Features
- Super light weight and dependency free
- Available as an ES module (imported directly from [unpkg.com](https://unpkg.com/csz))
- Import styles from a regular `.css` file
- Efficient caching of styles
- Import styles from regular `.css` files
- Available as an ES module (from [unpkg.com](https://unpkg.com/csz))
- Unique class name generation and namespacing `.csz-lur7p80ssnq`
- Global style injection `:global(selector)`
- Nested selectors `a { &:hover {} }`
- Vendor prefixing `-moz-placeholder`
- Flat stylesheets `color: red; h1 { color: red; }`
- Minification of appended styles
- Keyframe and animation namespacing
## Usage
The package is designed to be used as an ES module. You can import it directly from [unpkg.com](https://unpkg.com):
The package is designed to be used as an ES module. You can import it directly from [unpkg.com](https://unpkg.com/csz/):
```js
import css from 'https://unpkg.com/csz';
import css from 'https://unpkg.com/csz'
const static = css`background: blue` // generate class name from string
const dynamic = css`/index.css` // generate class name from file
const static = css`background: blue;` // generate class name for ruleset
const dynamic = css`/index.css` // generate class name for file contents
```
Both variations (static and dynamic) are sync and return a string of the form `csz-b60d61b8`. When using the static method write _naked_ rule sets – no need to wrap the rules in braces or assign an identifier.
Both variations (static and dynamic) are sync and return a string in a format similar to `csz-b60d61b8`. If a ruleset is provided as a string then it is processed immedietly but if a filepath is provided then processing is deffered until the contents of the file has been fetched.
When importing rules from a file then write your rules as you would do normally – with some kind of identifier followed by a rule set wrapped in curly braces. All the rules in the file will be scoped under the hash of the file name. For example:
> All file paths must start with a `/` and be absolute (relative to the current hostname) so if you are running your app on `example.com` and require `/styles/index.css` then csz will try fetch it from `example.com/styles/index.css`.
```css
h1 { background: red; }
.btn { background: blue; }
```
Styles imported from a file are inevitably going to some amount of time to download. Whilst the stylesheet is being downloaded a temporary ruleset is applied to the element which hides it (using `display: none`) until the fetched files have been processed. This was implemented to prevent flashes of unstyled content.
If your css file looks like the above then it will be appended to the stylesheet like below:
See below for an example of what a raw ruleset might look like and how it looks like after processing.
```css
.csz-b60d61b8 h1 { background: red; }
.csz-b60d61b8 .btn { background: blue; }
```
<details>
<summary>Example stylesheet (unprocessed)</summary>
```scss
font-size: 2em;
Because the supplied file path is used as the key it will only ever be fetched once.
// line comments
/* block comments */
:global(body) {background:red}
h1 {
h2 {
h3 {
content:'nesting'
}
}
}
@media (max-width: 600px) {
& {display:none}
}
&:before {
animation: slide 3s ease infinite
}
@keyframes slide {
from { opacity: 0}
to { opacity: 1}
}
& {
display: flex
}
&::placeholder {
color:red
}
```
</details>
<details>
<summary>Example stylesheet (processed)</summary>
```scss
.csz-a4B7ccH9 {font-size: 2em;}
body {background:red}
h1 h2 h3 {content: 'nesting'}
@media (max-width: 600px) {
.csz-a4B7ccH9 {display:none}
}
.csz-a4B7ccH9:before {
-webkit-animation: slide-id 3s ease infinite;
animation: slide-id 3s ease infinite;
}
@-webkit-keyframes slide-id {
from { opacity: 0}
to { opacity: 1}
}
@keyframes slide-id {
from { opacity: 0}
to { opacity: 1}
}
.csz-a4B7ccH9 {
display:-webkit-box;
display:-webkit-flex;
display:-ms-flexbox;
display:flex;
}
.csz-a4B7ccH9::-webkit-input-placeholder {color:red;}
.csz-a4B7ccH9::-moz-placeholder {color:red;}
.csz-a4B7ccH9:-ms-input-placeholder {color:red;}
.csz-a4B7ccH9::placeholder {color:red;}
```
</details>
## Example
Here is an example of how you can style a (react) component conditionally based upon some state.
This library is framework agnostic but here is a contrived example of how you can style a React component conditionally based upon some state; demonstrating switching between static and dynamic styles on the fly.
```js
import css from 'https://unpkg.com/csz';
```jsx
import css from 'https://unpkg.com/csz'
export default () => {
const [toggle, setToggle] = React.useState(false)
return html`
<div className=${toggle ? css`/index.css` : css`background: blue`}>
return (
<div
className={toggle
? css`/index.css`
: css`background: blue;`}
>
<h1>Hello World!</h1>
<button onClick=${e => setToggle(!toggle)}>Toggle</button>
<button onClick={e => setToggle(!toggle)}>Toggle</button>
</div>
`
)
}
```
> All file paths must start with a `/` and be absolute (relative to the window location) so if you are running your app on `example.com` and require `/styles/index.css` then csz will try fetch it from `example.com/styles/index.css`.
## Implementation
I was inspired by [emotion](https://github.com/emotion-js/emotion) and [styled-components](https://github.com/styled-components/styled-components) but unfortunately neither of these packages expose an es module compatible build and come with quite a lot of extraneous functionality that isn't required when the scope of the project is restricted to runtime only class name generation with a (very) simple caching mechanism with no cleanup strategy.
There is a lot about this implementation that is not _optimal_. It is a proof of concept more than anything.
I was inspired by [emotion](https://github.com/emotion-js/emotion) and [styled-components](https://github.com/styled-components/styled-components) but unfortunately neither of these packages expose an es module compatible build and come with quite a lot of extraneous functionality that isn't required when the scope of the project is restricted to runtime only class name generation and ruleset isolation.

@@ -7,7 +7,16 @@ import { React, ReactDOM } from 'https://unpkg.com/es-react'

const App = () => {
const [toggle, setToggle] = React.useState(false)
const [toggle, setToggle] = React.useState(true)
return html`
<div className=${toggle ? css`/test/index.css` : css`background: blue`}>
<div
className=${toggle
? css`
background: blue;
& button {
background: hotpink;
}
`
: css`/test/index.css`}
>
<h1>Hello World!</h1>
<button className='btn' onClick=${e => setToggle(!toggle)}>Toggle</button>
<button className="btn" onClick=${e => setToggle(!toggle)}>Toggle</button>
</div>

@@ -14,0 +23,0 @@ `

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc