├── .gitignore
├── .npmignore
├── LICENSE.md
├── README.md
├── package.json
├── src
├── example
│ ├── FacebookProfile.js
│ ├── GoogleProfile.js
│ ├── Profile.js
│ ├── TwitterProfile.js
│ └── index.js
└── react-inline-css.js
├── static
├── index.html
├── mao.jpg
├── mao2.png
├── mao3.png
└── mao4.png
├── webpack.client-watch.js
└── webpack.client.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | dist/
4 | *.log
5 | *.map
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | dist/
4 | *.log
5 | *.map
6 | .DS_Store
7 |
8 | static/
9 | src/example/
10 | src/example.*
11 | tmp/
12 | webpack.*.js
13 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # BSD 3-Clause License
2 |
3 | Copyright © 2015, Rick Wong
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. Neither the name of the copyright holder nor the
15 | names of its contributors may be used to endorse or promote products
16 | derived from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # React Inline CSS
4 |
5 | Make your React components visually predictable. React Inline CSS allows you to write traditional CSS stylesheets in your components, automatically namespacing them for you.
6 |
7 | Inspired by the [SUIT CSS](https://suitcss.github.io/) methodology.
8 |
9 | ## Demo
10 |
11 | [Mao-mao-mao!](https://edealer.nl/mao)
12 |
13 | ## Example
14 |
15 | You write:
16 |
17 | ```javascript
18 | var Profile = React.createClass({
19 | render: function () {
20 | return (
21 |
37 |
38 |

39 |
Mao
40 |
41 |
42 | );
43 | }
44 | });
45 | ```
46 |
47 | You get namespaced CSS that works on sub-components (comparable to HTML5 `
71 |
72 | ```
73 |
74 | For a cascaded effect, see the `index.html` demo.
75 |
76 | ## Installation
77 |
78 | npm install --save react-inline-css react
79 |
80 | ## Usage
81 |
82 | Run `npm run watch` in your terminal and play with `example/` to get a feel of react-inline-css.
83 |
84 | ### SASS / LESS
85 |
86 | You can override the `&` as the selector to the current component. This is useful if you want to require precompiled stylesheets from an external file. Here's an example with [SASS loader for Webpack](https://www.npmjs.com/package/sass-loader):
87 |
88 | **UserComponent.js**
89 | ```javascript
90 | import React from "react";
91 | import InlineCss from "react-inline-css";
92 | const stylesheet = require("!raw!sass!./UserComponent.scss");
93 |
94 | class UserComponent extends React.Component {
95 | render () {
96 | return (
97 |
98 | Mao is no longer red!
99 | Mao is no longer red!
100 | Mao is no longer red!
101 |
102 | );
103 | }
104 | };
105 | ```
106 |
107 | **UserComponent.scss**
108 | ```scss
109 | UserComponent {
110 | color: red;
111 | .facebook {
112 | color: blue;
113 | }
114 | .google {
115 | color: blue;
116 | }
117 | .twitter {
118 | color: green;
119 | }
120 | }
121 | ```
122 |
123 | **result**
124 |
125 | 
126 |
127 | ## Community
128 |
129 | Let's start one together! After you ★Star this project, follow me [@Rygu](https://twitter.com/rygu)
130 | on Twitter.
131 |
132 | ### Contributors
133 |
134 | - [Danilo Moret](https://github.com/moret)
135 | - [Will Butler](https://github.com/willbutler)
136 |
137 | ## License
138 |
139 | BSD 3-Clause license. Copyright © 2015, Rick Wong. All rights reserved.
140 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-inline-css",
3 | "description": "Inline CSS in your React components, namespaced automatically.",
4 | "version": "2.3.1",
5 | "license": "BSD-3-Clause",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/RickWong/react-inline-css.git"
9 | },
10 | "homepage": "https://github.com/RickWong/react-inline-css",
11 | "keywords": [
12 | "react",
13 | "inline",
14 | "css",
15 | "react-component",
16 | "style"
17 | ],
18 | "main": "src/react-inline-css",
19 | "scripts": {
20 | "localhost": "sleep 2; which open && open http://localhost:8080",
21 | "build": "webpack --verbose --colors --display-error-details --config webpack.client.js",
22 | "watch-client": "webpack --verbose --colors --display-error-details --config webpack.client-watch.js && webpack-dev-server --config webpack.client-watch.js",
23 | "watch": "concurrently 'npm run watch-client' 'npm run localhost'"
24 | },
25 | "devDependencies": {
26 | "babel-core": "6.11.4",
27 | "babel-loader": "6.2.4",
28 | "babel-preset-es2015": "6.9.0",
29 | "babel-preset-react": "6.11.1",
30 | "concurrently": "2.2.0",
31 | "json-loader": "0.5.4",
32 | "react": "15.3.0",
33 | "react-create-class": "1.0.0",
34 | "react-dom": "15.3.0",
35 | "react-hot-loader": "1.3.0",
36 | "webpack": "1.13.1",
37 | "webpack-dev-server": "1.14.1"
38 | },
39 | "dependencies": {
40 | "prop-types": "15.5.10"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/example/FacebookProfile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Profile from "./Profile";
3 | import InlineCss from "../react-inline-css";
4 |
5 | /**
6 | * @module Profile
7 | */
8 | const FacebookProfile = React.createClass({
9 | statics: {
10 | css: () => `
11 | & .card {
12 | padding-top: 30px;
13 | border-radius: 3px;
14 | border: 1px solid rgb(208, 209, 213);
15 | border-left-color: rgb(223, 224, 228);
16 | border-right-color: rgb(223, 224, 228);
17 | border-top-color: rgb(229, 230, 233);
18 | background: linear-gradient(to bottom, rgba(255,255,255,1) 56%,rgba(204,204,204,1) 100%);
19 | }
20 | & .card > img {
21 | vertical-align: middle;
22 | padding: 4px;
23 | background: #fff;
24 | border: 1px solid #ccc;
25 | border-radius: 3px;
26 | }
27 | & .card > p {
28 | font-family: Helvetica, Arial, 'lucida grande', tahoma, verdana, arial, sans-serif;
29 | font-weight: bold;
30 | color: rgb(59, 89, 152);
31 | display: inline;
32 | }
33 | `
34 | },
35 | render: function () {
36 | return (
37 |
38 |
39 |
40 | );
41 | }
42 | });
43 |
44 | export default FacebookProfile;
45 |
--------------------------------------------------------------------------------
/src/example/GoogleProfile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Profile from "./Profile";
3 | import InlineCss from "../react-inline-css";
4 |
5 | /**
6 | * @module GoogleProfile
7 | */
8 | const GoogleProfile = React.createClass({
9 | statics: {
10 | css: () => `
11 | @import url(https://fonts.googleapis.com/css?family=Roboto);
12 |
13 | & .card {
14 | border-radius: 3px;
15 | background-color: #fff;
16 | box-shadow: inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07);
17 | border: 1px solid #d8d8d8;
18 | border-bottom-width: 2px;
19 | border-top-width: 0;
20 | vertical-align: top;
21 | }
22 | & .card > img {
23 | vertical-align: middle;
24 | border-radius: 9999px;
25 | border: 0;
26 | padding: 0;
27 | margin-right: 3px;
28 | }
29 | & .card > p {
30 | font-family: Roboto, arial, sans-serif;
31 | color: rgb(38, 38, 38);
32 | display: block;
33 | }
34 | `
35 | },
36 | render: function () {
37 | return (
38 |
39 |
40 |
41 | );
42 | }
43 | });
44 |
45 | export default GoogleProfile;
46 |
--------------------------------------------------------------------------------
/src/example/Profile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import InlineCss from "../react-inline-css";
3 |
4 | /**
5 | * @module Profile
6 | */
7 | const Profile = React.createClass({
8 | statics: {
9 | css: (avatarSize) => `
10 | & .card {
11 | margin: 15px;
12 | padding: 15px;
13 | text-align: center;
14 | height: 200px;
15 | }
16 | & .card > img {
17 | width: ${avatarSize}px;
18 | height: ${avatarSize}px;
19 | }
20 | & .card > p {
21 | margin: 10px;
22 | }
23 | `
24 | },
25 | render: function () {
26 | return (
27 |
28 |
29 |

30 |
{this.props.name || 'Default Mao'}
31 |
32 |
33 | );
34 | }
35 | });
36 |
37 | export default Profile;
38 |
--------------------------------------------------------------------------------
/src/example/TwitterProfile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Profile from "./Profile";
3 | import InlineCss from "../react-inline-css";
4 |
5 | /**
6 | * @module TwitterProfile
7 | */
8 | const TwitterProfile = React.createClass({
9 | statics: {
10 | css: () => `
11 | & .card {
12 | border-radius: 5px;
13 | background-color: rgb(178, 223, 218);
14 | border: 1px solid #e1e8ed;
15 | padding: 0;
16 | }
17 | & .card > img {
18 | background: #fff;
19 | border: 6px solid #fff;
20 | border-radius: 6px;
21 | margin: 15px;
22 | box-shadow: rgba(136, 153, 166, 0.14902) 0px 1px 1px 0px;
23 | }
24 | & .card > p {
25 | font-family: 'Helvetica Neue', Helvetica, arial, sans-serif;
26 | color: #292f33;
27 | font-size: 18px;
28 | display: block;
29 | background: #f5f8fa;
30 | border-radius: 0 0 5px 5px;
31 | margin: 0;
32 | padding: 10px;
33 | bottom: 0;
34 | font-weight: bold;
35 | }
36 | `
37 | },
38 | render: function () {
39 | return (
40 |
41 |
42 |
43 | );
44 | }
45 | });
46 |
47 | export default TwitterProfile;
48 |
--------------------------------------------------------------------------------
/src/example/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import InlineCss from "./../react-inline-css"
4 | import Profile from "./Profile";
5 | import FacebookProfile from "./FacebookProfile";
6 | import GoogleProfile from "./GoogleProfile";
7 | import TwitterProfile from "./TwitterProfile";
8 |
9 | /**
10 | * @module Main
11 | */
12 | const Main = React.createClass({
13 | statics: {
14 | css: (avatarSize) => `
15 | * {
16 | box-sizing: border-box;
17 | }
18 | & {
19 | font-family: monospace;
20 | padding: 10px 30px 30px;
21 | width: 450px;
22 | margin: 10px auto;
23 | }
24 | & #github {
25 | position: absolute;
26 | top: 0;
27 | right: 0;
28 | border: 0;
29 | }
30 | `
31 | },
32 | render () {
33 | return (
34 |
35 |
36 |
39 |
40 | React Inline CSS
41 | React Inline CSS allows you to cascade CSS stylesheets on your components, automatically namespacing them.
42 |
43 |
44 |
45 |
46 |
47 | );
48 | }
49 | });
50 |
51 | ReactDOM.render(, document.getElementById("react-root"));
52 |
--------------------------------------------------------------------------------
/src/react-inline-css.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @copyright © 2015, Rick Wong. All rights reserved.
3 | */
4 | var React = require("react");
5 | var PropTypes = require("prop-types");
6 | var assign = Object.assign ? Object.assign : React.__spread;
7 | var refCounter = 0;
8 | var createReactClass = require('create-react-class');
9 |
10 |
11 | /**
12 | * @module InlineCss
13 | */
14 | var InlineCss = createReactClass({
15 | displayName: "InlineCss",
16 | propTypes: {
17 | namespace: PropTypes.string,
18 | componentName: PropTypes.string,
19 | stylesheet: PropTypes.string.isRequired,
20 | className: PropTypes.string,
21 | wrapper: PropTypes.string
22 | },
23 | _transformSheet: function (stylesheet, componentName, namespace) {
24 | return stylesheet.
25 | // Prettier output.
26 | replace(/}\s*/ig, '\n}\n').
27 | // Regular rules are namespaced.
28 | replace(
29 | /(^|{|}|;|,)\s*([&a-z0-9\-~_=\.:#^\|\(\)\[\]\$'",>*\s]+)\s*(\{)/ig,
30 | function (matched) {
31 | return matched.replace(new RegExp(componentName, "g"), "#" + namespace);
32 | }
33 | );
34 | },
35 | render: function () {
36 | var namespace = this.props.namespace || "InlineCss-" + refCounter++;
37 | var componentName = this.props.componentName || "&";
38 | var stylesheet = this._transformSheet(this.props.stylesheet, componentName, namespace);
39 | var Wrapper = this.props.wrapper || "div";
40 |
41 | var wrapperProps = assign({}, this.props, {
42 | id: namespace
43 | });
44 |
45 | delete wrapperProps.namespace;
46 | delete wrapperProps.componentName;
47 | delete wrapperProps.stylesheet;
48 | delete wrapperProps.wrapper;
49 |
50 | return React.createElement(
51 | Wrapper,
52 | wrapperProps,
53 | this.props.children,
54 | React.createElement("style", {
55 | scoped: true,
56 | dangerouslySetInnerHTML: {__html: stylesheet}
57 | })
58 | );
59 | }
60 | });
61 |
62 | module.exports = InlineCss;
63 |
--------------------------------------------------------------------------------
/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-inline-style
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/static/mao.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RickWong/react-inline-css/897ece053eb90f5a15ebf0ce9f053e03a05118e0/static/mao.jpg
--------------------------------------------------------------------------------
/static/mao2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RickWong/react-inline-css/897ece053eb90f5a15ebf0ce9f053e03a05118e0/static/mao2.png
--------------------------------------------------------------------------------
/static/mao3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RickWong/react-inline-css/897ece053eb90f5a15ebf0ce9f053e03a05118e0/static/mao3.png
--------------------------------------------------------------------------------
/static/mao4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RickWong/react-inline-css/897ece053eb90f5a15ebf0ce9f053e03a05118e0/static/mao4.png
--------------------------------------------------------------------------------
/webpack.client-watch.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var config = require("./webpack.client.js");
3 |
4 | config.cache = true;
5 | config.debug = true;
6 | config.devtool = "eval";
7 |
8 | config.entry.WDS = "webpack-dev-server/client?http://localhost:8080";
9 | config.entry.hot = "webpack/hot/only-dev-server";
10 |
11 | config.module.postLoaders = [
12 | {test: /\.js$/, loaders: ["react-hot"], exclude: /node_modules/}
13 | ];
14 |
15 | config.output.publicPath = "http://localhost:8080/dist/";
16 | config.output.hotUpdateMainFilename = "update/[hash]/update.json";
17 | config.output.hotUpdateChunkFilename = "update/[hash]/[id].update.js";
18 |
19 | config.plugins = [
20 | new webpack.HotModuleReplacementPlugin()
21 | ];
22 |
23 | config.devServer = {
24 | publicPath: "http://localhost:8080/dist/",
25 | contentBase: "./static",
26 | hot: true,
27 | inline: true,
28 | quiet: true,
29 | noInfo: true,
30 | headers: {"Access-Control-Allow-Origin": "*"},
31 | stats: {colors: true}
32 | };
33 |
34 | module.exports = config;
35 |
--------------------------------------------------------------------------------
/webpack.client.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var path = require("path");
3 |
4 | module.exports = {
5 | target: "web",
6 | cache: false,
7 | context: __dirname,
8 | devtool: false,
9 | entry: {example:"./src/example"},
10 | output: {
11 | path: path.join(__dirname, "static/dist"),
12 | filename: "[name].js",
13 | chunkFilename: "[name].[id].js",
14 | publicPath: "dist/"
15 | },
16 | plugins: [
17 | new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}}),
18 | new webpack.optimize.DedupePlugin(),
19 | new webpack.optimize.OccurenceOrderPlugin(),
20 | new webpack.optimize.UglifyJsPlugin()
21 | ],
22 | module: {
23 | loaders: [
24 | {test: /\.json$/, loaders: ["json-loader"]},
25 | {test: /\.js$/, loaders: ["babel-loader?presets[]=es2015&presets[]=react"], exclude: /node_modules/},
26 | {test: /\.scss$/, loaders: ["raw-loader", "sass-loader"], exclude: /node_modules/}
27 | ]
28 | },
29 | resolve: {
30 | extensions: ["", ".json", ".jsx", ".js"]
31 | }
32 | };
33 |
--------------------------------------------------------------------------------