UNPKG

13.9 kBMarkdownView Raw
1Composi
2=======
3
4Contents
5--------
6- [Components](./components.md)
7- [JSX](./jsx.md)
8- [Hyperx](./hyperx.md)
9- [Hyperscript](./hyperscript.md)
10- [Mount and Render](./render.md)
11- [State](./state.md)
12- [Lifecycle Methods](./lifecycle.md)
13- [Events](./events.md)
14- Styles
15- [Unmount](./unmount.md)
16- [Installation](../README.md)
17- [Third Party Libraries](./third-party.md)
18- [Functional Components](./functional-components.md)
19- [Deployment](./deployment.md)
20
21Styling a Component
22-------------------
23
24You can define styles for a component. There are three ways to do this:
25
261. You can style your component using [BEM] conventions(http://getbem.com/introduction/) and adding the component's styles to your project's stylesheet.
272. You can use inline styles. This expects an object of property/values.
283. You can provide your component with a style tag with styles for it inside the component's markup.
294. You can use the NPM module `stylor` to create a virtual stylesheet scoped to your component.
30
31Style Tag in Component
32----------------------
33If 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:
34
35```javascript
36import {h, Component} from 'composi'
37
38export const title = new Component({
39 container: 'header',
40 render: (message) => {
41 // Define styles for style tag:
42 const styles = `
43 #nav {
44 padding: 10px;
45 background-color: #333;
46 }
47 #nav h1 {
48 color: #fff;
49 font-size: 2rem;
50 }`
51
52 // Define style tag in component:
53 return (
54 <nav id='nav'>
55 <style>
56 {styles}
57 </style>
58 <h1>Hello, {message}!</h1>
59 </nav>
60 )
61 }
62})
63```
64
65If 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:
66
67```javascript
68import {h, Component, uuid} from 'composi'
69
70class List extends Component {
71 constructor(props) {
72 super(props)
73 this.container = 'section'
74 }
75 render(data) {
76 const styles = `
77 .list {
78 list-style: none;
79 padding: 0;
80 margin: 10px;
81 border: solid 1px #ccc;
82 width: 300px;
83 }
84 .list > li {
85 margin: 0;
86 padding: 10px;
87 border-bottom: solid 1px #ccc;
88 }
89 .list > li:hover {
90 cursor: pointer;
91 }
92 .list > li:last-of-type {
93 border: none;
94 }
95 `
96 return (
97 <div id='list-container'>
98 <style>
99 {styles}
100 </style>
101 <ul class='list'>
102 {
103 data.map(item => <li key={item.id} data-id={item.id}><input type="checkbox" checked={item.checked}/> {item.name}</li>)
104 }
105 </ul>
106 </div>
107 )
108 }
109}
110```
111
112When 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.
113
114Inline Styles
115-------------
116
117You 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:
118
119
120```javascript
121const title = new Component({
122 container: 'header',
123 state: 'World',
124 render: state => (
125 &lt;nav style='padding: 20px; background-color: #ccc;'>
126 &lt;h1 style='margin: 0; color: #fff;'>Hello, {state}!&lt;h1>
127 &lt;/nav>
128 )
129})
130```
131
132You can also choose to use an 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:
133
134```javascript
135const list = new Component({
136 container: 'section',
137 render: (data) => (
138 <ul style={{
139 listStyle: 'none',
140 margin: '20px',
141 border: 'solid 1px #ccc',
142 borderBottom: 'none'
143 }}>
144 {
145 data.map(item => <li style={{
146 borderBottom: 'solid 1px #ccc',
147 padding: '5px 10px'
148 }}>{item}</li>)
149 }
150 </ul>
151 )
152})
153```
154
155Since 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:
156
157```javascript
158// file: ./components/list.js
159
160// Define inline styles as separate objects:
161const listStyle = {
162 listStyle: 'none',
163 margin: '20px',
164 border: 'solid 1px #ccc',
165 borderBottom: 'none'
166}
167
168const listItemStyle = {
169 borderBottom: 'solid 1px #ccc',
170 padding: '5px 10px'
171}
172
173// Pass style objects to component:
174const list = new Component({
175 container: 'section',
176 render: (data) => (
177 <ul style={listStyle}>
178 {
179 data.map(item => <li style={listItemStyle}>{item}</li>)
180 }
181 </ul>
182 )
183})
184```
185
186Although 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.
187
188
189Using Stylor
190------------
191
192You can use the NPM module `stylor` to create a virtual stylesheet scoped to your components. You will do this inside the component's `componentWasCreated` 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.
193
194### Installing Stylor
195
196Use NPM:
197
198```sh
199# cd to the project folder and run this:
200npm i -D stylor
201```
202### Importing Stylor into Your Project
203After 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:
204
205```javascript
206import {createStylesheet} from 'stylor'
207```
208
209After 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.
210
211Here is an example of styles set up for a Component instance:
212
213```javascript
214const personComponent = new Component({
215 container: '#person',
216 state: personData,
217 render: (person) => (
218 <div>
219 <p>Name: {person.name.last}, {person.name.first}</p>
220 <p>Job: {person.job}</p>
221 <p>Employer: {person.employer}</p>
222 <p>Age: {person.age}</p>
223 </div>
224 ),
225 componentWasCreated: () => {
226 // Define conponent-scoped styles:
227 createStylesheet({
228 base: '#person',
229 styles: {
230 border: 'solid 1px #ccc',
231 margin: '1em',
232 p: {
233 margin: 0,
234 padding: '5px 10px',
235 // Hover effect for p tags:
236 ':hover': {
237 backgroundColor: '#ddd',
238 color: '#fff',
239 cursor: 'pointer'
240 }
241 }
242 }
243 })
244 }
245})
246```
247
248An here is an example of styles set up for when extending Component:
249
250```javascript
251class Person extends Component {
252 constructor(opts) {
253 super(opts)
254 this.container = '#person'
255 this.state = personData
256 }
257 render = (person) => (
258 <div>
259 <p>Name: {person.name.last}, {person.name.first}</p>
260 <p>Job: {person.job}</p>
261 <p>Employer: {person.employer}</p>
262 <p>Age: {person.age}</p>
263 </div>
264 )
265 componentWasCreated() {
266 // Define conponent-scoped styles:
267 createStylesheet({
268 base: '#person',
269 styles: {
270 border: 'solid 1px #ccc',
271 margin: '1em',
272 p: {
273 margin: 0,
274 padding: '5px 10px',
275 // Hover effect for p tags:
276 ':hover': {
277 backgroundColor: '#ddd',
278 color: '#fff',
279 cursor: 'pointer'
280 }
281 }
282 }
283 })
284 }
285}
286```
287
288And 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.
289
290```javascript
291styles: {
292 label: {
293 display: 'inline-block',
294 width: 100,
295 textAlign: 'right'
296 },
297 button: {
298 color: '#fff',
299 backgroundColor: 'green'
300 },
301 'button.delete': {
302 transition: 'all .25s ease-out',
303 border: 'solid 1px red',
304 backgroundColor: 'red',
305 color: '#fff',
306 padding: '10px 20px',
307 ':hover': {
308 backgroundColor: '#fff',
309 color: 'red',
310 }
311 },
312 'li:last-of-type': {
313 borderBottom: 'none'
314 },
315 input: {
316 width: 150
317 }
318}
319```
320
321Here's another sample `styles` object:
322
323```javascript
324styles: {
325 div: {
326 margin: 20,
327 span: {
328 position: 'relative',
329 top: '-1px',
330 display: 'inline-block',
331 border: 'solid 1px #007aff',
332 padding:' 5px 10px 5px',
333 minWidth: '20px',
334 textAlign: 'center'
335 },
336 button: {
337 fontSize: '13pt',
338 border: 'solid 1px #007aff',
339 color: '#fff',
340 backgroundColor: '#007aff',
341 padding: '3px 10px 3px',
342 cursor: 'pointer',
343 margin: 0,
344 ':first-of-type': {
345 borderRight: 'none'
346 },
347 ':nth-child(3)': {
348 borderLeft: 'none'
349 },
350 ':last-of-type': {
351 backgroundColor: 'red',
352 borderColor: 'red',
353 color: '#fff',
354 marginLeft: 10,
355 ':hover': {
356 backgroundColor: '#fff',
357 color: 'red'
358 }
359 },
360 ':hover': {
361 backgroundColor: '#fff',
362 color: '#007aff',
363 borderColor: '#007aff'
364 },
365 '[disabled]': {
366 backgroundColor: '#ccc',
367 borderColor: '#ccc',
368 cursor: 'default',
369 opacity: '.75',
370 ':hover': {
371 backgroundColor: '#ccc',
372 color: '#007aff',
373 borderColor: '#ccc'
374 }
375 }
376 }
377 }
378}
379```
380
381BEM
382---
383
384This 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:
385
386```html
387<ul class="list">
388 <li class="list__item">
389 <h3 class="item__title">Joe Bodoni</h3>
390 </li>
391 <li class="list__item">
392 <h3 class="item__title-selected">Ellen Vanderbilt</h3>
393 </li>
394 <li class="list__item">
395 <h3 class="item__title">Sam Anderson</h3>
396 </li>
397</ul>
398```
399Define BEM CSS for above markup:
400
401```javascript
402createStylesheet('body', {
403 '.list': {
404 margin: '20px 0',
405 listStyle: 'none',
406 border: 'solid 1px #ccc'
407 },
408 '.list__item': {
409 padding: 0,
410 borderBottom: 'solid 1px #ccc',
411 ':last-of-type': {
412 border: 'none'
413 }
414 },
415 '.item__title': {
416 margin: 0,
417 padding: 10,
418 ':hover': {
419 backgroundColor: '#333',
420 color: '#fff',
421 cursor: 'pointer'
422 }
423 },
424 'item__title-selected': {
425 backgroundColor: '#333',
426 color: '#fff'
427 }
428})
429```
430
431Scoped Stylesheets and Memory
432-----------------------------
433
434When 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.
435
436SASS, LESS, POST-CSS
437--------------------
438
439If 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:
440
441```sh
442npm i -D gulp-sass
443// or
444npm i -D gulp-less
445// or
446npm i -D gulp-postcss
447```
448
449Then add these to your project's gulpfile:
450
451```javascript
452// For SASS:
453const sass = require('gulp.sass');
454
455// Define a task for SASS:
456gulp.task('sass', function () {
457 gulp.src('./scss/.scss')
458 .pipe(sass())
459 .pipe(gulp.dest('./css'))
460});
461
462// Then add new SASS task to build:
463gulp.task('default', ['sass', 'serve', 'watch'])
464```
465
466```javascript
467// For LESS:
468const less = require('gulp-less')
469
470// Define a task for LESS:
471gulp.task('less', function () {
472 return gulp.src('./less/**/*.less')
473 .pipe(less({
474 paths: [ path.join(__dirname, 'less', 'includes') ]
475 }))
476 .pipe(gulp.dest('./css'))
477})
478
479// Then add new LESS task to build:
480gulp.task('default', ['less', 'serve', 'watch'])
481```
482
483```javascript
484// For PostCSS
485const postcss = require('gulp-postcss');
486
487// Define a task for PostCSS:
488gulp.task('postcss', function () {
489 return gulp.src('./src/*.css')
490 .pipe(postcss())
491 .pipe(gulp.dest('./css'))
492})
493
494// Then add new PostCSS task to build:
495gulp.task('default', ['postcss', 'serve', 'watch'])
496```
\No newline at end of file