Repository: giuseppeg/dss
Branch: master
Commit: f3c53eaba534
Files: 117
Total size: 113.5 KB
Directory structure:
gitextract_s4oatymr/
├── .editorconfig
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── acss-stats-tool/
│ ├── LICENSE
│ ├── README.md
│ ├── cli.js
│ ├── index.js
│ └── package.json
├── classnames/
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ └── package.json
├── compiler/
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── bin/
│ │ └── dss
│ ├── index.js
│ ├── package.json
│ ├── processor.js
│ ├── src/
│ │ ├── compile.js
│ │ ├── index.js
│ │ ├── plugins/
│ │ │ ├── nest-pseudo.js
│ │ │ ├── sort-at-rules.js
│ │ │ ├── split-grouped-selectors.js
│ │ │ └── validator.js
│ │ ├── processor.js
│ │ └── vendor/
│ │ └── hash.js
│ └── test/
│ ├── __snapshots__/
│ │ ├── index.test.js.snap
│ │ ├── objectify.test.js.snap
│ │ └── processor.test.js.snap
│ ├── browser/
│ │ ├── index.js
│ │ ├── server.js
│ │ └── utils.js
│ ├── index.test.js
│ ├── objectify.test.js
│ └── processor.test.js
├── examples/
│ ├── cli/
│ │ ├── a.css
│ │ ├── b.css
│ │ ├── d.css
│ │ ├── index.html
│ │ └── package.json
│ ├── webpack3/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── a.css
│ │ │ ├── b.css
│ │ │ ├── d.css
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── webpack.config.js
│ └── webpack4/
│ ├── package.json
│ ├── src/
│ │ ├── a.css
│ │ ├── b.css
│ │ ├── d.css
│ │ ├── index.html
│ │ └── index.js
│ └── webpack.config.js
├── lerna.json
├── next-dss/
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ └── package.json
├── package.json
├── release-website
├── webpack/
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ ├── loader.js
│ └── package.json
└── website/
├── .babelrc
├── LICENSE
├── components/
│ ├── analytics/
│ │ └── index.js
│ ├── body/
│ │ ├── index.css
│ │ └── index.js
│ ├── heading/
│ │ ├── index.css
│ │ └── index.js
│ ├── layout/
│ │ ├── index.css
│ │ └── index.js
│ ├── link/
│ │ ├── index.css
│ │ └── index.js
│ ├── logo/
│ │ ├── index.css
│ │ └── index.js
│ ├── markdown/
│ │ ├── index.css
│ │ └── index.js
│ ├── navigation/
│ │ ├── CurrentPath.js
│ │ ├── index.css
│ │ └── index.js
│ └── playground/
│ ├── index.css
│ └── index.js
├── md/
│ ├── atomic-css.md
│ ├── classnames-helper.md
│ ├── examples.md
│ ├── how-it-works.md
│ ├── index.md
│ ├── sass-preprocessors.md
│ ├── supported-css-features.md
│ ├── usage.md
│ └── webpack.md
├── next.config.js
├── package.json
├── pages/
│ ├── _app.js
│ ├── _document.js
│ ├── atomic-css.js
│ ├── classnames-helper.js
│ ├── examples.js
│ ├── how-it-works.js
│ ├── index.js
│ ├── sass-preprocessors.js
│ ├── supported-css-features.js
│ ├── usage.js
│ └── webpack.js
├── postcss.config.js
├── static/
│ └── playground/
│ └── index.html
└── theme/
├── index.js
├── utils.js
└── variables.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# http://editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
max_line_length = 100
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: giuseppeg
================================================
FILE: .gitignore
================================================
dist
node_modules
coverage
.next
website/out
================================================
FILE: .travis.yml
================================================
language: node_js
sudo: false
node_js:
- "8"
addons:
apt:
packages:
- xvfb
before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.6.0
- export PATH="$HOME/.yarn/bin:$PATH"
install:
- export DISPLAY=':99.0'
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- yarn
================================================
FILE: LICENSE
================================================
Copyright 2018-present Giuseppe Gurgone.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
<img width="32" alt="screen shot 2018-07-08 at 5 45 52 pm" src="https://user-images.githubusercontent.com/711311/42420995-f0441c16-82d6-11e8-984d-2a194d1fe570.png" role="presentation" />
# Deterministic Style Sheets ✨
[](https://travis-ci.org/giuseppeg/dss)
[](https://github.com/sindresorhus/xo)
[](https://github.com/prettier/prettier)
DSS (Deterministic StyleSheets) is a component-oriented CSS authoring system that compiles to high-performance atomic CSS classes-based stylesheets.
DSS works like CSS Modules except that styles resolution is deterministic, CSS is compiled to atomic classes and the final bundle is very small.
Read more about how it works on the [website](https://giuseppeg.github.io/dss/).
[**warning, this is an experimental project and might not be production ready**]
The repo comes with `examples`:
```shell
cd examples/cli
# or cd examples/webpack
npm install
npm start
```
## Features
* ⚡️ Automatic compilation to Atomic CSS classes and high-performance stylesheets
* 🆎 Deterministic styles resolution: styles are always resolved in application order
* 📦 Scoped Styles
* 🌎 Framework and language agnostic
* 🤝 Preprocessors friendly
* 💻 Standalone CLI and support for Webpack 3 and 4 with automatic vendor prefixing
* ✂️ CSS the Best Parts
## Contributing
DSS is developed as a monorepo thanks to lerna and yarn workspaces. Everything you need to know is in this repository.
Since this is a side project and I don't want to burn out, I decided to disable the GitHub issues.
### Bugs
If you find a bug please submit a pull request with a failing test or a fix, and good description for the issue.
### Features request
Please submit a pull request with an RFC where you explain the why and the how you think this feature is useful. I'd be glad to start a conversation from there before moving on to implementation. Also please let me know if you would be up to implement the feature you are suggesting.
### My code is crap
I know, it is a side project and I didn't sweat the details. I am more than happy to discuss about a complete rewrite if the project becomes popular.
## LICENSE
MIT
================================================
FILE: acss-stats-tool/LICENSE
================================================
Copyright 2018-present Giuseppe Gurgone.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: acss-stats-tool/README.md
================================================
# atomic-css-stats
Provides stats on `.css` files size (gzipped and brotli). Also compiles the styles to atomic CSS classes for compariaon.
```
npm i -g atomic-css-stats
```
Accepts a space separated list of paths or URL to css files.
```
acss-stats ./file.css https://example.com/bundle.css [...]
```
example:
```
$ acss-stats https://abs.twimg.com/a/1532484778/css/t1/twitter_core.bundle.css
==============
| https://abs.twimg.com/a/1532484778/css/t1/twitter_core.bundle.css
==============
Original file:
Size: 182.72 KB
Gzipped size: 34.08 KB
Brotli size: 28.95 KB
---
Compiled to atomic CSS classes:
Size: 60.05 KB
Gzipped size: 15.48 KB
Brotli size: 12.83 KB
```
## Contributing
This package is part of the [DSS monorepo](https://github.com/giuseppeg/dss#contributing).
## License
MIT
================================================
FILE: acss-stats-tool/cli.js
================================================
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const { promisify } = require('util')
const fetch = require('isomorphic-fetch')
const getStats = require('./')
function read(resource) {
if (/^https?:\/\//.test(resource)) {
return fetch(resource).then(r => r.text())
}
return promisify(fs.readFile)(path.resolve(resource))
}
const resources = [...process.argv.slice(2)]
;(async function () {
const stats = []
for (const resource of resources) {
if (!resources) {
console.error('Provide a valid path to file or url for ' + resource)
process.exit(1)
}
const content = await read(resource)
const s = await getStats(content)
stats.push({
resource,
stats: s,
})
}
stats.forEach(({ resource, stats }) => {
console.log(
`
==============
| ${resource}
==============
Original file:
Size: ${stats.original.size}
Gzipped size: ${stats.original.gzipSize}
Brotli size: ${stats.original.brotliSize}
---
Compiled to atomic CSS classes:
Size: ${stats.atomic.size}
Gzipped size: ${stats.atomic.gzipSize}
Brotli size: ${stats.atomic.brotliSize}
`)
})
}());
================================================
FILE: acss-stats-tool/index.js
================================================
const filesize = require('filesize')
const gzipSize = require('gzip-size')
const brotliSize = require('brotli-size')
const mrmuh = require('murmurhash')
const hash = str => mrmuh(str, str.length).toString(36)
const postcss = require('postcss')
exports = module.exports = async function getStats(content) {
const originalSize = filesize(content.length)
const atomizedContent = await atomizer(content)
return {
original: {
size: filesize(content.length),
gzipSize: filesize(gzipSize.sync(content)),
brotliSize:filesize(brotliSize.sync(content))
},
atomic: {
size: filesize(atomizedContent.length),
gzipSize: filesize(gzipSize.sync(atomizedContent)),
brotliSize: filesize(brotliSize.sync(atomizedContent)),
}
}
}
async function atomizer(src) {
const processed = {}
let css = ''
function plugin() {
return root => {
root.walkDecls(decl => {
if (processed[decl.prop + decl.value]) return
processed[decl.prop + decl.value] = true
if (decl.prop.startsWith('-') && !decl.prop.startsWith('--') && css.endsWith('}')) {
css += `${css.substring(0, -1)}; ${decl.prop}: ${decl.value} }`
} else {
css += `.dss_${hash(decl.prop)}-${hash(decl.value)} { ${decl.prop}: ${decl.value} }`
}
})
}
}
return await postcss(plugin()).process(src, { from: undefined }).then(() => css)
}
================================================
FILE: acss-stats-tool/package.json
================================================
{
"name": "atomic-css-stats",
"version": "0.1.0-beta.4",
"description": "Provides information about regular and compiled to atomic CSS classes files size (gzipped)",
"main": "index.js",
"bin": {
"acss-stats": "cli.js"
},
"keywords": [
"dss",
"atomic css",
"css in js",
"css",
"classes",
"css modules",
"sass",
"postcss",
"classnames"
],
"author": "Giuseppe Gurgone",
"license": "MIT",
"dependencies": {
"brotli-size": "^0.0.2",
"filesize": "^3.6.1",
"gzip-size": "^5.0.0",
"isomorphic-fetch": "^2.2.1",
"murmurhash": "^0.0.2",
"postcss": "^7.0.1"
}
}
================================================
FILE: classnames/LICENSE
================================================
Copyright 2018-present Giuseppe Gurgone.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: classnames/README.md
================================================
# dss-classnames
Deterministic Style Sheets - classNames helper. Read [more about this package](https://dss-lang.com/usage/#dss-classnames).
## Contributing
This package is part of the [DSS monorepo](https://github.com/giuseppeg/dss#contributing).
## License
MIT
================================================
FILE: classnames/index.js
================================================
/* eslint-disable no-var, prefer-arrow-callback */
;(function(root) {
if (typeof module === 'object' && module.exports) {
module.exports = classnames
} else {
root.classnames = classnames
}
function setVal(processed, out, propValue) {
var prop
if (propValue.substr(0, 4) !== 'dss_') {
return propValue + ' ' + out
}
prop = propValue.substr(0, propValue.indexOf('-'))
if (!processed[prop]) {
processed[prop] = true
return out + ' ' + propValue
}
return out
}
function classnames() {
var groups = arguments
var processed = {}
var out = ''
for (var i = groups.length - 1; i >= 0; i--) {
var group = groups[i]
if (!group) {
continue
}
if (typeof group === 'string') {
out = setVal(processed, out, group)
continue
}
group.forEach(function(item) {
out = setVal(processed, out, item)
})
}
return out
}
})(typeof self === 'undefined' ? this : self)
================================================
FILE: classnames/package.json
================================================
{
"name": "dss-classnames",
"version": "0.1.0-beta.0",
"description": "Deterministic Style Sheets - classNames helper",
"main": "index.js",
"keywords": [
"dss",
"atomic css",
"css in js",
"css",
"classes",
"css modules",
"sass",
"postcss",
"classnames"
],
"author": "Giuseppe Gurgone",
"license": "MIT"
}
================================================
FILE: compiler/.gitignore
================================================
dist
node_modules
coverage
================================================
FILE: compiler/LICENSE
================================================
Copyright 2018-present Giuseppe Gurgone.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: compiler/README.md
================================================
# dss-compiler
Deterministic Style Sheets - compiler. Read [more about this package](https://dss-lang.com/usage/#dss-compiler).
## Contributing
This package is part of the [DSS monorepo](https://github.com/giuseppeg/dss#contributing).
## License
MIT
================================================
FILE: compiler/bin/dss
================================================
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const getopts = require("getopts")
const glob = require('glob')
const dss = require('..')
const optimizer = require('../processor').optimizer
const read = filePath => fs.readFileSync(filePath, 'utf-8')
const args = getopts(process.argv.slice(2), {
alias: {
t: 'outType',
n: 'bundleName',
h: 'help'
},
default: {
outType: 'json',
bundleName: 'index.css',
help: false,
}
})
if (args._.length != 2 && !args.help) {
console.error('You must specifiy a glob pattern to find .css files and a target directory\n')
args.help = true
}
if (args.help) {
const programName = process.argv[1].split('/').slice(-1).toString()
console.log([
'Usage:',
programName + ' <src-glob> <dest-path> [--bundleName<filename.css>] [--outType<json|js>]',
'Example:',
programName + ' ./src/**/*.css ./build --bundleName bundle.css --outType js',
''
].join('\n\n'))
process.exit(args._.length != 2 ? 1 : 0)
}
const [globPattern, dist] = args._
const jsSheets = glob.sync(globPattern)
if (jsSheets.length === 0) {
console.error('DSS: the glob ' + globPattern + ' did not match any files.')
process.exit(1)
}
const compilePromises = jsSheets.map(filePath => {
const css = read(filePath)
return dss.singleton(css, { filePath, readableClass: process.env.NODE_ENV !== 'production' })
})
Promise.all(compilePromises).then(results => {
let flush
results.forEach((result, i) => {
const stringified = JSON.stringify(result.locals, null, 2)
const out = args.outType === 'js' ? `exports = module.exports = ${stringified}` : stringified
fs.writeFileSync(
path.resolve(path.join(dist, path.basename(jsSheets[i])+'.'+args.outType)),
out
)
flush = result.flush
})
const css = flush()
const destPath = path.resolve(path.join(dist, args.bundleName))
optimizer(css, { from: 'dss files', to: destPath })
.then(result => {
fs.writeFileSync(destPath, result.css)
})
.catch(e => { throw e })
}).catch(e => {
console.error(e.reason ? `Error in: ${e.file}\n\n${e.reason}` : e)
process.exit(1)
})
================================================
FILE: compiler/index.js
================================================
module.exports = require('./src')
================================================
FILE: compiler/package.json
================================================
{
"name": "dss-compiler",
"version": "0.1.0-beta.0",
"description": "Deterministic Style Sheets - compiler",
"main": "index.js",
"bin": {
"dss": "bin/dss"
},
"files": [
"bin",
"src",
"index.js",
"processor.js",
"LICENSE",
"README.md"
],
"keywords": [
"dss",
"atomic css",
"css in js",
"css",
"classes",
"css modules",
"sass",
"postcss",
"classnames"
],
"author": "Giuseppe Gurgone",
"license": "MIT",
"scripts": {
"test": "jest --coverage && npm run test:browser",
"test:browser": "run-p --race test:browser:server test:browser:run",
"test:browser:run": "browserify test/browser/index.js | tape-run",
"test:browser:server": "node test/browser/server.js",
"jest": "jest"
},
"dependencies": {
"autoprefixer": "8.6.4",
"getopts": "2.0.6",
"glob": "7.1.2",
"just-flatten-it": "2.0.0",
"postcss": "6.0.17",
"postcss-discard-duplicates": "2.1.0",
"postcss-js": "1.0.1",
"postcss-nest-atrules": "0.1.3"
},
"devDependencies": {
"browserify": "^16.1.1",
"dss-classnames": "0.1.0-beta.0",
"jest": "^22.4.3",
"npm-run-all": "^4.1.2",
"tape": "^4.9.0",
"tape-css": "^1.0.2-beta",
"tape-run": "^4.0.0"
},
"jest": {
"collectCoverageFrom": [
"src/**/*.{js}",
"!src/vendor/*"
]
}
}
================================================
FILE: compiler/processor.js
================================================
module.exports = require('./src/processor')
================================================
FILE: compiler/src/compile.js
================================================
const flatten = require('just-flatten-it')
const hash = require('./vendor/hash')
/* Fork of cxs with fundamendal changes */
const DEFAULT_SHEET_ID = '__defaultSheetId'
const cache = {}
const rules = {}
const insertedRules = {}
const insert = (sheetId, rule) => {
rules[sheetId].push(rule)
}
const hyph = s => s.replace(/[A-Z]|^ms/g, '-$&').toLowerCase()
const mx = (rule, media) => (media ? `${media}{${rule}}` : rule)
const propVal = (prop, val) =>
(Array.isArray(val) ? val : [val]).map(val => `${hyph(prop)}:${val}`).join(';')
const rx = (cn, prop, val) => `${cn.startsWith(':') ? '' : '.'}${cn}{${propVal(prop, val)}}`
const rxArr = (cn, prop, vals) => `.${cn}{${propVal(prop, vals)}}`
const noAnd = s => s.replace(/&/g, '')
const combinatorOrPure = (className, child) => {
if (child.endsWith('&')) {
return `${noAnd(child)}.${className}`
}
return className + noAnd(child)
}
const className = (sheetId, key, val, child, media) => {
const _key = key + val + child + media
const cached = cache[_key]
if (cached) {
const [className, rule] = cached
if (!insertedRules[sheetId][className]) {
insert(sheetId, rule)
insertedRules[sheetId][className] = true
}
return className
}
const className = `dss_${hash(key + media + child.replace(/[^\w]+$/i, ''))}-${hash(
val.toString()
)}`
const rxFn = Array.isArray(val) ? rxArr : rx
const rule = mx(rxFn(combinatorOrPure(className, child), key, val), media)
cache[_key] = [className, rule]
insert(sheetId, rule)
insertedRules[sheetId][className] = true
return className
}
const parse = (sheetId, obj, child = '', media, callback) =>
Object.keys(obj).map(key => {
const val = obj[key]
if (val === null) return ''
if (Object.prototype.toString.call(val) === '[object Object]') {
const m2 = key.charAt(0) === '@' ? key : null
const c2 = m2 ? child : child + key
return parse(sheetId, val, c2, m2 || media, callback)
}
return callback(sheetId, key, val, child, media)
})
module.exports = (styles, opts = {}) => {
const sheetId = opts.sheetId || DEFAULT_SHEET_ID
if (!rules[sheetId]) {
rules[sheetId] = []
insertedRules[sheetId] = {}
}
return Object.keys(styles).reduce((acc, key) => {
// Insert non nested at rules like @keyframes as-is
// since they don't have selectors associated to them.
if (key.charAt(0) === '@') {
;(Array.isArray(styles[key]) ? styles[key] : [styles[key]]).forEach(styles => {
const steps = {}
parse(sheetId, styles, '', null, (sheetId, prop, val, child) => {
const props = propVal(prop, val)
if (steps[child]) {
steps[child].push(props)
} else {
steps[child] = [props]
}
})
const css = Object.keys(steps).reduce((css, step) => {
css += mx(steps[step].join(';'), step)
return css
}, '')
insert(sheetId, mx(css, key))
})
return acc
}
const jsKey = key.replace(/^\./, '')
acc[jsKey] = flatten(parse(sheetId, styles[key], undefined, undefined, className))
if (typeof opts.makeReadableClass === 'function') {
const readableClass = opts.makeReadableClass(jsKey)
acc[jsKey].unshift(readableClass)
}
return acc
}, {})
}
module.exports.css = (sheetId = DEFAULT_SHEET_ID) => (rules[sheetId] || []).sort().join('')
module.exports.reset = (sheetId = DEFAULT_SHEET_ID) => {
if (rules[sheetId]) {
delete rules[sheetId]
delete insertedRules[sheetId]
}
}
================================================
FILE: compiler/src/index.js
================================================
const path = require('path')
const { objectify } = require('postcss-js')
const hash = require('./vendor/hash')
const processor = require('./processor')
const compile = require('./compile')
const defaultOptions = {
filePath: undefined,
readableClass: false,
}
let uuid = 1
const createDss = (singleton = false) => async (css, options = {}) => {
const opts = Object.assign({}, defaultOptions, options)
let makeReadableClass
if (opts.readableClass) {
if (typeof opts.readableClass === 'function') {
makeReadableClass = localName => opts.readableClass(localName, hash(css))
} else {
let prefix
if (typeof opts.filePath === 'string') {
const filename = path.basename(opts.filePath)
const p =
filename
.split('.')
.slice(0, -1)
.join('-') || 'DssSource'
prefix = p.charAt(0).toUpperCase() + p.substr(1)
} else {
prefix = 'DssSource'
}
makeReadableClass = localName => `${prefix}-${localName}-${hash(css)}`
}
}
const sheetId = String(singleton ? 0 : uuid++)
const result = await processor(css, { from: opts.filePath })
const locals = compile(objectify(result.root), {
makeReadableClass,
sheetId,
})
return {
locals,
css: () => compile.css(sheetId),
reset: () => compile.reset(sheetId),
flush: () => {
const css = compile.css(sheetId)
compile.reset(sheetId)
return css
},
}
}
module.exports = createDss()
module.exports.singleton = createDss(true)
================================================
FILE: compiler/src/plugins/nest-pseudo.js
================================================
const postcss = require('postcss')
module.exports = postcss.plugin('postcss-dss-nest-pseudo', () => {
return root => {
root.walkRules(rule => {
let parts = rule.selector.split(/:/)
if (parts.length < 2 || parts[0] === '&' || rule.selector.slice(-1) === '&') {
return
}
let parentSelector = parts[0]
let selector = ':' + parts.slice(1).join(':')
// :hover > .foo
if (parts[0] === '') {
const delimiter = selector.match(/[>~+]/)
if (delimiter) {
parts = selector.split(delimiter[0])
selector = `${parts[0].trim()} ${delimiter[0]} &`
parentSelector = parts[1].trim()
} else {
return
}
}
const clone = rule.clone()
clone.selector = selector
rule.nodes = [clone]
rule.selector = parentSelector
})
}
})
================================================
FILE: compiler/src/plugins/sort-at-rules.js
================================================
const postcss = require('postcss')
// Moves at rules at the bottom of the file.
module.exports = postcss.plugin('postcss-dss-sort-at-rules', () => {
return root => {
const atRules = []
root.walkAtRules(atRule => {
atRules.push(atRule.clone())
atRule.remove()
})
root.nodes = root.nodes.concat(atRules)
}
})
================================================
FILE: compiler/src/plugins/split-grouped-selectors.js
================================================
const postcss = require('postcss')
module.exports = postcss.plugin('postcss-dss-split-grouped-selectors', () => {
return root => {
root.walkRules(rule => {
const selector = rule.selector.split(',').map(s => s.trim())
if (selector.length < 2) {
return
}
rule.selector = selector[0]
for (let i = 1; i < selector.length; i++) {
const clone = rule.clone()
clone.selector = selector[i]
rule.parent.insertAfter(rule, clone)
}
})
}
})
================================================
FILE: compiler/src/plugins/validator.js
================================================
const postcss = require('postcss')
const shortHandProperties = [
'animation', 'background', 'border', 'border-bottom', 'border-left', 'border-radius', 'border-right', 'border-top', 'column-rule', 'columns', 'flex', 'flex-flow', 'font', 'grid', 'grid-area', 'grid-column', 'grid-row', 'grid-template', 'list-style', 'margin', 'offset', 'outline', 'overflow', 'padding', 'place-content', 'place-items', 'place-self', 'text-decoration', 'transition',
]
function error(node, message) {
throw node.error(
`DSS Error
${message}
For a comprehensive list of supported features refer to http://giuseppeg.github.io/dss/supported-css-features/
`
)
}
module.exports = postcss.plugin('postcss-dss-validator', () => {
return root => {
const processed = {}
root.walkRules(rule => {
const { selector, parent } = rule
if (parent && parent.type === 'atrule') {
return
}
const params = parent && parent.params ? parent.params : ''
if (processed[params + selector]) {
error(rule,
`Detected duplicated selector: '${selector}'. Please merge it with the previous one.`
)
}
if (selector.split(',').length > 1) {
error(rule,
`Invalid selector: ${selector}. Selectors cannot be grouped.`
)
}
if (/::?(after|before|first-letter|first-line)/.test(selector)) {
error(rule,
`Detected pseudo-element: '${selector}'. Pseudo-elements are not supported. Please use regular elements.`
)
}
if (/:(matches|has|not|lang|any|current)/.test(selector)) {
error(rule,
`Detected unsupported pseudo-class: '${selector}'.`
)
}
const split = selector.split(/\s*[+>~\s]\s*/g)
switch (split.length) {
case 2:
if (split[0].charAt(0) !== ':' || split[1].charAt(0) !== '.') {
error(rule,
`Invalid selector: ${selector}.`
)
}
break
case 1:
if (split[0].charAt(0) !== '.') {
error(rule,
`Invalid selector: ${selector}. Only class selectors are allowed.`
)
}
break
default:
error(rule,
`Invalid selector: ${selector}.`
)
}
if (/\[/.test(selector)) {
error(rule,
`Invalid selector: ${selector}. Cannot use complex selectors, please use only class selectors.`
)
}
processed[params + selector] = true
})
root.walkDecls(decl => {
if (shortHandProperties.includes(decl.prop)) {
error(decl,
'`' + decl.prop + '`: DSS does\'t support shorthand properties at the moment. This CSS feature will likely be supported in the future. Please expand your shorthand properties for now.' +
`\n Can't remember what is the long form for \`${decl.prop}\`? Ask Google 👉 https://google.com/search?q=${encodeURIComponent(`css ${decl.prop} properties`)}`
)
}
if (decl.important) {
error(decl,
'!important is not allowed'
)
}
})
}
})
================================================
FILE: compiler/src/processor.js
================================================
const postcss = require('postcss')
const nestAtRulesPlugin = require('postcss-nest-atrules')
const nestPseudoPlugin = require('./plugins/nest-pseudo')
const splitGroupedSelectorsPlugin = require('./plugins/split-grouped-selectors')
const validatorPlugin = require('./plugins/validator')
const processor = postcss([
splitGroupedSelectorsPlugin,
validatorPlugin,
nestAtRulesPlugin,
nestPseudoPlugin,
])
module.exports = (css, opts = { from: undefined }) => processor.process(css, opts)
const optimzr = postcss([
/* eslint-disable import/order */
require('postcss-discard-duplicates'),
require('autoprefixer'),
require('./plugins/sort-at-rules'),
/* eslint-enable import/order */
])
module.exports.optimizer = (css, opts = { from: undefined, to: undefined }) =>
optimzr.process(css, opts)
================================================
FILE: compiler/src/vendor/hash.js
================================================
// @flow
// murmurhash2 via https://gist.github.com/raycmorgan/588423
module.exports = function hashString(str) {
return hash(str, str.length).toString(36)
}
function hash(str, seed) {
let m = 0x5bd1e995
let r = 24
let h = seed ^ str.length
let length = str.length
let currentIndex = 0
while (length >= 4) {
let k = UInt32(str, currentIndex)
k = Umul32(k, m)
k ^= k >>> r
k = Umul32(k, m)
h = Umul32(h, m)
h ^= k
currentIndex += 4
length -= 4
}
switch (length) {
case 3:
h ^= UInt16(str, currentIndex)
h ^= str.charCodeAt(currentIndex + 2) << 16
h = Umul32(h, m)
break
case 2:
h ^= UInt16(str, currentIndex)
h = Umul32(h, m)
break
case 1:
h ^= str.charCodeAt(currentIndex)
h = Umul32(h, m)
break
}
h ^= h >>> 13
h = Umul32(h, m)
h ^= h >>> 15
return h >>> 0
}
function UInt32(str, pos) {
return (
str.charCodeAt(pos++) +
(str.charCodeAt(pos++) << 8) +
(str.charCodeAt(pos++) << 16) +
(str.charCodeAt(pos) << 24)
)
}
function UInt16(str, pos) {
return str.charCodeAt(pos++) + (str.charCodeAt(pos++) << 8)
}
function Umul32(n, m) {
n = n | 0
m = m | 0
let nlo = n & 0xffff
let nhi = n >>> 16
let res = (nlo * m + (((nhi * m) & 0xffff) << 16)) | 0
return res
}
================================================
FILE: compiler/test/__snapshots__/index.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`dss compiles 1`] = `
Object {
"a": Array [
"dss_rfc3hq-169mlyl",
"dss_16bwgo5-11z5xnj",
"dss_1u3exn4-nfznl2",
"dss_1u3exn4-1ysx8fe",
"dss_1qphpw1-hlr2nt",
],
}
`;
exports[`dss compiles 2`] = `
"
.a { color: red; }
.a:hover { color: blue; }
@media screen and (min-width: 30px) {
.a { color: hotpink }
}
:hover > .a {
color: orange;
}
:hover + .a {
display: block;
}
⬇⬇⬇⬇
.dss_1u3exn4-nfznl2:hover{color:blue}.dss_rfc3hq-169mlyl{color:red}:hover + .dss_1qphpw1-hlr2nt{display:block}:hover > .dss_1u3exn4-1ysx8fe{color:orange}@media screen and (min-width: 30px){.dss_16bwgo5-11z5xnj{color:hotpink}}"
`;
exports[`dss compiles fallbacks 1`] = `
Object {
"a": Array [
"dss_rfc3hq-1t0ure0",
],
}
`;
exports[`dss compiles fallbacks 2`] = `
"
.a {
color: red;
color: green;
}
⬇⬇⬇⬇
.dss_rfc3hq-1t0ure0{color:red;color:green}"
`;
exports[`dss compiles keyframes 1`] = `
Object {
"a": Array [
"dss_1q9w8i1-4hi1ll",
"dss_1ot5qnt-ykz8s4",
"dss_rwa454-yobcwj",
],
}
`;
exports[`dss compiles keyframes 2`] = `
"
@font-face {
font-family: 'foo';
src: url(http://b.ar)
}
.a {
transition-property: fade;
transition-timing-function: ease-out;
transition-duration: 0.5s;
}
@keyframes some {0% { opacity:0 } 100% { opacity:1}}
@keyframes fade {0% { opacity:0 } 100% { opacity:1}}
@keyframes fade {
0% { opacity:0; margin-left: 0; }
100% { opacity:1; margin-left: 100; }
}
⬇⬇⬇⬇
.dss_1ot5qnt-ykz8s4{transition-timing-function:ease-out}.dss_1q9w8i1-4hi1ll{transition-property:fade}.dss_rwa454-yobcwj{transition-duration:0.5s}@font-face{font-family:'foo';src:url(http://b.ar)}@keyframes fade{0%{opacity:0;margin-left:0}100%{opacity:1;margin-left:100}}@keyframes fade{0%{opacity:0}100%{opacity:1}}@keyframes some{0%{opacity:0}100%{opacity:1}}"
`;
exports[`dss does not have duplicates 1`] = `
Object {
"bar": Array [
"dss_14e3233-hlr2nt",
],
"foo": Array [
"dss_14e3233-hlr2nt",
],
}
`;
exports[`dss does not have duplicates 2`] = `".dss_14e3233-hlr2nt{display:block}"`;
================================================
FILE: compiler/test/__snapshots__/objectify.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`objetify css media and pseudo 1`] = `
Object {
".a": Object {
"@media (min-width: 30px)": Object {
":hover": Object {
"color": "red",
},
},
},
}
`;
exports[`objetify css multiple 1`] = `
Object {
".a": Object {
":hover": Object {
"color": "blue",
},
"@media screen and (min-width: 30px)": Object {
"color": "hotpink",
},
"color": "red",
},
}
`;
exports[`objetify css pseudo class 1`] = `
Object {
".a": Object {
":hover": Object {
"color": "red",
},
},
}
`;
exports[`objetify css simple and pseudo class 1`] = `
Object {
".a": Object {
":hover": Object {
"color": "red",
},
"color": "pink",
},
}
`;
exports[`objetify css simple rule 1`] = `
Object {
".a": Object {
"color": "red",
},
}
`;
exports[`objetify css state-combinator-class 1`] = `
Object {
".a": Object {
":hover > &": Object {
"color": "blue",
},
},
}
`;
================================================
FILE: compiler/test/__snapshots__/processor.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`processor applies the nest-atrules plugin 1`] = `
"
.root {
color: red;
@media (min-width: 10px) {
display: block
}
}
"
`;
exports[`processor applies the nest-pseudo plugin 1`] = `
"
.a {
:hover {
color: red;
}
}
.b {
:hover {
color: hotpink
}
}
.b {
color: hotpink
}
.c {
color: red;
}
.c {
:hover > & {
display: block;
}
}
"
`;
exports[`processor applies the split-grouped-selectors plugin 1`] = `
"
.a {
color: red;
}
.b {
color: red;
}
"
`;
exports[`processor merges rules 1`] = `
"
.root {
:hover {
color: yellow;
}
}
.block {
display: block;
margin-top: 10px;
}
.root {
color: red;
font-family: Verdana;
display: block;
@media (min-width: 600px) {
color: green
}
}
.test {
color: red;
color: pink;
@media (min-width: 600px) {
color: green;
color: yellow
}
}
"
`;
exports[`processor mixed nest-atrules and nest-pseudo 1`] = `
"
.a {
:hover {
@media (min-width: 10px) {
color: red
}
}
}
"
`;
exports[`processor moves at rules at the end of the file 1`] = `
"
div { color: red }
:hover > .foo { color: red }
@media (min-width: 1px) { body { color: red } }
@media (min-width: 10px) { body { color: gree } }
"
`;
================================================
FILE: compiler/test/browser/index.js
================================================
const test = require('tape-css')(require('tape'))
const classNames = require('dss-classnames')
const { compile, makeDom, run } = require('./utils')
run(async () => {
const {classes, styles} = await compile(`
.a { background-color: red }
.a:focus { background-color: yellow }
.b { background-color: green }
.c { background-color: red; display: block }
@media (min-width: 0px) {
.c {
background-color: green;
display: inline-block;
}
.d:focus { background-color: yellow }
}
.d { background-color: orange; font-weight: bold }
.f {
background-color: green;
background-color: invalid;
}
`)
const dom1 = makeDom(`
<div>
<div class="${classNames(classes.b, classes.a)}"></div>
<div class="${classNames(classes.a, classes.b)}"></div>
</div>
`)
test(
'resolves deterministically',
{
dom: dom1,
styles
},
t => {
t.equal(
getComputedStyle(dom1.children[0]).getPropertyValue('background-color'),
'rgb(255, 0, 0)',
'the first child should be rgb(255, 0, 0) i.e. red'
)
t.equal(
getComputedStyle(dom1.children[1]).getPropertyValue('background-color'),
'rgb(0, 128, 0)',
'the second child should be rgb(0, 128, 0) i.e. green'
)
t.end()
}
)
const dom2 = makeDom(`
<input class="${classNames(classes.a)}" />
`)
test(
'works with pseudo selectors',
{
dom: dom2,
styles
},
t => {
t.equal(
getComputedStyle(dom2).getPropertyValue('background-color'),
'rgb(255, 0, 0)',
'initially it should be rgb(255, 0, 0) i.e. red'
)
dom2.focus()
t.equal(
getComputedStyle(dom2).getPropertyValue('background-color'),
'rgb(255, 255, 0)',
'on focus it should be rgb(255, 255, 0) i.e. yellow'
)
t.end()
}
)
const dom3 = makeDom(`
<input class="${classNames(classes.d, classes.c)}" />
`)
test(
'works with media queries',
{
dom: dom3,
styles
},
t => {
const s = getComputedStyle(dom3)
t.equal(
s.getPropertyValue('background-color'),
'rgb(0, 128, 0)',
'initially `background-color` should be rgb(0, 128, 0) i.e. green'
)
t.equal(
s.getPropertyValue('display'),
'inline-block',
'initially `display` should be `inline-block`'
)
t.equal(
s.getPropertyValue('font-weight'),
'bold',
'initially `font-weight` should be `bold`'
)
dom3.focus()
t.equal(
getComputedStyle(dom3).getPropertyValue('background-color'),
'rgb(255, 255, 0)',
'on focus it should be rgb(255, 255, 0) i.e. yellow'
)
t.end()
}
)
const dom4 = makeDom(`
<input class="${classNames(classes.c, classes.d)}" />
`)
test(
'works with merged rules',
{
dom: dom4,
styles
},
t => {
t.equal(
getComputedStyle(dom4).getPropertyValue('background-color'),
'rgb(0, 128, 0)',
'initially `background-color` should be rgb(0, 128, 0) i.e. green'
)
dom4.focus()
t.equal(
getComputedStyle(dom4).getPropertyValue('background-color'),
'rgb(255, 255, 0)',
'on focus it should be rgb(255, 255, 0) i.e. yellow'
)
t.end()
}
)
const dom5 = makeDom(`
<input class="${classNames(classes.a, classes.f)}" />
`)
test(
'works with fallbacks',
{
dom: dom5,
styles
},
t => {
t.equal(
getComputedStyle(dom5).getPropertyValue('background-color'),
'rgb(0, 128, 0)',
'initially `background-color` should be rgb(0, 128, 0) i.e. green'
)
t.end()
}
)
const dom6 = makeDom(`
<input class="${classNames(classes.a, classes.f, 'Test')}" />
`)
test(
'has readable class names',
{
dom: dom6,
styles
},
t => {
const matches = dom6.className.match(/(Test-[a|f]-)/g)
t.equal(
matches ? matches.length : 0,
2,
'should have Test-a-hash and Test-f-hash class names'
)
t.end()
}
)
})
================================================
FILE: compiler/test/browser/server.js
================================================
const http = require('http')
const dss = require('../../').singleton
const port = process.env.PORT || 3000
const requestHandler = (request, response) => {
let css = ''
response.setHeader('Content-Type', 'application/json')
response.setHeader('Access-Control-Allow-Origin', '*')
response.setHeader('Access-Control-Request-Method', '*')
response.setHeader('Access-Control-Allow-Methods', 'POST')
response.setHeader('Access-Control-Allow-Headers', '*')
request.on('data', data => {
css += data
})
request.on('end', async () => {
const result = await dss(css, { readableClass: (localName, hash) => `Test-${localName}-${hash}` })
response.write(JSON.stringify({ classes: result.locals, styles: result.css() }))
response.end()
})
}
const server = http.createServer(requestHandler)
server.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
console.log(`server is listening on ${port}`)
})
================================================
FILE: compiler/test/browser/utils.js
================================================
const tape = require('tape')
module.exports.compile = function (css) {
return fetch('http://localhost:3000', {
body: css,
cache: 'no-cache',
method: 'POST',
redirect: 'follow',
referrer: 'no-referrer'
})
.then(response => response.json())
.catch(err => { throw err })
}
module.exports.makeDom = function (html) {
const fragment = document.createElement('div')
fragment.innerHTML = html
return fragment.children[0]
}
let runCount = 1
module.exports.run = function (fn) {
tape('suite ' + runCount++, t => {
const result = fn()
if (result instanceof Promise) {
result.then(() => {
t.end()
})
} else {
t.end()
}
})
}
================================================
FILE: compiler/test/index.test.js
================================================
const dss = require('../')
describe('dss', () => {
it('compiles', async () => {
const src = `
.a { color: red; }
.a:hover { color: blue; }
@media screen and (min-width: 30px) {
.a { color: hotpink }
}
:hover > .a {
color: orange;
}
:hover + .a {
display: block;
}
`
const { locals, flush } = await dss(src)
expect(locals).toMatchSnapshot()
expect(src + '\n\n⬇⬇⬇⬇\n\n' + flush()).toMatchSnapshot()
})
it('compiles fallbacks', async () => {
const src = `
.a {
color: red;
color: green;
}
`
const { locals, flush } = await dss(src)
expect(locals).toMatchSnapshot()
expect(src + '\n\n⬇⬇⬇⬇\n\n' + flush()).toMatchSnapshot()
})
it('works in async mode', async () => {
const styles1Promise = dss('.foo { color: red }')
const r1 = await dss('.bar { display: block }')
const css2 = r1.flush()
const r2 = await styles1Promise
const css1 = r2.flush()
expect(css2).toBeTruthy()
expect(css1).toBeTruthy()
})
it('does not have duplicates', async () => {
const { locals, css } = await dss('.foo { display: block } .bar { display: block }')
expect(locals).toMatchSnapshot()
expect(css()).toMatchSnapshot()
})
it('works as a singleton', async () => {
function rulesLength(css) {
return (css.match(/\.dss_/g) || []).length
}
const call1 = await dss.singleton('.foo { display: block }')
expect(call1.locals.foo.length).toBe(1)
expect(rulesLength(call1.css())).toBe(1)
const call2 = await dss.singleton('.bar { display: block }')
expect(call2.locals.bar.length).toBe(1)
expect(rulesLength(call2.css())).toBe(1)
const call3 = await dss.singleton('.foo { color: red } .baz { color: orange }')
expect(call3.locals.foo.length).toBe(1)
expect(call3.locals.baz.length).toBe(1)
expect(rulesLength(call3.css())).toBe(3)
const call4 = await dss.singleton('.bar { vertical-align: middle }')
const css = call1.flush()
expect(rulesLength(call4.css())).toBe(0)
expect(rulesLength(css)).toBe(4)
})
it('compiles keyframes', async () => {
// @keyframes fade {0% { opacity:0 } 100% { opacity:1}}
const src = `
@font-face {
font-family: 'foo';
src: url(http://b.ar)
}
.a {
transition-property: fade;
transition-timing-function: ease-out;
transition-duration: 0.5s;
}
@keyframes some {0% { opacity:0 } 100% { opacity:1}}
@keyframes fade {0% { opacity:0 } 100% { opacity:1}}
@keyframes fade {
0% { opacity:0; margin-left: 0; }
100% { opacity:1; margin-left: 100; }
}
`
const { locals, flush } = await dss(src)
expect(locals).toMatchSnapshot()
expect(src + '\n\n⬇⬇⬇⬇\n\n' + flush()).toMatchSnapshot()
})
})
================================================
FILE: compiler/test/objectify.test.js
================================================
const { parse } = require('postcss')
const { objectify } = require('postcss-js')
const compile = css => objectify(parse(css))
describe('objetify css', () => {
it('simple rule', () => {
expect(
compile(`
.a { color: red }
`)
).toMatchSnapshot()
})
it('pseudo class', () => {
expect(
compile(`
.a { :hover { color: red } }
`)
).toMatchSnapshot()
})
it('simple and pseudo class', () => {
expect(
compile(`
.a { color: pink; :hover { color: red } }
`)
).toMatchSnapshot()
})
it('media and pseudo', () => {
expect(
compile(`
.a { @media (min-width: 30px) { :hover { color: red } } }
`)
).toMatchSnapshot()
})
it('multiple', () => {
expect(
compile(`
.a { color: red; }
.a { :hover { color: blue; } }
.a {
@media screen and (min-width: 30px) {
color: hotpink
}
}
`)
).toMatchSnapshot()
})
it('state-combinator-class', () => {
expect(
compile(`
.a { :hover > & { color: blue; } }
`)
).toMatchSnapshot()
})
})
================================================
FILE: compiler/test/processor.test.js
================================================
const postcss = require('postcss')
const validatorPlugin = require('../src/plugins/validator')
const sortAtRulesPlugin = require('../src/plugins/sort-at-rules')
const processor = require('../src/processor')
describe('processor', () => {
it('processes css', async () => {
const { css } = await processor(`
.root {
color: red;
}
`)
expect(css).not.toBe('')
})
it('applies the split-grouped-selectors plugin', async () => {
const { css } = await processor(`
.a, .b {
color: red;
}
`)
expect(css).toMatchSnapshot()
})
it('applies the nest-atrules plugin', async () => {
const { css } = await processor(`
.root {
color: red;
}
@media (min-width: 10px) {
.root {
display: block;
}
}
`)
expect(css).toMatchSnapshot()
})
it('applies the nest-pseudo plugin', async () => {
const { css } = await processor(`
.a:hover {
color: red;
}
.b:hover, .b {
color: hotpink
}
.c {
color: red;
}
:hover > .c {
display: block;
}
`)
expect(css).toMatchSnapshot()
})
it('mixed nest-atrules and nest-pseudo', async () => {
const { css } = await processor(`
@media (min-width: 10px) {
.a:hover {
color: red;
}
}
`)
expect(css).toMatchSnapshot()
})
it('merges rules', async () => {
const { css } = await processor(`
.root:hover {
color: yellow;
}
.block {
display: block;
margin-top: 10px;
}
@media (min-width: 600px) {
.root {
color: green;
}
.test {
color: green;
color: yellow;
}
}
.root {
color: red;
font-family: Verdana;
display: block;
}
.test {
color: red;
color: pink;
}
`)
expect(css).toMatchSnapshot()
})
describe('validation', () => {
const selectorsProcessor = css => postcss([validatorPlugin]).process(css, { from: undefined })
describe('throws when it detecs duplicated selectors', async () => {
it('simple', () =>
expect(
processor(`
.a { color: red }
.a { color: blue }
`)
).rejects.toThrow(/Detected duplicated selector/))
it('combined', () =>
expect(
processor(`
.a, .a { color: red }
`)
).rejects.toThrow(/Detected duplicated selector/))
it('except when it is a state declaration', () =>
expect(
processor(`
.a { color: red }
.a:hover { color: blue }
`)
).resolves.toBeTruthy())
})
it('throws when selectors are grouped', () =>
expect(selectorsProcessor(`.a, .b { color: red }`)).rejects.toThrow(
/Selectors cannot be grouped/
))
it('throws when using pseudo-elements', () => {
expect.assertions(8)
return Promise.all(
[
'.a:before',
'.a:after',
'.a:first-line',
'.a:first-letter',
'.a::before',
'.a::after',
'.a::first-line',
'.a:first-letter',
].map(selector =>
expect(selectorsProcessor(`${selector} { color: red }`)).rejects.toThrow(
/Detected pseudo-element/
)
)
)
})
it('throws when using unsupported pseudo-classes', () => {
expect.assertions(6)
return Promise.all(
['.a:matches(b)', '.a:has(b)', '.a:not(b)', '.a:lang(en)', '.a:any(b)', '.a:current'].map(
selector =>
expect(selectorsProcessor(`${selector} { color: red }`)).rejects.toThrow(
/Detected unsupported pseudo-class/
)
)
)
})
it('throws when using complex or non class selectors', async () => {
expect.assertions(8)
await Promise.all(
['div', '[class]', '*'].map(selector =>
expect(selectorsProcessor(`${selector} { color: red }`)).rejects.toThrow(
/Invalid selector/
)
)
)
await Promise.all(
['.a[href]', '.a .b', '.a *', '.a > .b', '.a + .b'].map(selector =>
expect(selectorsProcessor(`${selector} { color: red }`)).rejects.toThrow(
/Invalid selector/
)
)
)
})
it('does not throw when using state-combinator-class selectors', async () => {
expect.assertions(2)
await Promise.all(
[':hover > .foo', ':focus + .foo'].map(selector =>
expect(selectorsProcessor(`${selector} { color: red }`)).resolves.toEqual(
expect.objectContaining({ css: `${selector} { color: red }` })
)
)
)
})
it('does not throw when using keyframes', async () => {
expect.assertions(1)
expect(processor('@keyframes fade {0% { opacity:0 } 100% { opacity:1}}')).resolves.toEqual(
expect.objectContaining({ css: '@keyframes fade {0% { opacity:0 } 100% { opacity:1}}' })
)
})
it('throws an error when using a shorthand property', async () => {
expect(selectorsProcessor(`.a { background: red }`)).rejects.toThrow(
/support shorthand properties/
)
})
it('throws an error when using !important', async () => {
expect(selectorsProcessor(`.a { color: red ! important }`)).rejects.toThrow(
/!important is not allowed/
)
})
})
it('moves at rules at the end of the file', async () => {
const src = `
@media (min-width: 1px) { body { color: red } }
div { color: red }
@media (min-width: 10px) { body { color: gree } }
:hover > .foo { color: red }
`
const { css } = await postcss([sortAtRulesPlugin]).process(src, { from: undefined })
expect(css).toMatchSnapshot()
})
})
================================================
FILE: examples/cli/a.css
================================================
.root:hover {
color: yellow;
}
.block {
display: block;
margin-top: 10px;
}
@media (min-width: 600px) {
.root {
color: green;
}
.test {
color: green;
color: yellow;
}
}
.root {
color: red;
font-family: Verdana;
display: block;
}
.test {
color: red;
color: pink;
}
================================================
FILE: examples/cli/b.css
================================================
.root {
color: blue;
font-family: monospace;
font-size: 2em;
}
@media (max-width: 400px) {
.root {
color: hotpink;
}
}
@media (min-width: 600px) {
.root {
color: orange;
}
}
================================================
FILE: examples/cli/d.css
================================================
@supports (color: yellow) {
.root {
color: yellow;
}
}
================================================
FILE: examples/cli/index.html
================================================
<!doctype html>
<link href="./index.css" rel="stylesheet" />
<script src="./dss-classnames/index.js"></script>
<h1>Deterministic Style Sheets POF</h1>
<main></main>
<script>
Promise.all([
fetch('./a.css.json').then(c => c.json()),
fetch('./b.css.json').then(c => c.json()),
fetch('./d.css.json').then(c => c.json()),
]).then(([a, b, d]) => {
console.log(a, b, d)
const root = document.querySelector('main')
root.innerHTML = `
<p class="${classnames(b.root, a.root)}">hello</p>
<p class="${classnames(a.root, b.root)}">hello</p>
<p class="${classnames()}">hello</p>
`
})
</script>
================================================
FILE: examples/cli/package.json
================================================
{
"name": "dss-example-vanilla",
"version": "0.1.0-beta.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "rm -rf dist && mkdir dist && cp -R ./index.html node_modules/dss-classnames dist && dss '*.css' dist && serve dist"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"dss-classnames": "0.1.0-beta.0"
},
"devDependencies": {
"dss-compiler": "0.1.0-beta.0",
"serve": "^7.0.0"
}
}
================================================
FILE: examples/webpack3/package.json
================================================
{
"name": "dss-example-webpack3",
"version": "0.1.0-beta.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server",
"prod": "NODE_ENV=production webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"dss-classnames": "0.1.0-beta.0"
},
"devDependencies": {
"dss-webpack": "0.1.0-beta.0",
"extract-text-webpack-plugin": "^3.0.2",
"html-webpack-plugin": "^2.0.0",
"webpack": "^3.0.0",
"webpack-dev-server": "^2.0.0",
"last-call-webpack-plugin": "^2.1.2"
}
}
================================================
FILE: examples/webpack3/src/a.css
================================================
.root:hover {
color: yellow;
}
.block {
display: block;
margin-top: 10px;
filter: blur(20px);
border-top-left-radius: 5px;
}
@media (min-width: 600px) {
.root {
color: green;
}
.test {
color: green;
color: yellow;
}
}
.root {
color: red;
font-family: Verdana;
display: block;
}
.test {
color: red;
color: pink;
}
================================================
FILE: examples/webpack3/src/b.css
================================================
.root {
color: blue;
font-family: monospace;
font-size: 2em;
}
@media (max-width: 400px) {
.root {
color: hotpink;
}
}
@media (min-width: 600px) {
.root {
color: orange;
}
}
================================================
FILE: examples/webpack3/src/d.css
================================================
@supports (color: yellow) {
.root {
color: yellow;
}
}
================================================
FILE: examples/webpack3/src/index.html
================================================
<!doctype html>
<link rel="stylesheet" href="./index.css">
<h1>Deterministic Style Sheets POF</h1>
<main></main>
================================================
FILE: examples/webpack3/src/index.js
================================================
const classNames = require('dss-classnames')
const a = require('./a.css')
const b = require('./b.css')
const root = document.querySelector('main')
root.innerHTML = `
<p class="${classNames(b.root, a.root)}">hello</p>
<p class="${classNames(a.root, b.root)}">hello</p>
<p class="${classNames()}">hello</p>
`
================================================
FILE: examples/webpack3/webpack.config.js
================================================
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const DSSWebpackPlugin = require('dss-webpack')
const localIdentName =
process.env.NODE_ENV === 'production' ? 'DSS-[hash:base32]' : '[name]-[local]--[hash:base32:5]'
const config = {
entry: path.resolve('./src/index.js'),
output: {
path: path.resolve('./dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: DSSWebpackPlugin.loader,
query: {
localIdentName,
},
},
],
}),
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('./src/index.html'),
}),
new ExtractTextPlugin('index.css'),
new DSSWebpackPlugin({
test: /index\.css$/,
}),
],
}
module.exports = config
================================================
FILE: examples/webpack4/package.json
================================================
{
"name": "dss-example-webpack4",
"version": "0.1.0-beta.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server",
"prod": "NODE_ENV=production webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"dss-classnames": "0.1.0-beta.0"
},
"devDependencies": {
"dss-webpack": "0.1.0-beta.0",
"html-webpack-plugin": "^3.0.0",
"mini-css-extract-plugin": "^0.4.0",
"webpack": "^4.0.0",
"webpack-cli": "^2.0.15",
"webpack-dev-server": "^3.0.0",
"last-call-webpack-plugin": "^3.0.0"
}
}
================================================
FILE: examples/webpack4/src/a.css
================================================
.root:hover {
color: yellow;
}
.block {
display: block;
margin-top: 10px;
filter: blur(20px);
border-top-left-radius: 5px;
}
@media (min-width: 600px) {
.root {
color: green;
}
.test {
color: green;
color: yellow;
}
}
.root {
color: red;
font-family: Verdana;
display: block;
}
.test {
color: red;
color: pink;
}
================================================
FILE: examples/webpack4/src/b.css
================================================
.root {
color: blue;
font-family: monospace;
font-size: 2em;
}
@media (max-width: 400px) {
.root {
color: hotpink;
}
}
@media (min-width: 600px) {
.root {
color: orange;
}
}
================================================
FILE: examples/webpack4/src/d.css
================================================
@supports (color: yellow) {
.root {
color: yellow;
}
}
================================================
FILE: examples/webpack4/src/index.html
================================================
<!doctype html>
<link rel="stylesheet" href="./index.css">
<h1>Deterministic Style Sheets POF</h1>
<main></main>
================================================
FILE: examples/webpack4/src/index.js
================================================
const classNames = require('dss-classnames')
const a = require('./a.css')
const b = require('./b.css')
const root = document.querySelector('main')
root.innerHTML = `
<p class="${classNames(b.root, a.root)}">hello</p>
<p class="${classNames(a.root, b.root)}">hello</p>
<p class="${classNames()}">hello</p>
`
================================================
FILE: examples/webpack4/webpack.config.js
================================================
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const DSSWebpackPlugin = require('dss-webpack')
const localIdentName =
process.env.NODE_ENV === 'production' ? 'DSS-[hash:base32]' : '[name]-[local]--[hash:base32:5]'
const mode = process.env.NODE_ENV || 'development'
const config = {
mode,
entry: path.resolve('./src/index.js'),
output: {
path: path.resolve('./dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: DSSWebpackPlugin.loader,
query: {
localIdentName,
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('./src/index.html'),
}),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'index.css',
}),
new DSSWebpackPlugin({
test: /index\.css$/,
}),
],
}
module.exports = config
================================================
FILE: lerna.json
================================================
{
"lerna": "2.11.0",
"packages": [
"classnames",
"compiler",
"next-dss",
"webpack"
],
"version": "independent",
"command": {
"init": {
"exact": true
}
},
"npmClient": "yarn",
"useWorkspaces": true
}
================================================
FILE: next-dss/LICENSE
================================================
Copyright 2018-present Giuseppe Gurgone.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: next-dss/README.md
================================================
# next-dss
Deterministic Style Sheets - Next.js plugin. Read [more about this package](https://dss-lang.com/usage/#next-dss).
## Contributing
This package is part of the [DSS monorepo](https://github.com/giuseppeg/dss#contributing).
## License
MIT
================================================
FILE: next-dss/index.js
================================================
const DSSWebpackPlugin = require('dss-webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const cssLoaderConfig = require('@zeit/next-css/css-loader-config')
const commonsChunkConfig = require('@zeit/next-css/commons-chunk-config')
const escapeStringRegexp = require('escape-string-regexp')
module.exports = (nextConfig = {}) => {
return Object.assign({}, nextConfig, {
webpack(config, options) {
if (!options.defaultLoaders) {
throw new Error(
'This plugin is not compatible with Next.js versions below 5.0.0 https://err.sh/next-plugins/upgrade'
)
}
const { dev, isServer } = options
const { dssLoaderOptions } = nextConfig
// Support the user providing their own instance of ExtractTextPlugin.
// If extractCSSPlugin is not defined we pass the same instance of ExtractTextPlugin to all css related modules
// So that they compile to the same file in production
let extractCSSPlugin = nextConfig.extractCSSPlugin || options.extractCSSPlugin
const bundleName = dssLoaderOptions.filename || 'static/style.css'
if (!extractCSSPlugin) {
extractCSSPlugin = new ExtractTextPlugin({
filename: bundleName
})
config.plugins.push(extractCSSPlugin)
options.extractCSSPlugin = extractCSSPlugin
if (!isServer) {
config = commonsChunkConfig(config, /\.css$/)
}
}
options.defaultLoaders.css = cssLoaderConfig(config, extractCSSPlugin, {
cssModules: true,
cssLoaderOptions: {},
dev,
isServer: false
}).map(loader => {
// Replace css-loader with the dss-loader
if (typeof loader.loader !== 'string' || !loader.loader.startsWith('css-loader')) {
return loader
}
return {
loader: DSSWebpackPlugin.loader,
query: {
localIdentName: dssLoaderOptions.localIdentName
}
}
})
config.module.rules.push({
test: /\.css$/,
use: options.defaultLoaders.css
})
config.plugins.push(
new DSSWebpackPlugin({
test: new RegExp(escapeStringRegexp(bundleName))
})
)
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, options)
}
return config
}
})
}
================================================
FILE: next-dss/package.json
================================================
{
"name": "next-dss",
"version": "0.1.0-beta.0",
"main": "index.js",
"keywords": [
"dss",
"atomic css",
"css in js",
"css",
"classes",
"css modules",
"sass",
"postcss",
"classnames",
"react",
"next plugin",
"next.js"
],
"license": "MIT",
"dependencies": {
"dss-webpack": "0.1.0-beta.0",
"@zeit/next-css": "0.2.0",
"escape-string-regexp": "1.0.5",
"extract-text-webpack-plugin": "3.0.2",
"last-call-webpack-plugin": "2.1.2"
}
}
================================================
FILE: package.json
================================================
{
"private": true,
"version": "0.1.0-beta.0",
"description": "Deterministic Style Sheets",
"keywords": [],
"author": "Giuseppe Gurgone",
"license": "MIT",
"workspaces": [
"classnames",
"compiler",
"examples/cli",
"examples/webpack*",
"webpack",
"website"
],
"scripts": {
"test": "xo && cd compiler && npm test",
"lint": "xo",
"format": "prettier --single-quote --trailing-comma=es5 --no-semi --write all {src,test,*}/**/*.js",
"clean": "rm -rf node_modules **/node_modules **/**/node_modules **/dist **/**/dist website/.next website/out"
},
"devDependencies": {
"lerna": "2.11.0",
"prettier": "^1.11.1",
"push-dir": "^0.4.1",
"xo": "^0.20.3"
},
"xo": {
"envs": [
"node",
"browser"
],
"extends": [
"prettier"
],
"ignores": [
"compiler/src/vendor",
"examples",
"website"
],
"rules": {
"capitalized-comments": 0,
"unicorn/import-index": 0
},
"globals": [
"describe",
"it",
"expect"
]
}
}
================================================
FILE: release-website
================================================
#!/usr/bin/env bash
rm -rf out
cd website &&
yarn export &&
touch out/.nojekyll &&
touch out/CNAME &&
echo "dss-lang.com" >> out/CNAME &&
../node_modules/.bin/push-dir --dir=out --branch=gh-pages
================================================
FILE: webpack/LICENSE
================================================
Copyright 2018-present Giuseppe Gurgone.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: webpack/README.md
================================================
# dss-webpack
Deterministic Style Sheets - webpack loader and plugin for webpack 3 and 4. Read [more about this package](https://dss-lang.com/usage/#dss-webpack).
## Contributing
This package is part of the [DSS monorepo](https://github.com/giuseppeg/dss#contributing).
## License
MIT
================================================
FILE: webpack/index.js
================================================
const optimizer = require('dss-compiler/processor').optimizer
let LastCallWebpackPlugin
try {
LastCallWebpackPlugin = require('last-call-webpack-plugin')
} catch (error) {
if (/cannot find module/i.test(error.message)) {
throw new Error(`DSSWebpackPlugin depends on last-call-webpack-plugin.
Are you Webpack 3 user?
Please install last-call-webpack-plugin@^2.0.0 as devDependency.
Are you Webpack 4 user?
Please install last-call-webpack-plugin@^3.0.0 as devDependency.
`)
}
throw error
}
const PHASES = "PHASE" in LastCallWebpackPlugin ? "PHASE" : "PHASES";
function processor(assetName, asset) {
const css = asset.source()
return optimizer(css, { from: assetName, to: assetName }).then(result => result.css)
}
class DSSPlugin extends LastCallWebpackPlugin {
constructor(options = { canPrint: false }) {
super({
assetProcessors: [
{
phase: LastCallWebpackPlugin[PHASES].OPTIMIZE_ASSETS,
regExp: options.test || /\.css$/g,
processor
},
{
phase: LastCallWebpackPlugin[PHASES].OPTIMIZE_CHUNK_ASSETS,
regExp: options.test || /\.css$/g,
processor
}
],
canPrint: options.canPrint
})
}
buildPluginDescriptor() {
return { name: 'DSSWebpackPlugin' }
}
}
DSSPlugin.loader = require.resolve('./loader')
module.exports = DSSPlugin
================================================
FILE: webpack/loader.js
================================================
const loaderUtils = require('loader-utils')
const dss = require('dss-compiler')
const BANNER = '/* DSS file */'
module.exports = function(content) {
if (this.cacheable) this.cacheable()
this.addDependency(this.resourcePath)
const callback = this.async()
const options = loaderUtils.getOptions(this) || {}
let readableClass
if (typeof options.localIdentName === 'string') {
const identName = loaderUtils.interpolateName(this, options.localIdentName, { content })
readableClass = localName => identName.replace(/\[local]/g, localName)
}
dss(content, { readableClass })
.then(({ locals, flush }) => {
const moduleExports = [
BANNER,
`exports = module.exports = [[module.id, "${flush()}", ""]];`,
`exports.locals = ${JSON.stringify(locals)}`
].join('\n')
callback(null, moduleExports)
})
.catch(callback)
}
================================================
FILE: webpack/package.json
================================================
{
"name": "dss-webpack",
"version": "0.1.0-beta.0",
"description": "Deterministic Style Sheets - webpack plugin and loader",
"main": "index.js",
"keywords": [
"dss",
"atomic css",
"css in js",
"css",
"classes",
"css modules",
"sass",
"postcss",
"classnames",
"webpack",
"react"
],
"author": "Giuseppe Gurgone",
"license": "MIT",
"dependencies": {
"dss-compiler": "0.1.0-beta.0",
"loader-utils": "1.1.0"
},
"peerDependencies": {
"last-call-webpack-plugin": "^2.0.0 || ^3.0.0"
}
}
================================================
FILE: website/.babelrc
================================================
{
"presets": ["next/babel"],
"plugins": [["babel-plugin-classnames", { "packageName": "dss-classnames"}]]
}
================================================
FILE: website/LICENSE
================================================
Copyright 2018-present Giuseppe Gurgone.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: website/components/analytics/index.js
================================================
import React from 'react'
import ReactGA from 'react-ga'
export default class Analytics extends React.Component {
track = () => {
ReactGA.set({ page: this.props.route + window.location.hash })
ReactGA.pageview(this.props.route + window.location.hash)
}
componentDidMount() {
ReactGA.initialize(this.props.id)
this.track()
window.addEventListener('hashchange', this.track)
}
componentDidUpdate(prevProps) {
if (prevProps.route !== this.props.route) {
this.track()
}
}
componentWillUnmount() {
window.removeEventListener('hashchange', this.track)
}
render() {
return this.props.children
}
}
================================================
FILE: website/components/body/index.css
================================================
.root {
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
margin-right: 0;
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
display: flex;
flex-direction: column;
}
================================================
FILE: website/components/body/index.js
================================================
import styles from './index.css'
export default ({children}) => <body className={[styles.root]}>{children}</body>
================================================
FILE: website/components/heading/index.css
================================================
.tag {
display: block;
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
margin-right: 0;
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
font-family: $HeadingFontFamily;
font-size: $HeadingFontSize;
font-weight: $HeadingFontWeight;
color: $HeadingColor;
}
.content {
display: block;
margin-top: 2em;
}
.link {
color: inherit;
}
.size1 {
font-size: $u-fontSize1;
margin-top: 0;
}
.size2 {
font-size: $u-fontSize2;
}
.size3 {
font-size: $u-fontSize3;
}
.size4 {
font-size: $u-fontSize4;
}
.size5 {
font-size: $u-fontSize5;
}
.size6 {
font-size: $u-fontSize6;
}
================================================
FILE: website/components/heading/index.js
================================================
import Link from '../link'
import styles from './index.css'
const defaultClassName = {
link: [],
tag: [],
content: [],
}
const Wrap = ({children, className, href, target}) => {
if (!href) {
return children
}
return <Link href={href} className={className} target={target}>{children}</Link>
}
export default ({children, className = defaultClassName, level = 1, size = 1, href = null, target = '_self', autolink = true}) => {
const Tag = `h${level}`
if (!href && autolink && level > 1) {
href = '#' + children.toLowerCase().replace(/[^a-z]/g, '-')
}
return (
<Wrap href={href} target={target} className={className.link}>
<Tag className={[styles.tag, href && styles.link, ...className.tag]}>
<span id={href && href.slice(1)} className={[styles.content, styles[`size${size}`], ...className.content]}>{children}</span>
</Tag>
</Wrap>
)
}
================================================
FILE: website/components/layout/index.css
================================================
.container {
display: flex;
flex-wrap: wrap;
border-top-width: $SideBarSpacing;
border-top-style: solid;
border-top-color: $SideBarBackgroundColor;
min-height: 100vh;
}
.side {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
width: 100%;
box-sizing: border-box;
padding-top: $SideBarSpacing;
padding-bottom: $SideBarSpacing;
padding-left: $SideBarSpacing;
padding-right: $SideBarSpacing;
background-color: $SideBarBackgroundColor;
color: $SideBarColor;
max-width: 100%;
}
.sideOpen {
background-color: $SideBarStateBackgroundColor;
}
.logo {
font-size: $LogoSize;
}
.spacer {
height: $SideBarSpacing;
}
.navigationButton {
margin-top: $SideBarNavigationSpacing;
margin-bottom: $SideBarNavigationSpacing;
margin-left: 0;
margin-right: 0;
}
.playground {
margin-top: $u-spacing1;
}
.main {
flex-basis: 0%;
flex-grow: 1;
flex-shrink: 1;
background-color: $MainBackgroundColor;
max-width: 100%;
}
.mainContent {
background-color: $MainContentBackgroundColor;
color: $MainColor;
max-width: 960px;
min-height: 100%;
padding-top: $SideBarSpacing;
padding-bottom: $SideBarSpacing;
padding-left: $MainSpacing;
padding-right: $MainSpacing;
}
.starOnGithub {
width: 140px;
height: 20px;
}
@media (min-width: 680px) {
.container {
flex-wrap: nowrap;
}
.side {
width: auto;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
}
@media (min-width: 1025px) {
.mainContent {
position: relative;
padding-top: calc($LogoSize + $SideBarSpacing * 2);
padding-bottom: calc($LogoSize + $SideBarSpacing * 2);
padding-left: calc(($LogoSize + $SideBarSpacing * 2) * 0.9);
padding-right: calc(($LogoSize + $SideBarSpacing * 2) * 0.9);
}
.starOnGithub {
position: absolute;
top: $SideBarSpacing;
left: 50%;
}
}
================================================
FILE: website/components/layout/index.js
================================================
import styles from './index.css'
import Link from '../link'
import Head from 'next/head'
import Logo from '../logo'
import Heading from '../heading'
import Navigation from '../navigation'
import Playground from '../playground'
import { LogoColor, LogoBackground } from '../../theme'
export default class Layout extends React.Component {
media = typeof window !== 'undefined' ? window.matchMedia('(min-width: 1150px)') : null
onMedia = ({matches}) => {
if (!this.state.isNavigationOpen && matches) {
this.setState({isNavigationOpen: true})
}
if (this.state.isNavigationOpen && !matches) {
this.setState({isNavigationOpen: false})
}
}
state = {
isNavigationOpen: false
}
toggle = e => {
e.preventDefault()
this.setState(({isNavigationOpen}) => ({ isNavigationOpen: !isNavigationOpen }))
}
componentDidMount() {
this.media = window.matchMedia('(min-width: 1150px)')
this.media.addListener(this.onMedia)
this.onMedia({ matches: this.media.matches })
}
componentWillUnmount() {
this.media.removeListener(this.onMedia)
}
render() {
const { children, title = 'Deterministic StyleSheets' } = this.props
const { isNavigationOpen } = this.state
return (
<React.Fragment>
<Head>
<title>{ `DSS | ${title}` }</title>
<meta charSet='utf-8'/>
<meta name='viewport' content='initial-scale=1.0, width=device-width'/>
</Head>
<div className={[styles.container]}>
<div className={[styles.side, isNavigationOpen && styles.sideOpen]}>
<div className={[styles.logo]}>
<Logo color={LogoColor} backgroundColor={LogoBackground} />
</div>
<div className={[styles.spacer]} />
<div className={[styles.navigation]}>
<Navigation open={isNavigationOpen} onPress={this.toggle} className={{ button: styles.navigationButton }} />
</div>
</div>
<main className={[styles.main]}>
<div className={[styles.mainContent]}>
<iframe
className={[styles.starOnGithub]}
src="https://ghbtns.com/github-btn.html?user=giuseppeg&repo=dss&type=star&count=true"
frameBorder="0"
scrolling="0"
/>
{ children }
<div className={[styles.playground]}><Playground /></div>
</div>
</main>
</div>
</React.Fragment>
)
}
}
================================================
FILE: website/components/link/index.css
================================================
.root {
color: $LinkColor;
font-family: $LinkFontFamily;
font-size: $LinkFontSize;
text-decoration-line: none;
font-weight: bold;
}
.root:hover,
.root:focus {
color: $LinkStateColor;
}
================================================
FILE: website/components/link/index.js
================================================
import _Link from 'next/link'
import styles from './index.css'
const Link = ({children, className = null, target, ...props}) => {
if (target && target === '_blank') {
return <a href={props.href} className={[styles.root, className]} target={target}>{children}</a>
}
return <_Link {...props}><a className={[styles.root, className]}>{children}</a></_Link>
}
export default Link
================================================
FILE: website/components/logo/index.css
================================================
.root {
display: inline-block;
vertical-align: middle;
width: 1em;
height: 1em;
line-height: 1;
color: $LogoColor;
}
================================================
FILE: website/components/logo/index.js
================================================
import styles from './index.css'
export default ({ size = undefined, color = '#fff', backgroundColor = '#000' }) => (
<svg className={[styles.root]} style={{ fontSize: size }} version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 800 800">
<g>
<polygon style={{fill: backgroundColor}} points="400,0.3 45.1,0.3 111,719.2 400,799.7 689,719.2 754.9,0.3 "/>
<path style={{fill: color}} d="M188.9,145.7l6.2,88.9h102.2v0.2h223.2l-20.4,290.4L402.5,555l-97.7-29.9l-12-170.4h-89.3l16.7,237.6
l182.2,55.8l182.2-55.8l31.4-446.7H188.9z"/>
</g>
</svg>
)
================================================
FILE: website/components/markdown/index.css
================================================
.p {
font-family: $MarkdownPFontFamily;
font-size: $MarkdownPFontSize;
line-height: 1.5;
margin-top: $MarkdownPFontSize;
margin-bottom: $MarkdownPFontSize;
}
.p:first-child {
margin-top: 0
}
.p:last-child {
margin-bottom: 0
}
.code {
display: inline-block;
font-family: $MarkdownCodeFontFamily;
font-size: $MarkdownCodeFontSize;
color: $MarkdownCodeColor;
background-color: $MarkdownCodeBackgroundColor;
padding-top: 2px;
padding-bottom: 2px;
padding-left: calc($MarkdownCodeSpacing * 2);
padding-right: calc($MarkdownCodeSpacing * 2);
border-top-left-radius: $MarkdownCodeBorderRadius;
border-top-right-radius: $MarkdownCodeBorderRadius;
border-bottom-right-radius: $MarkdownCodeBorderRadius;
border-bottom-left-radius: $MarkdownCodeBorderRadius;
line-height: 1.4;
margin-top: 1px;
margin-bottom: 1px;
}
.codeBlock {
display: block;
width: 100%;
box-sizing: border-box;
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
border-width: calc($MarkdownCodeSpacing * 4);
border-style: solid;
border-color: $MarkdownCodeBackgroundColor;
overflow-x: auto;
}
.inlineCodeBlock {
white-space: nowrap;
}
.ul {
font-family: $MarkdownULFontFamily;
font-size: $MarkdownULFontSize;
list-style-type: $MarkdownULListStyle;
line-height: 1.5;
}
================================================
FILE: website/components/markdown/index.js
================================================
import Heading from '../heading'
import Link from '../link'
import styles from './index.css'
const headings = [1, 2, 3, 4, 5, 6].reduce((components, level) => {
components[`h${level}`] = props => <Heading {...props} level={level} size={level} />
return components
}, {})
const p = ({children, ...props}) => <p className={[styles.p]}>{children}</p>
const Code = ({className = [], isBlock = true, ...props}) =>
<code {...props} className={[styles.code, isBlock ? styles.codeBlock : styles.inlineCodeBlock, className]} />
const inlineCode = props => <Code {...props} isBlock={false} />
const ul = ({children}) => <ul className={[styles.ul]}>{children}</ul>
export default {
...headings,
a: Link,
p,
inlineCode,
code: Code,
ul,
}
================================================
FILE: website/components/navigation/CurrentPath.js
================================================
import { createContext } from 'react'
export default createContext()
================================================
FILE: website/components/navigation/index.css
================================================
.button {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
position: relative;
background-color: transparent;
background-image: none;
line-height: 0;
color: currentColor;
border-width: 2px;
border-style: solid;
border-color: transparent;
border-top-left-radius: $NavigationButtonBorderRadius;
border-top-right-radius: $NavigationButtonBorderRadius;
border-bottom-right-radius: $NavigationButtonBorderRadius;
border-bottom-left-radius: $NavigationButtonBorderRadius;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 2px;
padding-right: 2px;
outline-style: none;
width: $NavigationButtonSize;
height: $NavigationButtonSize;
user-select: none;
transition-property: border;
transition-duration: 0.2s;
transition-timing-function: ease;
}
.button:active,
.button:focus:active {
border-top-width: 4px;
border-bottom-width: 4px;
}
.button:hover,
.button:focus {
border-top-width: 0;
border-bottom-width: 0;
}
.buttonLine {
display: block;
width: calc($NavigationButtonSize - 4px);
height: 2px;
background-color: currentColor;
}
.menu {
display: none;
padding-top: $u-spacing4;
}
.menuOpen {
display: block;
position: relative;
animation-name: menuFade, menuSlide;
animation-duration: 0.3s, 0.2s;
animation-timing-function: ease-in, ease-in;
}
.section {
display: block;
margin-top: $u-spacing4;
padding-top: 0;
padding-bottom: 0;
padding-left: $u-spacing4;
padding-right: $u-spacing4;
color: currentColor;
border-left-width: 2px;
border-left-style: solid;
border-left-color: transparent;
transition-property: border-color;
transition-timing-function: ease-out;
transition-duration: 0.3s;
outline-style: none;
font-weight: normal;
margin-left: 2px;
}
.section:focus-within,
.section:hover,
.section:focus,
.sectionActive {
color: currentColor;
border-left-color: currentColor;
}
.section:first-child {
margin-top: 0;
}
.subSection {
margin-left: $u-spacing4;
position: relative;
left: 4px;
}
@keyframes menuFade {
0% { opacity: 0 }
100% { opacity: 1 }
}
@keyframes menuSlide {
0% { left: -100% }
80% { left: 30px }
100% { left: 0 }
}
================================================
FILE: website/components/navigation/index.js
================================================
import styles from './index.css'
import Link from '../link'
import CurrentPath from './CurrentPath'
const NavLink = props => <CurrentPath.Consumer>{
currentPath =>
<Link {...props} className={[styles.section, currentPath === props.href && styles.sectionActive]} />
}</CurrentPath.Consumer>
const Navigation = ({open, onPress, className = { button: null }}) => (
<React.Fragment>
<button aria-label="Toggle menu" aria-controls="navigation-menu" aria-expanded={open} onClick={onPress} className={[styles.button, className.button, open && styles.buttonOpen]}>
<span className={[styles.buttonLine]} />
<span className={[styles.buttonLine]} />
<span className={[styles.buttonLine]} />
</button>
<nav aria-label="Main navigation" id="navigation-menu" aria-hidden={!open} className={[styles.menu, open && styles.menuOpen]}>
<NavLink href='/'>About</NavLink>
<NavLink href='/usage'>Usage</NavLink>
<div className={[styles.section, styles.subSection]}>
<NavLink href='/usage#dss-compiler'>dss-compiler</NavLink>
<NavLink href='/usage#dss-classnames'>dss-classnames</NavLink>
<NavLink href='/usage#dss-webpack'>dss-webpack</NavLink>
<NavLink href='/usage#dss-next'>dss-next</NavLink>
</div>
<NavLink href='/#features'>Features</NavLink>
<div className={[styles.section, styles.subSection]}>
<NavLink href='/atomic-css'>Atomic CSS</NavLink>
<NavLink href='/how-it-works'>Determinism</NavLink>
<NavLink href='/classnames-helper'>The classNames helper</NavLink>
<NavLink href='/supported-css-features'>CSS features and rules</NavLink>
<NavLink href='/webpack'>Webpack</NavLink>
<NavLink href='/sass-preprocessors'>SASS and Preprocessors</NavLink>
</div>
<NavLink href='/examples'>Examples</NavLink>
<NavLink href='/static/playground/index.html' target="_blank">Playground</NavLink>
</nav>
</React.Fragment>
)
export default Navigation
================================================
FILE: website/components/playground/index.css
================================================
.container {
padding-top: 1em;
padding-right: 1em;
padding-bottom: 1em;
padding-left: 1em;
border-width: 1px;
border-style: solid;
border-color: $u-colorBlue;
border-top-left-radius: $u-borderRadius5;
border-top-right-radius: $u-borderRadius5;
border-bottom-right-radius: $u-borderRadius5;
border-bottom-left-radius: $u-borderRadius5;
}
.iframeWrapper {
position: relative;
width: 100%;
padding-top: 60%;
display: block;
}
.iframe {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
border-top-width: 0;
border-right-width: 0;
border-bottom-width: 0;
border-left-width: 0;
}
.button {
background-color: $u-colorBlue;
color: $u-colorWhite;
font-family: $u-fontFamilyBase;
font-size: 1em;
border-top-width: 0;
border-right-width: 0;
border-bottom-width: 0;
border-left-width: 0;
border-top-left-radius: $u-borderRadius5;
border-top-right-radius: $u-borderRadius5;
border-bottom-right-radius: $u-borderRadius5;
border-bottom-left-radius: $u-borderRadius5;
padding-top: 0.5em;
padding-bottom: 0.5em;
padding-right: 1em;
padding-left: 1em;
display: block;
width: 100%;
cursor: pointer;
}
.link {
display: block;
margin-bottom: 1em;
}
================================================
FILE: website/components/playground/index.js
================================================
import Link from '../link'
import styles from './index.css'
export default class Playground extends React.PureComponent {
state = {
isShown: false
}
render() {
if (this.state.isShown) {
return (
<div className={[styles.container]}>
<Link className={[styles.link]} href="/static/playground/index.html" target="_blank">
🔗 Open in new full screen window
</Link>
<div className={[styles.iframeWrapper]}>
<iframe src="/static/playground/index.html" className={[styles.iframe]}/>
</div>
</div>
)
}
return (
<button className={[styles.button]} onClick={() => { this.setState({ isShown: true })}}>
Try DSS! Start the Playground 🎮
</button>
)
}
}
================================================
FILE: website/md/atomic-css.md
================================================
# Atomic CSS classes and smaller bundles ⚡️ 📦
The DSS compiler converts every CSS declaration to an atomic CSS classes and returns a JSON object that contains mappings to the source rules.
Since we are using atomic CSS classes, **declarations are deduped** and the final bundle size should be small. With atomic CSS classes the **file size growth is logarithmic** since DSS produces rules only for new declarations. This strategy also makes critical CSS extraction unnecessary.
## How it works
Given some CSS:
```css
/* index.css */
.foo {
display: flex;
flex-direction: column;
color: red;
}
.bar {
display: flex;
color: green;
}
```
the DSS compiler converts everything to atomic CSS classes and returns a `Promise` that resolves with an object that looks like the following:
```
{
locals,
css,
flush,
}
```
### locals
`locals` is an object where each `selector` is mapped to an array of atomic CSS classes:
```JSON
{
"foo": [
"dss_14e3233-fkmc3a",
"dss_1uacqdt-m23pbg",
"dss_rfc3hq-169mlyl"
],
"bar": [
"dss_14e3233-fkmc3a",
"dss_rfc3hq-5rjgso"
]
}
```
Locals should be written to disk as `json` as each CSS file is processed by DSS.
### css
`css` is a function that returns the generated CSS.
```css
/* css() */
.dss_14e3233-fkmc3a{display:flex}
.dss_1uacqdt-m23pbg{flex-direction:column}
.dss_rfc3hq-169mlyl{color:red}
.dss_rfc3hq-5rjgso{color:green}
```
When compiling multiple files `css` should be called at the end, only after all the files have been processed. This is because DSS collects rules as the files are processed.
### flush
`flush` is like `css` except that it resets the internal collection of styles. When calling `css` multiple times you always get the latest styles. Instead if you call `flush` subsequent calls of either `css` or `flush` will return an empty string.
```
css()
// .dss_14e3233-fkmc3a{display:flex}
css()
// .dss_14e3233-fkmc3a{display:flex}
flush()
// .dss_14e3233-fkmc3a{display:flex}
css()
// ''
flush()
// ''
```
### Putting everything together
```
const fs = require('fs')
const dss = require('dss-compiler').singleton
let getCSS
const source1 = fs.readFileSync('./component1/styles.css')
const first = dss(source).then(({ locals }) => {
// locals contains the JSON above
fs.writeFileSync('./component1/styles.css.json', JSON.stringify(locals))
})
const source2 = fs.readFileSync('./component2/styles.css')
const second = dss(source).then(({ locals, css, flush }) => {
fs.writeFileSync('./component2/styles.css.json', JSON.stringify(locals))
getCSS = flush
})
Promise.all([first, second]).then(() => {
fs.writeFileSync('./bundle.css', getCSS())
})
```
When compiling multiple files, the JSON for each file should be written to disk and at the end of the compilation the CSS generated by DSS is available via a dss' `css()` call. The string returned by `css` contains the entire app CSS.
**Note** that DSS comes with a CLI and a Webpack loader/plugin that automates the process above, so that you can chill and just focus on writing styles!
================================================
FILE: website/md/classnames-helper.md
================================================
# The classNames helper 📇
Similarly to CSS Modules, DSS generates mappings of `selector`-`array of atomic classes` and writes this information to a `json` file:
```JSON
{
"foo": [
"dss_rfc3hq-169mlyl"
],
"bar": [
"dss_rfc3hq-5rjgso"
]
}
```
Once we have this information we can write a simple `classNames` helper that accepts a comma separated list of class references (`foo` and `bar` in the example) and **merges them right to left**:
```js
classNames(styles.foo, styles.bar)
// dss_rfc3hq-5rjgso
classNames(styles.bar, styles.foo)
// dss_rfc3hq-169mlyl
```
This is similar to how `Object.assign` works in JavaScript, except that we are merging lists of atomic CSS classes.
By merging these lists of atomic CSS classes right to left we can guarnatee that the final subset of classes applied to an element is predictable regardless of where the styles are defined.
If you use JavaScript you don't need to implement the `classNames` helper since we provide a ready to use package https://www.npmjs.com/package/dss-classnames
## Implementing a classNames helper
You might want to take a look at the JavaScript implementation of classNames:
https://github.com/giuseppeg/dss/tree/master/classnames
================================================
FILE: website/md/examples.md
================================================
# Examples
The most comprehensive example is this website which is styled with DSS. Feel free to take a look at its implementation https://github.com/giuseppeg/dss/tree/master/website
* [CLI example](https://github.com/giuseppeg/dss/tree/master/examples/cli)
* [webpack 3 example](https://github.com/giuseppeg/dss/tree/master/examples/webpack3)
* [webpack 4 example](https://github.com/giuseppeg/dss/tree/master/examples/webpack4)
* [Next.js (React) and PostCSS](https://github.com/giuseppeg/dss/tree/master/website)
================================================
FILE: website/md/how-it-works.md
================================================
# Deterministic styles resolution 🆎
DSS' mission is to provide confidence when authoring CSS. This is done by resolving styles (selectors) in a deterministic way based on the application order of each class name. We think that it is very important to get a predictable result when applying two classes to an element.
Determinism can be achieved thanks to atomic CSS classes. DSS converts declarations to atomic CSS classes. This is done by hashing each property and value and building a class name like the following:
```
dss_<hash(property)>-<hash(value)>
```
For example `color: red` is always hashed to:
```
dss_rfc3hq-169mlyl
```
and `color: green` to:
```
dss_rfc3hq-5rjgso
```
The first part of these class names is the same: `dss_rfc3hq-` and this is information is used to resolve styles.
Given two CSS rules:
```css
.foo {
color: red;
}
.bar {
color: green;
}
```
DSS compiles them to the following class names:
```JSON
{
"foo": [
"dss_rfc3hq-169mlyl"
],
"bar": [
"dss_rfc3hq-5rjgso"
]
}
```
Once we have this information we can write a simple `classNames` helper that accepts a comma separated list of class references (`foo` and `bar` in the example) and merges them right to left:
```js
className(styles.foo, styles.bar)
// dss_rfc3hq-5rjgso
className(styles.bar, styles.foo)
// dss_rfc3hq-169mlyl
```
This is similar to how `Object.assign` works in JavaScript, except that we are merging lists of atomic CSS classes.
================================================
FILE: website/md/index.md
================================================
import Playground from '../components/playground'
# DSS ✨
DSS (_Deterministic StyleSheets_) is a component-oriented CSS authoring system that compiles to high-performance _atomic CSS classes_-based stylesheets.
DSS works like CSS Modules except that supports [a subset of CSS](/supported-css-features) that can be compiled to atomic CSS classes. Thanks to atomic CSS classes styles can be resolved in a deterministic way based on their application order:
```html
<!-- the text will be green -->
<div class="red green">hello</div>
<!-- the text will be red -->
<div class="green red">hello</div>
```
<Playground />
## Features
* ⚡️ Automatic compilation to Atomic CSS classes and high-performance stylesheets
* 🆎 Deterministic styles resolution: styles are always resolved in application order
* 📦 Scoped Styles
* 🌎 Framework and language agnostic
* 🤝 Preprocessors friendly
* 💻 Standalone CLI and support for Webpack 3 and 4 with automatic vendor prefixing
* ✂️ CSS the Best Parts
## How it works
Thanks to the DSS compiler and a simple `classNames` helper, DSS styles are resolved in deterministic way that respects the application order.
DSS is language agnostic. Styles are authored in static `.css` files, compiled down to atomic CSS classes for smaller bundle size and then consumed in any language (Ruby, PHP, Python etc) that implements the super simple `classNames` helper.
Given two class names that set the `color` to `red` and `green`:
```css
.foo {
color: red;
}
.bar {
color: green;
}
```
when applied to an element one class wins over the other depending on the order in which the classes are applied:
```html
<!-- the text will be green -->
<div class="foo bar">hello</div>
<!-- the text will be red -->
<div class="bar foo">hello</div>
```
Such a feature makes it possible to tell with **confidence** which rules apply or overrule others at any given point in time.
Read more about [how it works](/how-it-works).
This website is styled with DSS and its source code is available on [GitHub](https://github.com/giuseppeg/dss/tree/master/website). We also have a handful of [examples](https://github.com/giuseppeg/dss/tree/master/examples).
================================================
FILE: website/md/sass-preprocessors.md
================================================
# SASS and Preprocessors 💪
We 💙 <img alt="sass" src="/static/sass.png" style={{ height: '2em', marginLeft: 5, verticalAlign: '-0.1em' }} /> <img alt="postcss" src="/static/postcss.svg" style={{ height: '2em', marginLeft: 10, verticalAlign: '-0.1em' }} />
Since DSS is just CSS you can use any preprocessor before your code is compiled with the [DSS compiler](/usage#dss-compiler).
To setup SASS or PostCSS with DSS and Webpack please refer to the [webpack page](/webpack#with-sass).
================================================
FILE: website/md/supported-css-features.md
================================================
# CSS features and rules ✂️
**DSS supports a subset of CSS that makes it possible to compile down to atomic CSS classes**.
Generally DSS allows **single class selectors** however there are some exceptions where a higher specificity is actually necessary, this is the case for **states and at-rules which are supported as well**.
Below is a comprehensive list of features:
## Supported
* Class selectors: `.foo`
* CSS states: `:hover`, `:active`, `:focus`, `:visited`, `:focus-within`, `:checked`, `:disabled`, `:required` etc.
* At-rules like `@media`, `@supports`, `@keyframes`, `@font-face`
* State-combinator-selector like `:hover > .foo` or `:focus + .bar`
* `:nth-child`, `:first-child` etc.
## Not supported
* Element, id, universal and attribute selectors
* Descendants selectors `.foo .bar`
* Class-combinator-class selectors `.foo > .bar`
* Not shallow (complex) selectors like `:hover > .foo + .bar`
* Pseudo elements like `:after` and `:before` since regular elements can be used instead
* `:has()`, `:matches()`, `:not()` etc.
* Short hand properties like `background`, `border` need to be written in the long form. In the future we might allow them and unwrap them for you automatically ... thank you for your patience 🙏
* `!important`
================================================
FILE: website/md/usage.md
================================================
# Usage 🔋
To its core DSS is a simple compiler that takes [regular CSS files](/supported-css-features) and generates atomic CSS classes.
## Pre-requisites
The compiler is written in JavaScript and therefore you will need [Node.js](https://nodejs.org) v7.6+ installed on your machine.
Please read the [how it works](/how-it-works) page before you continue.
## dss-compiler
**required** - The DSS compiler.
Add the compiler to your project:
```
npm i dss-compiler
```
The easiest way to use the DSS compiler is via the CLI tool which accepts a [`glob`](https://www.npmjs.com/package/glob) to match your css files to compile, a `dist` folder and an optional bundle filename (by default it would write to `index.css`):
```
dss ./components/*.css ./build --bundleName bundle.css
```
This will generate a `bundle.css` in the `build` folder. You can then include this bundle to your app using a simple `link` tag.
DSS will also write the atomic CSS classnames mappings to JSON files to the same `build` folder. For example when compiling `components/button/styles.css` DSS writes `build/components/button/styles.css.json`. This file contains mappings of selector-array of atomic classes.
Optionally DSS can generate JavaScript modules instead of JSON files. Prefer this option if you are consuming the mappings in a JavaScript application since this allows you to import from `components/button/styles.css` right away.
```
dss ./components/*.css ./build --bundleName bundle.css --outType js
```
### dss-compiler as a library
The compiler can be used as a library in two modes: `singleton` and `multi instance`. The multi instance version is for when you are using asynchronous compilations eg. in a webpack loader.
```js
const fs = require('fs')
const dss = require('dss-compiler')
const src = `
.btn {
color: red
}
`
dss.singleton(src).then({ locals, css, flush } => {
fs.writeFileSync('./component1/styles.css.json', , JSON.stringify(locals))
fs.writeFileSync('./bundle.css', flush())
})
```
For more details see the [atomic-css](/atomic-css) page.
## dss-classnames
This package implements the _classnames helper_ required to consume the DSS styles. Right now it contains only a JavaScript implementation, however we are planning to add implementations in other languages and always welcome user contributions! If you want to implement this helper in another language you can find more details on the [classnames helper page](/classnames-helper).
```
npm i dss-classnames
```
Which you can use similarly to the popular [classnames](https://www.npmjs.com/package/classnames) library:
```js
import classNames from 'dss-classnames'
import styles from './component1/styles.css'
const test =
`<div class="${classNames(styles.btn, styles.anotherClass, 'a-custom-class')}">
hi
</div>`
```
This helper accepts a mix of DSS tokens and regular CSS classnames and makes sure that styles are resolved deterministically. It accepts a list of comma separated classes and you can even have conditions.
```js
classNames(styles.btn, isDisabled && styles.btnDisabled)
```
When using DSS with React you might want to pair this helper with [`babel-plugin-classnames`](https://www.npmjs.com/package/babel-plugin-classnames) which imports `classNames` for you automatically and lets you write this instead:
```js
import styles from './component1/styles.css'
<div className={[styles.btn, styles.anotherClass, 'a-custom-class']}>hi</div>
```
## dss-webpack
DSS comes with a webpack loader and plugin and since it works similarly to CSS Modules can leverage existing tools like `extract-text-webpack-plugin` (webpack 3) and `mini-css-extract-plugin` (webpack 4) to allow you to easily compile your styles.
For more details see the dedicated [webpack page](/webpack).
## dss-next
If you use [Next.js](https://nextjs.org) we prepared a simple plugin for you to seamlessly integrate DSS.
```
npm i dss-next
```
In `next.config.js`
```js
const withDSS = require('dss-next-dss')
const localIdentName =
process.env.NODE_ENV === 'production' ? 'DSS-[hash:base32]' : '[name]-[local]--[hash:base32:5]'
module.exports = withDSS({
dssLoaderOptions: {
localIdentName,
filename: 'static/index.css'
}
})
```
You will then need to add a `link` to `/_next/static/index.css` in `pages/_document.js`
```js
<link rel="stylesheet" href="/_next/static/index.css" />
```
================================================
FILE: website/md/webpack.md
================================================
# webpack 📦
DSS comes with a webpack loader and plugin and since it works similarly to CSS Modules can leverage existing tools like `extract-text-webpack-plugin` (webpack 3) and `mini-css-extract-plugin` (webpack 4) to allow you to easily compile your styles.
```
npm i --save-dev dss-webpack
```
`dss-webpack` exports a plugin which optimizes your final bundle after extraction, and a loader that compiles your styles.
## webpack 3
For webpack 3 you might want to use `extract-text-webpack-plugin`
```js
const path = require('path')
const HtmlwebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const DSSwebpackPlugin = require('dss-webpack')
const localIdentName =
process.env.NODE_ENV === 'production' ? 'DSS-[hash:base32]' : '[name]-[local]--[hash:base32:5]'
const config = {
entry: path.resolve('./src/index.js'),
output: {
path: path.resolve('./dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: [
// This plugin is similar to the css-loader for CSS Modules
{
loader: DSSwebpackPlugin.loader,
query: {
// optional, adds readable classnames
localIdentName,
},
},
],
}),
},
],
},
plugins: [
new HtmlwebpackPlugin({
template: path.resolve('./src/index.html'),
}),
new ExtractTextPlugin('index.css'),
// Important! Optimizes your DSS styles - always include this.
new DSSwebpackPlugin({
test: /index\.css$/,
}),
],
}
module.exports = config
```
## webpack 4
For webpack 4 you might want to use `mini-css-extract-plugin`
```js
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlwebpackPlugin = require('html-webpack-plugin')
const DSSwebpackPlugin = require('dss-webpack')
const localIdentName =
process.env.NODE_ENV === 'production' ? 'DSS-[hash:base32]' : '[name]-[local]--[hash:base32:5]'
const mode = process.env.NODE_ENV || 'development'
const config = {
mode,
entry: path.resolve('./src/index.js'),
output: {
path: path.resolve('./dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
// This plugin is similar to the css-loader for CSS Modules
loader: DSSwebpackPlugin.loader,
query: {
// optional, adds readable classnames
localIdentName,
},
},
],
},
],
},
plugins: [
new HtmlwebpackPlugin({
template: path.resolve('./src/index.html'),
}),
new MiniCssExtractPlugin({
filename: 'index.css',
}),
// Important! Optimizes your DSS styles - always include this.
new DSSwebpackPlugin({
test: /index\.css$/,
}),
],
}
module.exports = config
```
## with SASS
The webpack configuration looks exactly the same as the previous ones except that now we test for `scss` files and run the `sass-loader` before the dss loader.
```js
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
// This plugin is similar to the css-loader for CSS Modules
loader: DSSwebpackPlugin.loader,
query: {
// optional, adds readable classnames
localIdentName,
},
},
// Compile sass before using DSSwebpackPlugin.loader
{
loader: 'sass-loader',
options: {
sourceMaps: false,
},
}
],
},
]
```
## with PostCSS
The webpack configuration looks exactly the same as the previous ones except that now we run the `postcss-loader` before the dss loader.
```js
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
// This plugin is similar to the css-loader for CSS Modules
loader: DSSwebpackPlugin.loader,
query: {
// optional, adds readable classnames
localIdentName,
},
},
// Compile the CSS with PostCSS
{
loader: 'postcss-loader',
options: {
// Configure here the plugins or
// omit this option if you have a `postcss.config.js` file
plugins: () => [
require('postcss-easy-import')(),
require('postcss-simple-vars')({ variables: () => require('./theme') })
// ...
],
},
}
],
},
]
```
================================================
FILE: website/next.config.js
================================================
const withDSS = require('next-dss')
const withMDX = require('@zeit/next-mdx')({
extension: /\.mdx?$/
})
const localIdentName =
process.env.NODE_ENV === 'production' ? 'DSS-[hash:base32]' : '[name]-[local]--[hash:base32:5]'
module.exports = withMDX(
withDSS({
dssLoaderOptions: {
localIdentName,
filename: 'static/index.css'
}
})
)
================================================
FILE: website/package.json
================================================
{
"name": "website",
"version": "0.1.0-beta.0",
"description": "",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start",
"export": "next build && next export"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@mdx-js/mdx": "^0.8.0",
"@zeit/next-mdx": "^1.1.0",
"dss-classnames": "0.1.0-beta.0",
"next": "^6.0.0",
"next-dss": "0.1.0-beta.0",
"postcss-easy-import": "^3.0.0",
"postcss-loader": "^2.1.5",
"postcss-simple-vars": "^4.1.0",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-ga": "^2.5.3",
"system-font-css": "^2.0.2"
},
"devDependencies": {
"babel-plugin-classnames": "^0.1.0"
}
}
================================================
FILE: website/pages/_app.js
================================================
import DefaultApp, { Container } from 'next/app'
import Layout from '../components/layout'
import components from '../components/markdown'
import Analytics from '../components/analytics'
import CurrentPath from '../components/navigation/CurrentPath'
export default class App extends DefaultApp {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
compomentDidMount() {}
render() {
const {
Component,
pageProps,
router: { route },
} = this.props
const meta = Component.meta || {}
return (
<Analytics id="UA-9670480-10" route={route}>
<Container>
<CurrentPath.Provider value={route}>
<Layout title={meta.title}>
<Component {...pageProps} components={components} />
</Layout>
</CurrentPath.Provider>
</Container>
</Analytics>
)
}
}
================================================
FILE: website/pages/_document.js
================================================
import Document, { Head, Main, NextScript } from 'next/document'
import Body from '../components/body'
export default class MyDocument extends Document {
render() {
return (
<html>
<Head>
<meta charset="utf-8" />
<title>Deterministic Style Sheets</title>
<style>{`* { box-sizing: border-box; }`}</style>
<link rel="stylesheet" href="/_next/static/index.css" />
<link rel="icon" href="/static/favicon.ico" type="image/x-icon" />
<meta
name="description"
content="DSS is like CSS Modules but styles are compiled to atomic CSS classes which thanks to a helper resolve deterministically."
/>
<meta name="image" content="/static/dss.png" />
<meta itemprop="name" content="Deterministic Style Sheets" />
<meta
itemprop="description"
content="DSS is like CSS Modules but styles are compiled to atomic CSS classes which thanks to a helper resolve deterministically."
/>
<meta itemprop="image" content="/static/dss.png" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="Deterministic Style Sheets" />
<meta
name="twitter:description"
content="DSS is like CSS Modules but styles are compiled to atomic CSS classes which thanks to a helper resolve deterministically."
/>
<meta name="twitter:site" content="@giuseppegurgone" />
<meta name="twitter:image:src" content="/static/dss.png" />
<meta name="og:title" content="Deterministic Style Sheets" />
<meta
name="og:description"
content="DSS is like CSS Modules but styles are compiled to atomic CSS classes which thanks to a helper resolve deterministically."
/>
<meta name="og:image" content="/static/dss.png" />
<meta name="og:site_name" content="Deterministic Style Sheets" />
<meta name="og:locale" content="en_US" />
<meta name="og:type" content="website" />
</Head>
<Body>
<Main />
<NextScript />
</Body>
</html>
)
}
}
================================================
FILE: website/pages/atomic-css.js
================================================
import Document from '../md/atomic-css.md'
export default Document
================================================
FILE: website/pages/classnames-helper.js
================================================
import Document from '../md/classnames-helper.md'
export default Document
================================================
FILE: website/pages/examples.js
================================================
import Document from '../md/examples.md'
export default Document
================================================
FILE: website/pages/how-it-works.js
================================================
import Document from '../md/how-it-works.md'
export default Document
================================================
FILE: website/pages/index.js
================================================
import Document from '../md/index.md'
export default Document
================================================
FILE: website/pages/sass-preprocessors.js
================================================
import Document from '../md/sass-preprocessors.md'
export default Document
================================================
FILE: website/pages/supported-css-features.js
================================================
import Document from '../md/supported-css-features.md'
export default Document
================================================
FILE: website/pages/usage.js
================================================
import Document from '../md/usage.md'
export default Document
================================================
FILE: website/pages/webpack.js
================================================
import Document from '../md/webpack.md'
export default Document
================================================
FILE: website/postcss.config.js
================================================
module.exports = {
plugins: [
require('postcss-easy-import')(),
require('postcss-simple-vars')({ variables: () => require('./theme') })
],
sourceMap: false
}
================================================
FILE: website/static/playground/index.html
================================================
<!DOCTYPE html>
<meta charSet="utf-8" />
<title>DSS Playground</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; font-family: monospace; }
.Playground {
display: flex;
flex-direction: column;
min-height: 100vh
}
.Playground-editors {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.Playground-editor {
flex: 1;
display: flex;
flex-direction: column;
color: #1e3e83;
background-color: #eff4ff;
padding: 1em;
padding-top: 2.4em;
border-radius: 4px;
margin-top: 1em;
position: relative;
}
.Playground-editor textarea {
all: inherit;
border: 0;
padding: 0;
width: 100%;
flex: 1;
}
.Playground-result:before,
.Playground-editor[data-lang]:before {
content: attr(data-lang);
position: absolute;
padding: 0.2em;
background-color: white;
top: 0;
left: 1em;
}
.Playground-editor[data-lang="js"] textarea {
margin-top: 1em;
}
.Playground-result {
position: relative;
margin-top: 1em;
padding-top: 1.4em
}
.Playground-result:before {
background-color: #eff4ff;
}
.Playground-result iframe {
border: 0;
display: block;
width: 100%;
background-color: white;
}
.Playground-run {
border: 0;
display: block;
width: 100%;
color: white;
background-color: #3467d6;
padding: 0.5em;
text-transform: uppercase;
border-radius: 4px;
}
@media (min-width: 840px) {
.Playground {
flex-direction: row;
}
.Playground-result {
margin-top: 2.8em;
flex: 1;
}
}
</style>
<div class="Playground">
<div class="Playground-editors">
<button class="Playground-run">run</button>
<div class="Playground-editor" data-lang="css">
<textarea>
.red { color: red }
.green { color: green }
</textarea>
</div>
<div class="Playground-editor" data-lang="js">
<div class="FakeImport">import classnames from 'dss-classnames'</div>
<div class="FakeImport">import styles from './styles.css'</div>
<textarea>
// try to invert the order of the classes!
document.body.innerHTML = `
<div class="${classnames(styles.red, styles.green)}">Hello from DSS!</div>
<div class="${classnames(styles.green, styles.red)}">Hello from DSS!</div>
`
</textarea>
</div>
</div>
<div class="Playground-result" data-lang="result">
</div>
</div>
<script>
(function () {
const cssEditor = document.querySelector('.Playground-editor[data-lang="css"] textarea')
const jsEditor = document.querySelector('.Playground-editor[data-lang="js"] textarea')
const result = document.querySelector('.Playground-result')
document.querySelector('.Playground-run').addEventListener('click', run)
function run() {
const resultIframe = document.createElement('iframe')
result.innerHTML = ''
result.appendChild(resultIframe)
getCSS()
.then(dss => {
const resultDocument = resultIframe.contentDocument
resultDocument.open()
resultDocument.write(
getHTML(getJS(), dss)
)
resultDocument.close()
})
}
run()
function getHTML(js, dss) {
console.log(dss)
return `
<!doctype html>
<script src="https://unpkg.com/dss-classnames@0.1.0-beta.0/index.js"><\/script>
<style>${dss.css}</style>
<script>
window.addEventListener('load', function () {
${dss.error
? `document.body.innerHTML = '<div class="error">Error: ${JSON.stringify(dss.error)}</div>'`
: `(new Function(${JSON.stringify(`const styles = ${JSON.stringify(dss.locals)};` + js)})())`
}
})
<\/script>
`
}
function getJS() {
return jsEditor.value
}
function getCSS() {
const src = cssEditor.value.trim()
if (!src) {
return Promise.resolve({
css: '',
locals: {},
error: 'Your CSS is an empty string.'
})
}
return fetch('https://IncompatibleNarrowProgrammers.gsppe.repl.co', {
method: 'post',
body: src,
}).then(r => {
if (r.status !== 200 && r.status !== 304) {
return Promise.reject(
`An error occurred while processing your styles. ${r.status} ${r.statusText}`
)
}
return r.json()
}).catch(e => {
return {
css: '',
locals: {},
error: e.message,
}
})
}
}())
</script>
================================================
FILE: website/theme/index.js
================================================
const { generateVariables } = require('./utils')
const { borderRadius, color, fontFamily, fontSize, spacing } = require('./variables')
const theme = {
Logo: {
Color: color.White,
Background: color.Blue,
Size: fontSize[0],
},
Heading: {
Color: color.Blue,
FontSize: fontSize[3],
FontFamily: fontFamily.Base,
FontWeight: 'normal',
},
Link: {
Color: color.Blue,
StateColor: color.Blue1,
FontSize: fontSize[3],
FontFamily: fontFamily.Base,
},
NavigationButton: {
Color: color.Blue,
StateColor: color.Blue1,
BorderRadius: borderRadius[5],
Size: fontSize[1],
},
SideBar: {
BackgroundColor: color.Gray,
StateBackgroundColor: color.Gray3,
Color: color.Blue1,
Spacing: spacing[1],
// LogoSize - NavigationButtonSize
NavigationSpacing: fontSize[0] - fontSize[1],
},
Main: {
BackgroundColor: color.Gray,
ContentBackgroundColor: color.White,
Color: color.Blue1,
Spacing: spacing[0],
},
MarkdownP: {
FontSize: fontSize[3],
FontFamily: fontFamily.Base,
},
MarkdownCode: {
FontSize: fontSize[3] - borderRadius[4] / 2,
FontFamily: fontFamily.Monospace,
Color: color.Blue1,
BackgroundColor: color.Blue6,
Spacing: borderRadius[4],
BorderRadius: borderRadius[4],
},
MarkdownUL: {
FontSize: fontSize[3],
FontFamily: fontFamily.Base,
ListStyle: 'square',
},
}
exports = module.exports = Object.assign(
generateVariables(theme),
generateVariables({ borderRadius, color, fontFamily, fontSize, spacing }, 'u-')
)
================================================
FILE: website/theme/utils.js
================================================
function fixNumber(thing) {
return typeof thing === 'number' ? thing + 'px' : thing
}
function generateVariables(variables, prefix = '') {
return Object.keys(variables).reduce((vars, prop) => {
const propVal = variables[prop]
if (Array.isArray(propVal)) {
propVal.forEach((val, index) => {
vars[`${prefix}${prop}${index + 1}`] = fixNumber(val)
})
} else {
Object.keys(propVal).forEach(subProp => {
// colorWhite1
vars[`${prefix}${prop}${subProp}`] = fixNumber(propVal[subProp])
})
}
return vars
}, {})
}
exports = module.exports = {
generateVariables,
}
================================================
FILE: website/theme/variables.json
================================================
{
"color": {
"White": "#fff",
"Gray": "#fafafa",
"Gray3": "#f4f4f4",
"Black": "#000",
"Black1": "#111",
"Black2": "#222",
"Blue": "#3467d6",
"Blue1": "#1e3e83",
"Blue2": "#2b57b6",
"Blue6": "#eff4ff"
},
"borderRadius": [20, 16, 12, 6, 4, 2],
"fontFamily": {
"Base": "system-ui, sans-serif",
"Serif": "serif",
"SansSerif": "sans-serif",
"Monospace": "Monaco, monospace"
},
"fontSize": [36, 24, 18, 16, 14, 12],
"spacing": [36, 24, 18, 16, 14, 12]
}
gitextract_s4oatymr/
├── .editorconfig
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── acss-stats-tool/
│ ├── LICENSE
│ ├── README.md
│ ├── cli.js
│ ├── index.js
│ └── package.json
├── classnames/
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ └── package.json
├── compiler/
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── bin/
│ │ └── dss
│ ├── index.js
│ ├── package.json
│ ├── processor.js
│ ├── src/
│ │ ├── compile.js
│ │ ├── index.js
│ │ ├── plugins/
│ │ │ ├── nest-pseudo.js
│ │ │ ├── sort-at-rules.js
│ │ │ ├── split-grouped-selectors.js
│ │ │ └── validator.js
│ │ ├── processor.js
│ │ └── vendor/
│ │ └── hash.js
│ └── test/
│ ├── __snapshots__/
│ │ ├── index.test.js.snap
│ │ ├── objectify.test.js.snap
│ │ └── processor.test.js.snap
│ ├── browser/
│ │ ├── index.js
│ │ ├── server.js
│ │ └── utils.js
│ ├── index.test.js
│ ├── objectify.test.js
│ └── processor.test.js
├── examples/
│ ├── cli/
│ │ ├── a.css
│ │ ├── b.css
│ │ ├── d.css
│ │ ├── index.html
│ │ └── package.json
│ ├── webpack3/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── a.css
│ │ │ ├── b.css
│ │ │ ├── d.css
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── webpack.config.js
│ └── webpack4/
│ ├── package.json
│ ├── src/
│ │ ├── a.css
│ │ ├── b.css
│ │ ├── d.css
│ │ ├── index.html
│ │ └── index.js
│ └── webpack.config.js
├── lerna.json
├── next-dss/
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ └── package.json
├── package.json
├── release-website
├── webpack/
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ ├── loader.js
│ └── package.json
└── website/
├── .babelrc
├── LICENSE
├── components/
│ ├── analytics/
│ │ └── index.js
│ ├── body/
│ │ ├── index.css
│ │ └── index.js
│ ├── heading/
│ │ ├── index.css
│ │ └── index.js
│ ├── layout/
│ │ ├── index.css
│ │ └── index.js
│ ├── link/
│ │ ├── index.css
│ │ └── index.js
│ ├── logo/
│ │ ├── index.css
│ │ └── index.js
│ ├── markdown/
│ │ ├── index.css
│ │ └── index.js
│ ├── navigation/
│ │ ├── CurrentPath.js
│ │ ├── index.css
│ │ └── index.js
│ └── playground/
│ ├── index.css
│ └── index.js
├── md/
│ ├── atomic-css.md
│ ├── classnames-helper.md
│ ├── examples.md
│ ├── how-it-works.md
│ ├── index.md
│ ├── sass-preprocessors.md
│ ├── supported-css-features.md
│ ├── usage.md
│ └── webpack.md
├── next.config.js
├── package.json
├── pages/
│ ├── _app.js
│ ├── _document.js
│ ├── atomic-css.js
│ ├── classnames-helper.js
│ ├── examples.js
│ ├── how-it-works.js
│ ├── index.js
│ ├── sass-preprocessors.js
│ ├── supported-css-features.js
│ ├── usage.js
│ └── webpack.js
├── postcss.config.js
├── static/
│ └── playground/
│ └── index.html
└── theme/
├── index.js
├── utils.js
└── variables.json
SYMBOL INDEX (37 symbols across 16 files)
FILE: acss-stats-tool/cli.js
function read (line 9) | function read(resource) {
FILE: acss-stats-tool/index.js
function atomizer (line 27) | async function atomizer(src) {
FILE: classnames/index.js
function setVal (line 9) | function setVal(processed, out, propValue) {
function classnames (line 22) | function classnames() {
FILE: compiler/src/compile.js
constant DEFAULT_SHEET_ID (line 6) | const DEFAULT_SHEET_ID = '__defaultSheetId'
FILE: compiler/src/plugins/validator.js
function error (line 7) | function error(node, message) {
FILE: compiler/src/vendor/hash.js
function hash (line 8) | function hash(str, seed) {
function UInt32 (line 54) | function UInt32(str, pos) {
function UInt16 (line 63) | function UInt16(str, pos) {
function Umul32 (line 67) | function Umul32(n, m) {
FILE: compiler/test/index.test.js
function rulesLength (line 57) | function rulesLength(css) {
FILE: next-dss/index.js
method webpack (line 9) | webpack(config, options) {
FILE: webpack/index.js
constant PHASES (line 20) | const PHASES = "PHASE" in LastCallWebpackPlugin ? "PHASE" : "PHASES";
function processor (line 22) | function processor(assetName, asset) {
class DSSPlugin (line 27) | class DSSPlugin extends LastCallWebpackPlugin {
method constructor (line 28) | constructor(options = { canPrint: false }) {
method buildPluginDescriptor (line 46) | buildPluginDescriptor() {
FILE: webpack/loader.js
constant BANNER (line 4) | const BANNER = '/* DSS file */'
FILE: website/components/analytics/index.js
class Analytics (line 4) | class Analytics extends React.Component {
method componentDidMount (line 9) | componentDidMount() {
method componentDidUpdate (line 14) | componentDidUpdate(prevProps) {
method componentWillUnmount (line 19) | componentWillUnmount() {
method render (line 22) | render() {
FILE: website/components/layout/index.js
class Layout (line 10) | class Layout extends React.Component {
method componentDidMount (line 31) | componentDidMount() {
method componentWillUnmount (line 37) | componentWillUnmount() {
method render (line 41) | render() {
FILE: website/components/playground/index.js
class Playground (line 4) | class Playground extends React.PureComponent {
method render (line 9) | render() {
FILE: website/pages/_app.js
class App (line 7) | class App extends DefaultApp {
method getInitialProps (line 8) | static async getInitialProps({ Component, router, ctx }) {
method compomentDidMount (line 18) | compomentDidMount() {}
method render (line 20) | render() {
FILE: website/pages/_document.js
class MyDocument (line 4) | class MyDocument extends Document {
method render (line 5) | render() {
FILE: website/theme/utils.js
function fixNumber (line 1) | function fixNumber(thing) {
function generateVariables (line 5) | function generateVariables(variables, prefix = '') {
Condensed preview — 117 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (129K chars).
[
{
"path": ".editorconfig",
"chars": 310,
"preview": "# EditorConfig helps developers define and maintain consistent\n# coding styles between different editors and IDEs\n# http"
},
{
"path": ".github/FUNDING.yml",
"chars": 65,
"preview": "# These are supported funding model platforms\n\ngithub: giuseppeg\n"
},
{
"path": ".gitignore",
"chars": 45,
"preview": "dist\nnode_modules\ncoverage\n.next\nwebsite/out\n"
},
{
"path": ".travis.yml",
"chars": 320,
"preview": "language: node_js\nsudo: false\nnode_js:\n - \"8\"\naddons:\n apt:\n packages:\n - xvfb\nbefore_install:\n - curl -o- -L"
},
{
"path": "LICENSE",
"chars": 1065,
"preview": "Copyright 2018-present Giuseppe Gurgone.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "README.md",
"chars": 2363,
"preview": "<img width=\"32\" alt=\"screen shot 2018-07-08 at 5 45 52 pm\" src=\"https://user-images.githubusercontent.com/711311/4242099"
},
{
"path": "acss-stats-tool/LICENSE",
"chars": 1065,
"preview": "Copyright 2018-present Giuseppe Gurgone.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "acss-stats-tool/README.md",
"chars": 810,
"preview": "# atomic-css-stats\n\nProvides stats on `.css` files size (gzipped and brotli). Also compiles the styles to atomic CSS cla"
},
{
"path": "acss-stats-tool/cli.js",
"chars": 1151,
"preview": "#!/usr/bin/env node\n\nconst fs = require('fs')\nconst path = require('path')\nconst { promisify } = require('util')\nconst f"
},
{
"path": "acss-stats-tool/index.js",
"chars": 1430,
"preview": "const filesize = require('filesize')\nconst gzipSize = require('gzip-size')\nconst brotliSize = require('brotli-size')\ncon"
},
{
"path": "acss-stats-tool/package.json",
"chars": 639,
"preview": "{\n \"name\": \"atomic-css-stats\",\n \"version\": \"0.1.0-beta.4\",\n \"description\": \"Provides information about regular and co"
},
{
"path": "classnames/LICENSE",
"chars": 1065,
"preview": "Copyright 2018-present Giuseppe Gurgone.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "classnames/README.md",
"chars": 268,
"preview": "# dss-classnames\n\nDeterministic Style Sheets - classNames helper. Read [more about this package](https://dss-lang.com/us"
},
{
"path": "classnames/index.js",
"chars": 1011,
"preview": "/* eslint-disable no-var, prefer-arrow-callback */\n;(function(root) {\n if (typeof module === 'object' && module.exports"
},
{
"path": "classnames/package.json",
"chars": 357,
"preview": "{\n \"name\": \"dss-classnames\",\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"Deterministic Style Sheets - classNames hel"
},
{
"path": "compiler/.gitignore",
"chars": 27,
"preview": "dist\nnode_modules\ncoverage\n"
},
{
"path": "compiler/LICENSE",
"chars": 1065,
"preview": "Copyright 2018-present Giuseppe Gurgone.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "compiler/README.md",
"chars": 255,
"preview": "# dss-compiler\n\nDeterministic Style Sheets - compiler. Read [more about this package](https://dss-lang.com/usage/#dss-co"
},
{
"path": "compiler/bin/dss",
"chars": 2162,
"preview": "#!/usr/bin/env node\n\nconst fs = require('fs')\nconst path = require('path')\nconst getopts = require(\"getopts\")\nconst glob"
},
{
"path": "compiler/index.js",
"chars": 34,
"preview": "module.exports = require('./src')\n"
},
{
"path": "compiler/package.json",
"chars": 1370,
"preview": "{\n \"name\": \"dss-compiler\",\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"Deterministic Style Sheets - compiler\",\n \"ma"
},
{
"path": "compiler/processor.js",
"chars": 44,
"preview": "module.exports = require('./src/processor')\n"
},
{
"path": "compiler/src/compile.js",
"chars": 3550,
"preview": "const flatten = require('just-flatten-it')\nconst hash = require('./vendor/hash')\n\n/* Fork of cxs with fundamendal change"
},
{
"path": "compiler/src/index.js",
"chars": 1543,
"preview": "const path = require('path')\nconst { objectify } = require('postcss-js')\nconst hash = require('./vendor/hash')\nconst pro"
},
{
"path": "compiler/src/plugins/nest-pseudo.js",
"chars": 864,
"preview": "const postcss = require('postcss')\n\nmodule.exports = postcss.plugin('postcss-dss-nest-pseudo', () => {\n return root => "
},
{
"path": "compiler/src/plugins/sort-at-rules.js",
"chars": 341,
"preview": "const postcss = require('postcss')\n\n// Moves at rules at the bottom of the file.\n\nmodule.exports = postcss.plugin('postc"
},
{
"path": "compiler/src/plugins/split-grouped-selectors.js",
"chars": 509,
"preview": "const postcss = require('postcss')\n\nmodule.exports = postcss.plugin('postcss-dss-split-grouped-selectors', () => {\n ret"
},
{
"path": "compiler/src/plugins/validator.js",
"chars": 3138,
"preview": "const postcss = require('postcss')\n\nconst shortHandProperties = [\n 'animation', 'background', 'border', 'border-bottom'"
},
{
"path": "compiler/src/processor.js",
"chars": 809,
"preview": "const postcss = require('postcss')\nconst nestAtRulesPlugin = require('postcss-nest-atrules')\nconst nestPseudoPlugin = re"
},
{
"path": "compiler/src/vendor/hash.js",
"chars": 1338,
"preview": "// @flow\n// murmurhash2 via https://gist.github.com/raycmorgan/588423\n\nmodule.exports = function hashString(str) {\n ret"
},
{
"path": "compiler/test/__snapshots__/index.test.js.snap",
"chars": 2295,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`dss compiles 1`] = `\nObject {\n \"a\": Array [\n \"dss_rfc3hq-169mly"
},
{
"path": "compiler/test/__snapshots__/objectify.test.js.snap",
"chars": 1009,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`objetify css media and pseudo 1`] = `\nObject {\n \".a\": Object {\n "
},
{
"path": "compiler/test/__snapshots__/processor.test.js.snap",
"chars": 1675,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`processor applies the nest-atrules plugin 1`] = `\n\"\n .root {\n "
},
{
"path": "compiler/test/browser/index.js",
"chars": 4232,
"preview": "const test = require('tape-css')(require('tape'))\nconst classNames = require('dss-classnames')\nconst { compile, makeDom,"
},
{
"path": "compiler/test/browser/server.js",
"chars": 972,
"preview": "const http = require('http')\nconst dss = require('../../').singleton\n\nconst port = process.env.PORT || 3000\n\nconst reque"
},
{
"path": "compiler/test/browser/utils.js",
"chars": 696,
"preview": "const tape = require('tape')\n\nmodule.exports.compile = function (css) {\n return fetch('http://localhost:3000', {\n bo"
},
{
"path": "compiler/test/index.test.js",
"chars": 2884,
"preview": "const dss = require('../')\n\ndescribe('dss', () => {\n it('compiles', async () => {\n const src = `\n .a { color: r"
},
{
"path": "compiler/test/objectify.test.js",
"chars": 1121,
"preview": "const { parse } = require('postcss')\nconst { objectify } = require('postcss-js')\n\nconst compile = css => objectify(parse"
},
{
"path": "compiler/test/processor.test.js",
"chars": 5918,
"preview": "const postcss = require('postcss')\nconst validatorPlugin = require('../src/plugins/validator')\nconst sortAtRulesPlugin ="
},
{
"path": "examples/cli/a.css",
"chars": 305,
"preview": ".root:hover {\n color: yellow;\n}\n\n.block {\n display: block;\n margin-top: 10px;\n}\n\n@media (min-width: 600px) {\n .root "
},
{
"path": "examples/cli/b.css",
"chars": 198,
"preview": ".root {\n color: blue;\n font-family: monospace;\n font-size: 2em;\n}\n\n@media (max-width: 400px) {\n .root {\n color: h"
},
{
"path": "examples/cli/d.css",
"chars": 63,
"preview": "@supports (color: yellow) {\n .root {\n color: yellow;\n }\n}\n"
},
{
"path": "examples/cli/index.html",
"chars": 635,
"preview": "<!doctype html>\n<link href=\"./index.css\" rel=\"stylesheet\" />\n<script src=\"./dss-classnames/index.js\"></script>\n\n<h1>Dete"
},
{
"path": "examples/cli/package.json",
"chars": 460,
"preview": "{\n \"name\": \"dss-example-vanilla\",\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": "
},
{
"path": "examples/webpack3/package.json",
"chars": 596,
"preview": "{\n \"name\": \"dss-example-webpack3\",\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\":"
},
{
"path": "examples/webpack3/src/a.css",
"chars": 358,
"preview": ".root:hover {\n color: yellow;\n}\n\n.block {\n display: block;\n margin-top: 10px;\n filter: blur(20px);\n border-top-left"
},
{
"path": "examples/webpack3/src/b.css",
"chars": 198,
"preview": ".root {\n color: blue;\n font-family: monospace;\n font-size: 2em;\n}\n\n@media (max-width: 400px) {\n .root {\n color: h"
},
{
"path": "examples/webpack3/src/d.css",
"chars": 63,
"preview": "@supports (color: yellow) {\n .root {\n color: yellow;\n }\n}\n"
},
{
"path": "examples/webpack3/src/index.html",
"chars": 113,
"preview": "<!doctype html>\n<link rel=\"stylesheet\" href=\"./index.css\">\n<h1>Deterministic Style Sheets POF</h1>\n<main></main>\n"
},
{
"path": "examples/webpack3/src/index.js",
"chars": 315,
"preview": "const classNames = require('dss-classnames')\nconst a = require('./a.css')\nconst b = require('./b.css')\n\nconst root = doc"
},
{
"path": "examples/webpack3/webpack.config.js",
"chars": 1000,
"preview": "const path = require('path')\nconst HtmlWebpackPlugin = require('html-webpack-plugin')\nconst ExtractTextPlugin = require("
},
{
"path": "examples/webpack4/package.json",
"chars": 622,
"preview": "{\n \"name\": \"dss-example-webpack4\",\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\":"
},
{
"path": "examples/webpack4/src/a.css",
"chars": 358,
"preview": ".root:hover {\n color: yellow;\n}\n\n.block {\n display: block;\n margin-top: 10px;\n filter: blur(20px);\n border-top-left"
},
{
"path": "examples/webpack4/src/b.css",
"chars": 198,
"preview": ".root {\n color: blue;\n font-family: monospace;\n font-size: 2em;\n}\n\n@media (max-width: 400px) {\n .root {\n color: h"
},
{
"path": "examples/webpack4/src/d.css",
"chars": 63,
"preview": "@supports (color: yellow) {\n .root {\n color: yellow;\n }\n}\n"
},
{
"path": "examples/webpack4/src/index.html",
"chars": 113,
"preview": "<!doctype html>\n<link rel=\"stylesheet\" href=\"./index.css\">\n<h1>Deterministic Style Sheets POF</h1>\n<main></main>\n"
},
{
"path": "examples/webpack4/src/index.js",
"chars": 315,
"preview": "const classNames = require('dss-classnames')\nconst a = require('./a.css')\nconst b = require('./b.css')\n\nconst root = doc"
},
{
"path": "examples/webpack4/webpack.config.js",
"chars": 1161,
"preview": "const path = require('path')\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin')\nconst HtmlWebpackPlugin = r"
},
{
"path": "lerna.json",
"chars": 245,
"preview": "{\n \"lerna\": \"2.11.0\",\n \"packages\": [\n \"classnames\",\n \"compiler\",\n \"next-dss\",\n \"webpack\"\n ],\n \"version\":"
},
{
"path": "next-dss/LICENSE",
"chars": 1065,
"preview": "Copyright 2018-present Giuseppe Gurgone.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "next-dss/README.md",
"chars": 253,
"preview": "# next-dss\n\nDeterministic Style Sheets - Next.js plugin. Read [more about this package](https://dss-lang.com/usage/#next"
},
{
"path": "next-dss/index.js",
"chars": 2381,
"preview": "const DSSWebpackPlugin = require('dss-webpack')\nconst ExtractTextPlugin = require('extract-text-webpack-plugin')\nconst c"
},
{
"path": "next-dss/package.json",
"chars": 511,
"preview": "{\n \"name\": \"next-dss\",\n \"version\": \"0.1.0-beta.0\",\n \"main\": \"index.js\",\n \"keywords\": [\n \"dss\",\n \"atomic css\",\n"
},
{
"path": "package.json",
"chars": 1075,
"preview": "{\n \"private\": true,\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"Deterministic Style Sheets\",\n \"keywords\": [],\n \"au"
},
{
"path": "release-website",
"chars": 197,
"preview": "#!/usr/bin/env bash\n\nrm -rf out\ncd website &&\nyarn export &&\ntouch out/.nojekyll &&\ntouch out/CNAME &&\necho \"dss-lang.co"
},
{
"path": "webpack/LICENSE",
"chars": 1065,
"preview": "Copyright 2018-present Giuseppe Gurgone.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "webpack/README.md",
"chars": 290,
"preview": "# dss-webpack\n\nDeterministic Style Sheets - webpack loader and plugin for webpack 3 and 4. Read [more about this package"
},
{
"path": "webpack/index.js",
"chars": 1375,
"preview": "const optimizer = require('dss-compiler/processor').optimizer\n\nlet LastCallWebpackPlugin\ntry {\n LastCallWebpackPlugin ="
},
{
"path": "webpack/loader.js",
"chars": 884,
"preview": "const loaderUtils = require('loader-utils')\nconst dss = require('dss-compiler')\n\nconst BANNER = '/* DSS file */'\n\nmodule"
},
{
"path": "webpack/package.json",
"chars": 559,
"preview": "{\n \"name\": \"dss-webpack\",\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"Deterministic Style Sheets - webpack plugin an"
},
{
"path": "website/.babelrc",
"chars": 112,
"preview": "{\n \"presets\": [\"next/babel\"],\n \"plugins\": [[\"babel-plugin-classnames\", { \"packageName\": \"dss-classnames\"}]]\n}\n"
},
{
"path": "website/LICENSE",
"chars": 1065,
"preview": "Copyright 2018-present Giuseppe Gurgone.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "website/components/analytics/index.js",
"chars": 652,
"preview": "import React from 'react'\nimport ReactGA from 'react-ga'\n\nexport default class Analytics extends React.Component {\n tra"
},
{
"path": "website/components/body/index.css",
"chars": 205,
"preview": ".root {\n margin-top: 0;\n margin-bottom: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;"
},
{
"path": "website/components/body/index.js",
"chars": 115,
"preview": "import styles from './index.css'\n\nexport default ({children}) => <body className={[styles.root]}>{children}</body>\n"
},
{
"path": "website/components/heading/index.css",
"chars": 635,
"preview": ".tag {\n display: block;\n margin-top: 0;\n margin-bottom: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n p"
},
{
"path": "website/components/heading/index.js",
"chars": 893,
"preview": "import Link from '../link'\nimport styles from './index.css'\n\nconst defaultClassName = {\n link: [],\n tag: [],\n content"
},
{
"path": "website/components/layout/index.css",
"chars": 1899,
"preview": ".container {\n display: flex;\n flex-wrap: wrap;\n border-top-width: $SideBarSpacing;\n border-top-style: solid;\n borde"
},
{
"path": "website/components/layout/index.js",
"chars": 2513,
"preview": "import styles from './index.css'\nimport Link from '../link'\nimport Head from 'next/head'\nimport Logo from '../logo'\nimpo"
},
{
"path": "website/components/link/index.css",
"chars": 198,
"preview": ".root {\n color: $LinkColor;\n font-family: $LinkFontFamily;\n font-size: $LinkFontSize;\n text-decoration-line: none;\n "
},
{
"path": "website/components/link/index.js",
"chars": 387,
"preview": "import _Link from 'next/link'\nimport styles from './index.css'\n\nconst Link = ({children, className = null, target, ...pr"
},
{
"path": "website/components/logo/index.css",
"chars": 129,
"preview": ".root {\n display: inline-block;\n vertical-align: middle;\n width: 1em;\n height: 1em;\n line-height: 1;\n color: $Logo"
},
{
"path": "website/components/logo/index.js",
"chars": 599,
"preview": "import styles from './index.css'\n\nexport default ({ size = undefined, color = '#fff', backgroundColor = '#000' }) => (\n "
},
{
"path": "website/components/markdown/index.css",
"chars": 1331,
"preview": ".p {\n font-family: $MarkdownPFontFamily;\n font-size: $MarkdownPFontSize;\n line-height: 1.5;\n margin-top: $MarkdownPF"
},
{
"path": "website/components/markdown/index.js",
"chars": 750,
"preview": "import Heading from '../heading'\nimport Link from '../link'\nimport styles from './index.css'\n\nconst headings = [1, 2, 3,"
},
{
"path": "website/components/navigation/CurrentPath.js",
"chars": 69,
"preview": "import { createContext } from 'react'\nexport default createContext()\n"
},
{
"path": "website/components/navigation/index.css",
"chars": 2221,
"preview": ".button {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n position"
},
{
"path": "website/components/navigation/index.js",
"chars": 2003,
"preview": "import styles from './index.css'\nimport Link from '../link'\nimport CurrentPath from './CurrentPath'\n\nconst NavLink = pro"
},
{
"path": "website/components/playground/index.css",
"chars": 1262,
"preview": ".container {\n padding-top: 1em;\n padding-right: 1em;\n padding-bottom: 1em;\n padding-left: 1em;\n border-width: 1px;\n"
},
{
"path": "website/components/playground/index.js",
"chars": 781,
"preview": "import Link from '../link'\nimport styles from './index.css'\n\nexport default class Playground extends React.PureComponent"
},
{
"path": "website/md/atomic-css.md",
"chars": 3075,
"preview": "# Atomic CSS classes and smaller bundles ⚡️ 📦\n\nThe DSS compiler converts every CSS declaration to an atomic CSS classes "
},
{
"path": "website/md/classnames-helper.md",
"chars": 1223,
"preview": "# The classNames helper 📇\n\nSimilarly to CSS Modules, DSS generates mappings of `selector`-`array of atomic classes` and "
},
{
"path": "website/md/examples.md",
"chars": 520,
"preview": "# Examples\n\nThe most comprehensive example is this website which is styled with DSS. Feel free to take a look at its imp"
},
{
"path": "website/md/how-it-works.md",
"chars": 1469,
"preview": "# Deterministic styles resolution 🆎\n\nDSS' mission is to provide confidence when authoring CSS. This is done by resolving"
},
{
"path": "website/md/index.md",
"chars": 2177,
"preview": "import Playground from '../components/playground'\n\n# DSS ✨\n\nDSS (_Deterministic StyleSheets_) is a component-oriented CS"
},
{
"path": "website/md/sass-preprocessors.md",
"chars": 486,
"preview": "# SASS and Preprocessors 💪\n\nWe 💙 <img alt=\"sass\" src=\"/static/sass.png\" style={{ height: '2em', marginLeft: 5, verticalA"
},
{
"path": "website/md/supported-css-features.md",
"chars": 1257,
"preview": "# CSS features and rules ✂️\n\n**DSS supports a subset of CSS that makes it possible to compile down to atomic CSS classes"
},
{
"path": "website/md/usage.md",
"chars": 4392,
"preview": "# Usage 🔋\n\nTo its core DSS is a simple compiler that takes [regular CSS files](/supported-css-features) and generates at"
},
{
"path": "website/md/webpack.md",
"chars": 4628,
"preview": "# webpack 📦\n\nDSS comes with a webpack loader and plugin and since it works similarly to CSS Modules can leverage existin"
},
{
"path": "website/next.config.js",
"chars": 361,
"preview": "const withDSS = require('next-dss')\nconst withMDX = require('@zeit/next-mdx')({\n extension: /\\.mdx?$/\n})\n\nconst localId"
},
{
"path": "website/package.json",
"chars": 725,
"preview": "{\n \"name\": \"website\",\n \"version\": \"0.1.0-beta.0\",\n \"description\": \"\",\n \"scripts\": {\n \"dev\": \"next\",\n \"build\": "
},
{
"path": "website/pages/_app.js",
"chars": 1025,
"preview": "import DefaultApp, { Container } from 'next/app'\nimport Layout from '../components/layout'\nimport components from '../co"
},
{
"path": "website/pages/_document.js",
"chars": 2227,
"preview": "import Document, { Head, Main, NextScript } from 'next/document'\nimport Body from '../components/body'\n\nexport default c"
},
{
"path": "website/pages/atomic-css.js",
"chars": 67,
"preview": "import Document from '../md/atomic-css.md'\nexport default Document\n"
},
{
"path": "website/pages/classnames-helper.js",
"chars": 74,
"preview": "import Document from '../md/classnames-helper.md'\nexport default Document\n"
},
{
"path": "website/pages/examples.js",
"chars": 65,
"preview": "import Document from '../md/examples.md'\nexport default Document\n"
},
{
"path": "website/pages/how-it-works.js",
"chars": 69,
"preview": "import Document from '../md/how-it-works.md'\nexport default Document\n"
},
{
"path": "website/pages/index.js",
"chars": 62,
"preview": "import Document from '../md/index.md'\nexport default Document\n"
},
{
"path": "website/pages/sass-preprocessors.js",
"chars": 75,
"preview": "import Document from '../md/sass-preprocessors.md'\nexport default Document\n"
},
{
"path": "website/pages/supported-css-features.js",
"chars": 79,
"preview": "import Document from '../md/supported-css-features.md'\nexport default Document\n"
},
{
"path": "website/pages/usage.js",
"chars": 62,
"preview": "import Document from '../md/usage.md'\nexport default Document\n"
},
{
"path": "website/pages/webpack.js",
"chars": 64,
"preview": "import Document from '../md/webpack.md'\nexport default Document\n"
},
{
"path": "website/postcss.config.js",
"chars": 172,
"preview": "module.exports = {\n plugins: [\n require('postcss-easy-import')(),\n require('postcss-simple-vars')({ variables: ()"
},
{
"path": "website/static/playground/index.html",
"chars": 4431,
"preview": "<!DOCTYPE html>\n<meta charSet=\"utf-8\" />\n<title>DSS Playground</title>\n<style>\n* { box-sizing: border-box; }\nbody { marg"
},
{
"path": "website/theme/index.js",
"chars": 1575,
"preview": "const { generateVariables } = require('./utils')\nconst { borderRadius, color, fontFamily, fontSize, spacing } = require("
},
{
"path": "website/theme/utils.js",
"chars": 632,
"preview": "function fixNumber(thing) {\n return typeof thing === 'number' ? thing + 'px' : thing\n}\n\nfunction generateVariables(vari"
},
{
"path": "website/theme/variables.json",
"chars": 518,
"preview": "{\n \"color\": {\n \"White\": \"#fff\",\n \"Gray\": \"#fafafa\",\n \"Gray3\": \"#f4f4f4\",\n \"Black\": \"#000\",\n \"Black1\": \"#"
}
]
About this extraction
This page contains the full source code of the giuseppeg/dss GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 117 files (113.5 KB), approximately 34.0k tokens, and a symbol index with 37 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.