diff --git a/README.md b/README.md index 0c28568b..3013485b 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,8 @@ const Html = () => ( ) ``` -Rendering on the client automatically includes your app styles: +Rendering on the client automatically includes your app styles and supports +progressive app loading (i.e. code-splitting / lazy bundle loading): ```js // client.js diff --git a/docs/apis/StyleSheet.md b/docs/apis/StyleSheet.md index 22d7a063..b0b96c8e 100644 --- a/docs/apis/StyleSheet.md +++ b/docs/apis/StyleSheet.md @@ -37,24 +37,10 @@ Use styles: ``` -Render styles on the server or in the browser: - -```js -StyleSheet.renderToString() -``` - ## Methods **create**(obj: {[key: string]: any}) -**destroy**() - -Clears all style information. - -**renderToString**() - -Renders a CSS Style Sheet. - ## About ### Strategy @@ -71,9 +57,9 @@ CSS](https://speakerdeck.com/vjeux/react-css-in-js): 6. Non-deterministic resolution 7. Breaking isolation -The strategy also minimizes the amount of generated CSS, making it more viable -to inline the style sheet when pre-rendering pages on the server. There is one -unique selector per unique style _declaration_. +The strategy minimizes the amount of generated CSS, making it viable to inline +the style sheet when pre-rendering pages on the server. There is one unique +selector per unique style _declaration_. ```js // definition @@ -88,7 +74,7 @@ unique selector per unique style _declaration_. } } -// css +// css output // // .a { color: gray; } // .b { font-size: 2rem; } @@ -130,16 +116,17 @@ In production the class names are obfuscated. (CSS libraries like [Atomic CSS](http://acss.io/), [Basscss](http://www.basscss.com/), [SUIT CSS](https://suitcss.github.io/), and [tachyons](http://tachyons.io/) are attempts to limit style scope and limit -style sheet growth in a similar way. But they're CSS utility libraries, each with a -particular set of classes and features to learn. All of them require developers -to manually connect CSS classes for given styles.) +style sheet growth in a similar way. But they're CSS utility libraries, each +with a particular set of classes and features to learn. And all of them require +developers to manually connect CSS classes for given styles.) ### Reset React Native for Web includes a very small CSS reset taken from -[normalize.css](https://necolas.github.io/normalize.css/). It removes unwanted -User Agent styles from (pseudo-)elements beyond the reach of React (e.g., -`html`, `body`) or inline styles (e.g., `::-moz-focus-inner`). +[normalize.css](https://necolas.github.io/normalize.css/) – **you do not need +to include normalize.css**. It removes unwanted User Agent styles from +(pseudo-)elements beyond the reach of React (e.g., `html`, `body`) or inline +styles (e.g., `::-moz-focus-inner`). ```css html { @@ -162,4 +149,10 @@ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } + +ol, +ul, +li { + list-style:none +} ``` diff --git a/package.json b/package.json index ff38ac60..bbfae4eb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dist" ], "scripts": { - "build": "rm -rf ./dist && mkdir dist && babel src -d dist --ignore src/**/__tests__,src/modules/specHelpers", + "build": "rm -rf ./dist && mkdir dist && babel src -d dist --ignore **/__tests__,src/modules/specHelpers", "build:umd": "webpack --config config/webpack.config.js --sort-assets-by --progress", "examples": "webpack-dev-server --config config/webpack.config.example.js --inline --hot --colors --quiet", "lint": "eslint config examples src", diff --git a/src/__tests__/index-test.js b/src/__tests__/index-test.js index a047a866..a287efd7 100644 --- a/src/__tests__/index-test.js +++ b/src/__tests__/index-test.js @@ -1,6 +1,5 @@ /* eslint-env mocha */ -import * as utils from '../modules/specHelpers' import assert from 'assert' import React from '..' diff --git a/src/index.js b/src/index.js index 95e8ad70..3f6aa607 100644 --- a/src/index.js +++ b/src/index.js @@ -15,7 +15,7 @@ import Touchable from './components/Touchable' import View from './components/View' const renderStyle = () => { - return `` + return `` } const render = (element, container, callback) => { diff --git a/src/modules/StyleSheet/__tests__/index-test.js b/src/modules/StyleSheet/__tests__/index-test.js index dcf16c4f..93939085 100644 --- a/src/modules/StyleSheet/__tests__/index-test.js +++ b/src/modules/StyleSheet/__tests__/index-test.js @@ -8,24 +8,40 @@ const styles = { root: { borderWidth: 1 } } suite('modules/StyleSheet', () => { setup(() => { - StyleSheet.destroy() + StyleSheet._destroy() }) - test('create', () => { - assert.equal(StyleSheet.create(styles), styles) - }) + suite('create', () => { + const div = document.createElement('div') - test('renderToString', () => { - StyleSheet.create(styles) - assert.equal( - StyleSheet.renderToString(), - `${resetCSS}\n${predefinedCSS}\n` + - `/* 4 unique declarations */\n` + - `.borderBottomWidth\\:1px{border-bottom-width:1px;}\n` + - `.borderLeftWidth\\:1px{border-left-width:1px;}\n` + - `.borderRightWidth\\:1px{border-right-width:1px;}\n` + - `.borderTopWidth\\:1px{border-top-width:1px;}` - ) + setup(() => { + document.body.appendChild(div) + StyleSheet.create(styles) + div.innerHTML = `` + }) + + teardown(() => { + document.body.removeChild(div) + }) + + test('returns styles object', () => { + assert.equal(StyleSheet.create(styles), styles) + }) + + test('updates already-rendered style sheet', () => { + StyleSheet.create({ root: { color: 'red' } }) + + assert.equal( + document.getElementById('react-stylesheet').textContent, + `${resetCSS}\n${predefinedCSS}\n` + + `/* 5 unique declarations */\n` + + `.borderBottomWidth\\:1px{border-bottom-width:1px;}\n` + + `.borderLeftWidth\\:1px{border-left-width:1px;}\n` + + `.borderRightWidth\\:1px{border-right-width:1px;}\n` + + `.borderTopWidth\\:1px{border-top-width:1px;}\n` + + `.color\\:red{color:red;}` + ) + }) }) test('resolve', () => { @@ -34,4 +50,17 @@ suite('modules/StyleSheet', () => { StyleSheet.create(styles) assert.deepEqual(StyleSheet.resolve(props), expected) }) + + test('_renderToString', () => { + StyleSheet.create(styles) + assert.equal( + StyleSheet._renderToString(), + `${resetCSS}\n${predefinedCSS}\n` + + `/* 4 unique declarations */\n` + + `.borderBottomWidth\\:1px{border-bottom-width:1px;}\n` + + `.borderLeftWidth\\:1px{border-left-width:1px;}\n` + + `.borderRightWidth\\:1px{border-right-width:1px;}\n` + + `.borderTopWidth\\:1px{border-top-width:1px;}` + ) + }) }) diff --git a/src/modules/StyleSheet/index.js b/src/modules/StyleSheet/index.js index 9e41a1e8..0f1222ca 100644 --- a/src/modules/StyleSheet/index.js +++ b/src/modules/StyleSheet/index.js @@ -13,6 +13,24 @@ const initialState = { classNames: predefinedClassNames } const options = { obfuscateClassNames: process.env.NODE_ENV === 'production' } const createStore = () => new Store(initialState, options) let store = createStore() +let isRendered = false + +/** + * Destroy existing styles + */ +const _destroy = () => { + store = createStore() + isRendered = false +} + +/** + * Render the styles as a CSS style sheet + */ +const _renderToString = () => { + const css = store.toString() + isRendered = true + return `${resetCSS}\n${predefinedCSS}\n${css}` +} /** * Process all unique declarations @@ -33,24 +51,20 @@ const create = (styles: Object): Object => { } }) }) + + // update the style sheet in place + if (isRendered) { + const stylesheet = document.getElementById('react-stylesheet') + if (stylesheet) { + stylesheet.textContent = _renderToString() + } else if (process.env.NODE_ENV !== 'production') { + console.error('ReactNativeWeb: cannot find "react-stylesheet" element') + } + } + return styles } -/** - * Destroy existing styles - */ -const destroy = () => { - store = createStore() -} - -/** - * Render the styles as a CSS style sheet - */ -const renderToString = () => { - const css = store.toString() - return `${resetCSS}\n${predefinedCSS}\n${css}` -} - /** * Accepts React props and converts inline styles to single purpose classes * where possible. @@ -81,8 +95,8 @@ const resolve = ({ className = '', style = {} }) => { } export default { + _destroy, + _renderToString, create, - destroy, - renderToString, resolve }