Skip to content

Commit 13fb19c

Browse files
add screenshots to components gallery
1 parent d680c39 commit 13fb19c

File tree

78 files changed

+739
-362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+739
-362
lines changed

build.js

+21-12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
require('./src/header-build')()
2-
require('./src/gallery-build')()
3-
require('./src/resources-build')()
4-
console.log('header build complete')
5-
require('./src/table-of-styles-build')()
6-
console.log('table of styles build complete')
7-
require('./src/table-of-properties-build')()
8-
console.log('table of properties build complete')
9-
require('./src/home-build')()
10-
console.log('home build complete')
11-
//require('./src/components-build')()
12-
//console.log('components build complete')
1+
// require('./src/header-build')()
2+
// require('./src/gallery-build')()
3+
// require('./src/resources-build')()
4+
// console.log('header build complete')
5+
// require('./src/table-of-styles-build')()
6+
// console.log('table of styles build complete')
7+
// require('./src/table-of-properties-build')()
8+
// console.log('table of properties build complete')
9+
// require('./src/home-build')()
10+
// console.log('home build complete')
11+
12+
// The rest of the build process is async and requires some promises
13+
// var comp_build = Promise.resolve(true); // uncomment and comment next to skip build
14+
var comp_build = require('./src/components-build')();
15+
comp_build.then(function () {
16+
console.log('components build complete')
17+
}).then(function () {
18+
return require('./src/components-screenshot-build')().then(function () {
19+
console.log('components screenshots complete')
20+
})
21+
})

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
"url": "https://github.com/tachyons-css/tachyons-css.github.io/issues"
3434
},
3535
"dependencies": {
36+
"co": "^4.6.0",
37+
"express": "^4.14.0",
38+
"nightmare": "^2.8.1",
3639
"tachyons-background-size": "^5.0.3",
3740
"tachyons-base": "^1.2.5",
3841
"tachyons-border-colors": "^4.2.2",

src/components-build.js

+94-82
Original file line numberDiff line numberDiff line change
@@ -28,93 +28,105 @@ var header = fs.readFileSync('src/templates/header.html', 'utf8')
2828
var highlight = fs.readFileSync('src/templates/highlight.html', 'utf8')
2929

3030
module.exports = function () {
31-
glob('src/components/**/*.html', {}, function (err, components) {
32-
if (err) {
33-
console.error(err)
34-
return
35-
}
36-
37-
var template = fs.readFileSync('src/templates/components.html', 'utf8')
38-
var indexTemplate = fs.readFileSync('src/templates/components-index.html', 'utf8')
39-
40-
var componentsForNav = {}
41-
components.map(function (component) {
42-
var componentTokens = component.replace('src/components/', '').split('/')
43-
var category = componentTokens[0]
44-
45-
componentsForNav[category] = componentsForNav[category] || []
46-
componentsForNav[category].push({
47-
href: component.replace('src', '').replace('.html', '') + '/index.html',
48-
name: getName(component)
49-
})
50-
})
51-
52-
var compiledPage = _.template(indexTemplate)({
53-
componentsForNav: componentsForNav,
54-
title: 'Components',
55-
analytics: analytics,
56-
footer: footer,
57-
head: head,
58-
header: header,
59-
})
60-
61-
mkdirp.sync('components')
62-
fs.writeFileSync('components/index.html', compiledPage)
63-
64-
components.forEach(function (component) {
65-
var newDir = rmHtmlExt(component.replace('src/', ''))
66-
var newFile = newDir + '/index.html'
67-
var componentHtml = fs.readFileSync(component, 'utf8')
68-
69-
var fmParsed = fm.parse(componentHtml)
70-
var frontMatter = fmParsed.attributes || {}
71-
frontMatter.bodyClass = frontMatter.bodyClass || ''
72-
frontMatter.title = frontMatter.title || getTitle(component)
73-
frontMatter.name = frontMatter.name || getName(component)
74-
frontMatter.classes = getClasses(fmParsed.body).map(function(klass) {
75-
return '.' + klass
31+
return new Promise(function (resolve, reject) {
32+
glob('src/components/**/*.html', {}, function (err, components) {
33+
if (err) {
34+
console.error(err)
35+
return reject(err)
36+
}
37+
38+
var template = fs.readFileSync('src/templates/components.html', 'utf8')
39+
var indexTemplate = fs.readFileSync('src/templates/components-index.html', 'utf8')
40+
41+
var componentsForNav = {}
42+
components.map(function (component) {
43+
var componentTokens = component.replace('src/components/', '').split('/')
44+
var category = componentTokens[0]
45+
46+
// Check the front matter for screenshot overrides
47+
var componentHtml = fs.readFileSync(component, 'utf8')
48+
var fmParsed = fm.parse(componentHtml)
49+
var frontMatter = fmParsed.attributes || {}
50+
var screenshot = frontMatter.screenshot || {}
51+
screenshot.path = component.replace('src', '').replace('.html', '') + '/screenshot.png'
52+
53+
componentsForNav[category] = componentsForNav[category] || []
54+
componentsForNav[category].push({
55+
href: component.replace('src', '').replace('.html', '') + '/index.html',
56+
name: getName(component),
57+
screenshot: screenshot
58+
})
7659
})
77-
frontMatter.componentHtml = componentHtml
78-
frontMatter.content = fmParsed.body
79-
frontMatter.escapedHtml = escapeHtml(fmParsed.body)
80-
frontMatter.footer = footer
81-
frontMatter.analytics = analytics
82-
frontMatter.head = head
83-
frontMatter.highlight = highlight
84-
frontMatter.componentsForNav = componentsForNav
85-
86-
var moduleSrcs = {}
87-
var getModules = postcss.plugin('get-modules', function () {
88-
return function (css, result) {
89-
css.walkRules(function (rule) {
90-
moduleSrcs[rule.source.input.from] = true
91-
})
92-
}
60+
61+
var compiledPage = _.template(indexTemplate)({
62+
componentsForNav: componentsForNav,
63+
title: 'Components',
64+
analytics: analytics,
65+
footer: footer,
66+
head: head,
67+
header: header,
9368
})
9469

95-
postcss([
96-
atImport(), cssVariables(), conditionals(), customMedia(), select(frontMatter.classes),
97-
removeComments({ removeAll: true }), mqPacker(), removeEmpty(), getModules(), perfectionist()
98-
]).process(tachyonsCss, {
99-
from: 'src/css/tachyons.css'
100-
}).then(function (result) {
101-
console.log('component css selection complete for', component)
102-
frontMatter.componentCss = result.css
103-
frontMatter.stats = cssstats(frontMatter.componentCss)
104-
105-
// TODO: Update me once src/ uses the npm modules
106-
frontMatter.modules = Object.keys(moduleSrcs).map(function (module) {
107-
return 'tachyons-' + module.split('/_')[1].replace('.css', '')
70+
mkdirp.sync('components')
71+
fs.writeFileSync('components/index.html', compiledPage)
72+
73+
var promises = []
74+
components.forEach(function (component) {
75+
var newDir = rmHtmlExt(component.replace('src/', ''))
76+
var newFile = newDir + '/index.html'
77+
var componentHtml = fs.readFileSync(component, 'utf8')
78+
79+
var fmParsed = fm.parse(componentHtml)
80+
var frontMatter = fmParsed.attributes || {}
81+
frontMatter.bodyClass = frontMatter.bodyClass || ''
82+
frontMatter.title = frontMatter.title || getTitle(component)
83+
frontMatter.name = frontMatter.name || getName(component)
84+
frontMatter.classes = getClasses(fmParsed.body).map(function(klass) {
85+
return '.' + klass
86+
})
87+
frontMatter.componentHtml = componentHtml
88+
frontMatter.content = fmParsed.body
89+
frontMatter.escapedHtml = escapeHtml(fmParsed.body)
90+
frontMatter.footer = footer
91+
frontMatter.analytics = analytics
92+
frontMatter.head = head
93+
frontMatter.highlight = highlight
94+
frontMatter.componentsForNav = componentsForNav
95+
96+
var moduleSrcs = {}
97+
var getModules = postcss.plugin('get-modules', function () {
98+
return function (css, result) {
99+
css.walkRules(function (rule) {
100+
moduleSrcs[rule.source.input.from] = true
101+
})
102+
}
108103
})
109104

110-
var compiledPage = _.template(template)(frontMatter)
111-
console.log('creating new dir', newDir)
112-
mkdirp.sync(newDir)
113-
fs.writeFileSync(newFile, compiledPage)
114-
console.log('finished component build for', component)
115-
}).catch(function (e) { console.log(e) })
116-
})
117-
})
105+
promises.push(postcss([
106+
atImport(), cssVariables(), conditionals(), customMedia(), select(frontMatter.classes),
107+
removeComments({ removeAll: true }), mqPacker(), removeEmpty(), getModules(), perfectionist()
108+
]).process(tachyonsCss, {
109+
from: 'src/css/tachyons.css'
110+
}).then(function (result) {
111+
console.log('component css selection complete for', component)
112+
frontMatter.componentCss = result.css
113+
frontMatter.stats = cssstats(frontMatter.componentCss)
114+
115+
// TODO: Update me once src/ uses the npm modules
116+
frontMatter.modules = Object.keys(moduleSrcs).map(function (module) {
117+
return 'tachyons-' + module.split('/_')[1].replace('.css', '')
118+
})
119+
120+
var compiledPage = _.template(template)(frontMatter)
121+
console.log('creating new dir', newDir)
122+
mkdirp.sync(newDir)
123+
fs.writeFileSync(newFile, compiledPage)
124+
console.log('finished component build for', component)
125+
}).catch(function (e) { console.log(e) }))
126+
})
127+
resolve(Promise.all(promises))
128+
}) // glob
129+
}) // return promise
118130
}
119131

120132
function getTitle(component) {

src/components-screenshot-build.js

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
var co = require('co')
2+
var express = require('express');
3+
var fm = require('json-front-matter')
4+
var fs = require('fs')
5+
var glob = require('glob')
6+
var Nightmare = require('nightmare')
7+
var path = require('path')
8+
var rmHtmlExt = require('remove-html-extension')
9+
10+
var screenshotTargetWidth = 1024
11+
var screenshotMaxHeight = 768
12+
var screenshotName = 'screenshot.png'
13+
var screenshotSelector = '[data-name="component-container"]'
14+
15+
module.exports = function () {
16+
return new Promise(function (resolve, reject) {
17+
glob('src/components/**/*.html', {}, function (err, components) {
18+
if (err) {
19+
console.error(err)
20+
return reject(err)
21+
}
22+
23+
// Setup a quick static HTML server so that CSS is loaded correctly.
24+
var app = express()
25+
app.set('port', 3333)
26+
app.use(express.static(path.join(__dirname, '..')))
27+
var server = app.listen(app.get('port'), function() {
28+
console.log('Starting static HTML server on port ' + server.address().port)
29+
})
30+
31+
// Initialize nightmware now.
32+
var nightmare = Nightmare({
33+
show: false,
34+
frame: false,
35+
useContentSize: true,
36+
switches: {
37+
// Unfortunately .viewport() sets the viewport size in *CSS pixels*.
38+
// Let's force device pixel ratio to 1 otherwise screenshots will vary in size
39+
// depending on the machine running this script (double for Retina Macbook Pro)
40+
// https://github.com/segmentio/nightmare/issues/498#issuecomment-265948400
41+
'force-device-scale-factor': '1'
42+
}
43+
})
44+
45+
return co(function *() {
46+
for (var i = 0; i < components.length; i++) {
47+
var newDir = rmHtmlExt(components[i].replace('src/', ''))
48+
var newFile = newDir + '/index.html'
49+
50+
// Check the front matter for screenshot overrides
51+
var componentHtml = fs.readFileSync(components[i], 'utf8')
52+
var fmParsed = fm.parse(componentHtml)
53+
var frontMatter = fmParsed.attributes || {}
54+
var selector = screenshotSelector
55+
if (frontMatter.screenshot && frontMatter.screenshot.selector) {
56+
selector += ' ' + frontMatter.screenshot.selector
57+
}
58+
59+
// Grab the size of the component enclosing rectangle
60+
// https://github.com/segmentio/nightmare/issues/498#issuecomment-189156529
61+
var rect = yield nightmare
62+
.viewport(screenshotTargetWidth, screenshotMaxHeight)
63+
// .wait(1000)
64+
.goto('http://localhost:' + app.get('port') + '/' + newFile)
65+
.wait(selector)
66+
.evaluate(function(_selector) {
67+
// Hide scrollbar that could pop up due to .scrollTo
68+
var sheet = document.styleSheets[0]
69+
sheet.insertRule('::-webkit-scrollbar { display:none; }')
70+
var element = document.querySelector(_selector)
71+
if (element) {
72+
var rect = element.getBoundingClientRect()
73+
return {
74+
x: Math.round(rect.left),
75+
y: Math.round(rect.top),
76+
width: Math.round(rect.width),
77+
height: Math.round(rect.height)
78+
}
79+
}
80+
return false
81+
}, selector)
82+
83+
// Capture the component
84+
if (rect !== false) {
85+
rect.height = Math.min(rect.height, screenshotMaxHeight)
86+
yield nightmare
87+
// we can not use .screenshot() with rect, so constrain the viewport instead
88+
.viewport(rect.width, rect.height)
89+
.scrollTo(rect.y, rect.x)
90+
// .wait(1000)
91+
// do *not* use rect in .screenshot() below or risk distortions
92+
.screenshot(newDir + '/' + screenshotName)
93+
.then(function () {
94+
console.log('screenshotted: ' + newFile)
95+
})
96+
}
97+
}
98+
99+
yield nightmare.end()
100+
}).then(function () {
101+
console.log('finished rendering screenshots')
102+
}, function(err) {
103+
console.error(err)
104+
}).then(function () {
105+
console.log('closing static HTML server')
106+
server.close()
107+
})
108+
109+
// TODO: optimize all PNG files, eventually resize to smaller width
110+
// const imagemin = require('imagemin');
111+
// const imageminPngquant = require('imagemin-pngquant');
112+
// imagemin(['components/*.{jpg,png}'], ??, {
113+
// plugins: [
114+
// imageminPngquant({quality: '65-80'})
115+
// ]
116+
// }).then(files => {
117+
// console.log(files);
118+
// //=> [{data: <Buffer 89 50 4e …>, path: 'build/images/foo.jpg'}, …]
119+
// });
120+
121+
}) // glob
122+
}) // return promise
123+
}

src/components/banners/info.html

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{{{
2-
"bodyClass" : "bg-white pt5"
2+
"bodyClass" : "bg-white pt5",
3+
"screenshot" : {
4+
"background-size" : "contain"
5+
}
36
}}}
47
<div class="flex items-center justify-center pa4 bg-lightest-blue navy">
58
<svg class="w1" data-icon="info" viewBox="0 0 32 32" style="fill:currentcolor">

src/components/banners/single-cta.html

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11

22
{{{
3-
"bodyClass" : "bg-white"
3+
"bodyClass" : "bg-white",
4+
"screenshot" : {
5+
"background-size" : "contain"
6+
}
47
}}}
58

69
<section class="ph3 ph5-ns pv5">

src/components/buttons/basic-previous-next.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"bodyClass" : "bg-white pt5"
33
}}}
44

5-
<div class="flex items-center justify-center pb5">
5+
<div class="flex items-center justify-center pa4">
66
<a href="#0" class="f5 no-underline black bg-animate hover-bg-black hover-white inline-flex items-center pa3 ba border-box mr4">
77
<svg class="w1" data-icon="chevronLeft" viewBox="0 0 32 32" style="fill:currentcolor">
88
<title>chevronLeft icon</title>

0 commit comments

Comments
 (0)