UNPKG

14.5 kBMarkdownView Raw
1Composi
2=======
3
4Contents
5--------
6- [Installation](../README.md)
7- [JSX](./jsx.md)
8- [Hyperx](./hyperx.md)
9- [Hyperscript](./hyperscript.md)
10- [Functional Components](./functional-components.md)
11- [Mount, Render and Unmount](./render.md)
12- [Components](./components.md)
13- [State](./state.md)
14- [Lifecycle Methods](./lifecycle.md)
15- [Events](./events.md)
16- Styles
17 - [Styling a Component](#Styling-a-Component)
18 - [Inline Styles](#Inline-Styles)
19 - [Style Tag in Component](#Style-Tag-in-Component)
20 - [Styling Components with Stylor](#Styling-Components-with-Stylor)
21 - [Installing Stylor](#Installing-Stylor)
22 - [Using Stylor into Your Project](#Using-Stylor-into-Your-Project)
23 - [BEM](#BEM)
24 - [Scoped Stylesheets and Memory](#Scoped-Stylesheets-and-Memory)
25 - [SASS, LESS, POST-CSS](#SASS,-LESS,-POST-CSS)
26- [Unmount](./unmount.md)
27- [State Management with DataStore](./data-store.md)
28- [Third Party Libraries](./third-party.md)
29- [Deployment](./deployment.md)
30- [Differrences with React](./composi-react.md)
31
32
33## Styling a Component
34
35You can define styles for a component. There are three ways to do this:
36
371. You can style your component using [BEM] conventions(http://getbem.com/introduction/) and adding the component's styles to your project's stylesheet.
382. You can use inline styles. For this you can use a normal string value, or you can use a JavaScript object of property/values. This allows you to create dynamic style values based on particular conditions or component state.
393. You can provide your component with a style tag with styles for it inside the component's markup.
404. You can use the NPM module `stylor` to create a virtual stylesheet scoped to your component.
41
42
43## Inline Styles
44
45You can provide your component elements with inline styles. This is just like you would normally do with HTML, a style attribute followed by a string of CSS properties and values:
46
47
48```javascript
49const title = new Component({
50 container: 'header',
51 state: 'World',
52 render: state => (
53 <nav style='padding: 20px; background-color: #ccc;'>
54 <h1 style='margin: 0; color: #fff;'>Hello, {state}!<h1>
55 </nav>
56 )
57})
58```
59
60You can also choose to use a JavaScript object notation approach. This allows you to use dynamic JavaScript evaluations in your CSS. In this case you are passing the `style` attribute a JavaScript object defining the style to be created. This means that CSS properties that are hyphenated must be camel cased. Also all values other than pure numbers must be enclosed in quotes. Since the style property's value needs to be interpolated, the style definition needs to be enclosed in curly braces. Notice how we provide objects as styles inside curly braces:
61
62```javascript
63const list = new Component({
64 container: 'section',
65 render: (data) => (
66 <ul style={{
67 listStyle: 'none',
68 margin: '20px',
69 border: 'solid 1px #ccc',
70 borderBottom: 'none'
71 }}>
72 {
73 data.map(item => <li style={{
74 borderBottom: 'solid 1px #ccc',
75 padding: '5px 10px'
76 }}>{item}</li>)
77 }
78 </ul>
79 )
80})
81```
82
83Since the style value is a JavaScript object, you can remove a style from within the markup and store it as a separate value. This is especially easy when you define a component in its own file:
84
85```javascript
86// file: ./components/list.js
87
88// Define inline styles as separate objects:
89const listStyle = {
90 listStyle: 'none',
91 margin: '20px',
92 border: 'solid 1px #ccc',
93 borderBottom: 'none'
94}
95
96const listItemStyle = {
97 borderBottom: 'solid 1px #ccc',
98 padding: '5px 10px'
99}
100
101// Pass style objects to component:
102const list = new Component({
103 container: 'section',
104 render: (data) => (
105 <ul style={listStyle}>
106 {
107 data.map(item => <li style={listItemStyle}>{item}</li>)
108 }
109 </ul>
110 )
111})
112```
113
114Although inline styles result in highly portable styled components, they also result in markup that is harder to read. If you mind your component's legibility getting degraded by inline styles, consider using the style tag as explained previously, or using the <code>stylor</code> module explained next.
115
116## Style Tag in Component
117
118If you are creating an instance of the Component class, you want to define your styles in the render function and then pass them to the style tag inside your component's markup. In the example below, notice how we use the `nav` tag's id to scope the styles to it:
119
120```javascript
121import {h, Component} from 'composi'
122
123export const title = new Component({
124 container: 'header',
125 render: (message) => {
126 // Define styles for style tag:
127 const styles = `
128 #nav {
129 padding: 10px;
130 background-color: #333;
131 }
132 #nav h1 {
133 color: #fff;
134 font-size: 2rem;
135 }`
136
137 // Define style tag in component:
138 return (
139 <nav id='nav'>
140 <style>
141 {styles}
142 </style>
143 <h1>Hello, {message}!</h1>
144 </nav>
145 )
146 }
147})
148```
149
150If you are extending the Component class, the process is the same. Define your styles as a variable inside the `render` function before returning the markup:
151
152```javascript
153import {h, Component, uuid} from 'composi'
154
155class List extends Component {
156 constructor(props) {
157 super(props)
158 this.container = 'section'
159 }
160 render(data) {
161 const styles = `
162 .list {
163 list-style: none;
164 padding: 0;
165 margin: 10px;
166 border: solid 1px #ccc;
167 width: 300px;
168 }
169 .list > li {
170 margin: 0;
171 padding: 10px;
172 border-bottom: solid 1px #ccc;
173 }
174 .list > li:hover {
175 cursor: pointer;
176 }
177 .list > li:last-of-type {
178 border: none;
179 }
180 `
181 return (
182 <div id='list-container'>
183 <style>
184 {styles}
185 </style>
186 <ul class='list'>
187 {
188 data.map(item => <li key={item.id} data-id={item.id}><input type="checkbox" checked={item.checked}/> {item.name}</li>)
189 }
190 </ul>
191 </div>
192 )
193 }
194}
195```
196
197When you are using this technique, it is up to you to make sure the styles in the tag are scoped to the component. In the above examples we used an id on the base element of the component. However, if you want to have multiple instances of a component, then you might want to think about using BEM and add the styles directly to your project's stylesheet.
198
199
200## Styling Components with Stylor
201
202You can use the NPM module `stylor` to create a virtual stylesheet scoped to your components. You will do this inside the component's `componentDidMount` lifecyle method. This requires the styles to be defined as a JavaScript object. Properties must be camel cased and values must be quoted. If you want, you can use hypenated properties by enclosing them in quotes. Simple selectors are fine, but complex properties will need to be enclosed in quotes. You can use hierachical nesting to define parent child relationships, similar to how LESS and SASS do. If the value for a property will be a pixel value, you do not need to provide the "px". values of plain numbers will get converted to pixel values.
203
204
205## Installing Stylor
206
207Use NPM:
208
209```sh
210# cd to the project folder and run this:
211npm i -D stylor
212```
213
214## Using Stylor into Your Project
215After installing `stylor` as a dependency of your project, you need to import it in your project. In whichever file you want to use it, import it like this:
216
217```javascript
218import {createStylesheet} from 'stylor'
219```
220
221After importing `createStylesheet` from `stylor` you can use it to create a stylesheet for a component. The `createStylesheet` function takes an object with two properties: `base` and `styles`. `base` is a selector for the element from which styles will be scoped. This should be a unique selector, preferrably with an id. `styles` is an object defining the styles to be created.
222
223Here is an example of styles set up for a Component instance:
224
225```javascript
226const personComponent = new Component({
227 container: '#person',
228 state: personData,
229 render: (person) => (
230 <div>
231 <p>Name: {person.name.last}, {person.name.first}</p>
232 <p>Job: {person.job}</p>
233 <p>Employer: {person.employer}</p>
234 <p>Age: {person.age}</p>
235 </div>
236 ),
237 componentDidMount: () => {
238 // Define conponent-scoped styles:
239 createStylesheet({
240 base: '#person',
241 styles: {
242 border: 'solid 1px #ccc',
243 margin: '1em',
244 p: {
245 margin: 0,
246 padding: '5px 10px',
247 // Hover effect for p tags:
248 ':hover': {
249 backgroundColor: '#ddd',
250 color: '#fff',
251 cursor: 'pointer'
252 }
253 }
254 }
255 })
256 }
257})
258```
259
260An here is an example of styles set up for when extending Component:
261
262```javascript
263class Person extends Component {
264 constructor(opts) {
265 super(opts)
266 this.container = '#person'
267 this.state = personData
268 }
269 render = (person) => (
270 <div>
271 <p>Name: {person.name.last}, {person.name.first}</p>
272 <p>Job: {person.job}</p>
273 <p>Employer: {person.employer}</p>
274 <p>Age: {person.age}</p>
275 </div>
276 )
277 componentDidMount() {
278 // Define conponent-scoped styles:
279 createStylesheet({
280 base: '#person',
281 styles: {
282 border: 'solid 1px #ccc',
283 margin: '1em',
284 p: {
285 margin: 0,
286 padding: '5px 10px',
287 // Hover effect for p tags:
288 ':hover': {
289 backgroundColor: '#ddd',
290 color: '#fff',
291 cursor: 'pointer'
292 }
293 }
294 }
295 })
296 }
297}
298```
299
300And here's a sample of some general `styles` objects from an actual component. You can see that we can target element inside a component. Because the styles get scoped to the component, these styles will not leak out and affect other elements in the page.
301
302```javascript
303styles: {
304 label: {
305 display: 'inline-block',
306 width: 100,
307 textAlign: 'right'
308 },
309 button: {
310 color: '#fff',
311 backgroundColor: 'green'
312 },
313 'button.delete': {
314 transition: 'all .25s ease-out',
315 border: 'solid 1px red',
316 backgroundColor: 'red',
317 color: '#fff',
318 padding: '10px 20px',
319 ':hover': {
320 backgroundColor: '#fff',
321 color: 'red',
322 }
323 },
324 'li:last-of-type': {
325 borderBottom: 'none'
326 },
327 input: {
328 width: 150
329 }
330}
331```
332
333Here's another sample `styles` object:
334
335```javascript
336styles: {
337 div: {
338 margin: 20,
339 span: {
340 position: 'relative',
341 top: '-1px',
342 display: 'inline-block',
343 border: 'solid 1px #007aff',
344 padding:' 5px 10px 5px',
345 minWidth: '20px',
346 textAlign: 'center'
347 },
348 button: {
349 fontSize: '13pt',
350 border: 'solid 1px #007aff',
351 color: '#fff',
352 backgroundColor: '#007aff',
353 padding: '3px 10px 3px',
354 cursor: 'pointer',
355 margin: 0,
356 ':first-of-type': {
357 borderRight: 'none'
358 },
359 ':nth-child(3)': {
360 borderLeft: 'none'
361 },
362 ':last-of-type': {
363 backgroundColor: 'red',
364 borderColor: 'red',
365 color: '#fff',
366 marginLeft: 10,
367 ':hover': {
368 backgroundColor: '#fff',
369 color: 'red'
370 }
371 },
372 ':hover': {
373 backgroundColor: '#fff',
374 color: '#007aff',
375 borderColor: '#007aff'
376 },
377 '[disabled]': {
378 backgroundColor: '#ccc',
379 borderColor: '#ccc',
380 cursor: 'default',
381 opacity: '.75',
382 ':hover': {
383 backgroundColor: '#ccc',
384 color: '#007aff',
385 borderColor: '#ccc'
386 }
387 }
388 }
389 }
390}
391```
392
393## BEM
394
395This will also work with [BEM](https://css-tricks.com/bem-101/). When doing so, best to just use the generic body tag as the base for the stylesheet:
396
397```html
398<ul class="list">
399 <li class="list__item">
400 <h3 class="item__title">Joe Bodoni</h3>
401 </li>
402 <li class="list__item">
403 <h3 class="item__title-selected">Ellen Vanderbilt</h3>
404 </li>
405 <li class="list__item">
406 <h3 class="item__title">Sam Anderson</h3>
407 </li>
408</ul>
409```
410Define BEM CSS for above markup:
411
412```javascript
413createStylesheet('body', {
414 '.list': {
415 margin: '20px 0',
416 listStyle: 'none',
417 border: 'solid 1px #ccc'
418 },
419 '.list__item': {
420 padding: 0,
421 borderBottom: 'solid 1px #ccc',
422 ':last-of-type': {
423 border: 'none'
424 }
425 },
426 '.item__title': {
427 margin: 0,
428 padding: 10,
429 ':hover': {
430 backgroundColor: '#333',
431 color: '#fff',
432 cursor: 'pointer'
433 }
434 },
435 'item__title-selected': {
436 backgroundColor: '#333',
437 color: '#fff'
438 }
439})
440```
441
442
443## Scoped Stylesheets and Memory
444
445When you define styles on a class constructor, each instance of the class will have its own virtual stylesheet created. This is fine if the number of instances are not large. You should, however, bare in mind that each scoped stylesheet takes up memory. If you intend to create many instances of the same component, it might make sense to not create a scope style but to instead put the styles that all instances will share in your project's stylesheet.
446
447
448## SASS, LESS, POST-CSS
449
450If you want, you can use SASS, LESS or PostCSS as CSS preprocessors in your project. To do so you will have to use the `gulp` versions. For SASS, use [gulp-sass](https://www.npmjs.com/package/gulp-sass), or LESS use [gulp-less](https://www.npmjs.com/package/gulp-less) and for PostCSS use [postcss](https://www.npmjs.com/package/gulp-postcss). Just install these like this:
451
452```sh
453npm i -D gulp-sass
454// or
455npm i -D gulp-less
456// or
457npm i -D gulp-postcss
458```
459
460Then add these to your project's gulpfile:
461
462```javascript
463// For SASS:
464const sass = require('gulp.sass');
465
466// Define a task for SASS:
467gulp.task('sass', function () {
468 gulp.src('./scss/.scss')
469 .pipe(sass())
470 .pipe(gulp.dest('./css'))
471});
472
473// Then add new SASS task to build:
474gulp.task('default', ['sass', 'serve', 'watch'])
475```
476
477```javascript
478// For LESS:
479const less = require('gulp-less')
480
481// Define a task for LESS:
482gulp.task('less', function () {
483 return gulp.src('./less/**/*.less')
484 .pipe(less({
485 paths: [ path.join(__dirname, 'less', 'includes') ]
486 }))
487 .pipe(gulp.dest('./css'))
488})
489
490// Then add new LESS task to build:
491gulp.task('default', ['less', 'serve', 'watch'])
492```
493
494```javascript
495// For PostCSS
496const postcss = require('gulp-postcss');
497
498// Define a task for PostCSS:
499gulp.task('postcss', function () {
500 return gulp.src('./src/*.css')
501 .pipe(postcss())
502 .pipe(gulp.dest('./css'))
503})
504
505// Then add new PostCSS task to build:
506gulp.task('default', ['postcss', 'serve', 'watch'])
507```
\No newline at end of file