Skip to content
This repository was archived by the owner on Jul 18, 2024. It is now read-only.

Commit 16e5e80

Browse files
committed
Initial commit
0 parents  commit 16e5e80

File tree

104 files changed

+12536
-0
lines changed

Some content is hidden

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

104 files changed

+12536
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
examples
2+
extension

LICENSE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Copyright 2017 IBM
2+
Licensed under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License.
4+
You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software
9+
distributed under the License is distributed on an "AS IS" BASIS,
10+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the License for the specific language governing permissions and
12+
limitations under the License.

README.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# CSS Gridish
2+
3+
![CSS Gridish Logo](/graphics/gridish_3times.gif?raw=true)
4+
5+
CSS Gridish takes design specs of your product’s grid and builds out several resources for your team to use:
6+
7+
* Sketch file with artboards and grid/layout settings for designers
8+
* CSS/SCSS code using CSS Grid with a CSS Flexbox fallback for developers
9+
* [Google Chrome extension](https://chrome.google.com/webstore/detail/css-gridish/ebhcneoilkamaddhlphlehojpcooobgc) for anyone to check a webpage’s alignment
10+
11+
**This tool is not a grid system with a grid already designed for you.** Instead, CSS Gridish builds all of the resources for the grid your team designed.
12+
13+
We hope it helps teams adapt CSS Grid sooner and enables more complex layouts. To show how versatile the tool is, we have examples of grids from [Bootstrap,](https://ibm.github.io/css-gridish/examples/bootstrap/index.html) [Carbon Design System,](https://ibm.github.io/css-gridish/examples/carbon/index.html) and [Material Design.](https://ibm.github.io/css-gridish/examples/material/index.html)
14+
15+
The truth is that many enterprise projects can’t afford to drop support for browsers that do not [support CSS Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/grid#Browser_compatibility) yet. This tool takes your grid’s design specs and builds a slim CSS Grid Layout implementation and a fallback to [CSS Flexbox support.](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout#Browser_compatibility)
16+
17+
## Build your grid code
18+
19+
Requires [Node v8.2.0](https://nodejs.org/en/) or higher, which includes npm and npx.
20+
21+
1. Create a file called `css-gridish.json` in your project root. See the [config documentation](#quick-overview) or an [example config](./examples/material/css-gridish.json) for help.
22+
2. Determine how you want your grid built:
23+
* If you want to build once, run command `npx css-gridish`.
24+
* If you want to add the grid building to your project’s build process:
25+
1. Run command `npm install css-gridish`.
26+
2. Add `scripts: {build: "css-gridish"}` in your `package.json`.
27+
3. Run command `npm run build`.
28+
29+
Your CSS and `README.md` with class documentation will be built into `./css-gridish/`.
30+
31+
### Config file
32+
33+
![CSS Gridish takes design redlines and makes developer-friendly code](/graphics/configDiagram.png?raw=true)
34+
35+
The config file is where all of your design system specs live. See this [example](./examples/carbon/css-gridish.json) for help. Edit your `css-gridish.json` to have all generated grid content match your design system:
36+
37+
```
38+
{
39+
"prefix": "gridish", // Custom prefix for all classes and filenames
40+
"breakpoints": {
41+
// Class name for a breakpoint
42+
"sm": {
43+
"breakpoint": 20, // Min-width for media query (number in rem)
44+
"columns": 4, // Quantity of columns (number)
45+
"gutter": "2rem", // Space between columns (rem string, px string, vw string or 0)
46+
"margin": "3vw" // Horizontal margin of grid container (rem string, px string, vw string or 0)
47+
},
48+
...
49+
"max": {
50+
"breakpoint": 100,
51+
"columns": 12,
52+
"gutter": "4rem",
53+
"margin": "5vw"
54+
}
55+
},
56+
"extraArtboards": {
57+
"xlg": 100 // Additional breakpoint for the Sketch file (number in rem)
58+
}
59+
"rem": 16, // Base rem unit for all measurements (number in px)
60+
"rowHeight": 0.5, // Height of a fixed row (number in rem)
61+
"rows": 30, // Quantity of row variables (number)
62+
"paths": {
63+
"route": "css-gridish", // Route that files save in from project root (optional, use `""` for project root, `"css-gridish"` is default)
64+
"intro": "intro.md" // Path to any markdown you want inserted at the top of your README.md documentation(optional)
65+
}
66+
}
67+
```
68+
69+
**Tip:** For the best results in Sketch, we recommend you make your grid breakpoints, margin, and gutter divisible by the row height.
70+
71+
**Required:** Even if your design specs do not change between breakpoints, you need to list the max-width breakpoint in the `breakpoints` object.
72+
73+
The first breakpoint min-width media query is not used to save kilobytes, but we recommend stating it anyways for future artboard-making tools.
74+
75+
## Legacy support
76+
77+
If you are supporting browsers that lack [CSS Grid Layout support](https://developer.mozilla.org/en-US/docs/Web/CSS/grid#Browser_compatibility), you can use `css-gridish/yourPrefix-legacy.min.css` and the legacy classes detailed in the `README.md`. With the legacy file and classes, the browsers that do not support the final CSS Grid Legacy spec will fallback to a CSS Flexbox alternative. The CSS Flexbox alternative supports embedded subgrids that still reflect the overall grid system’s column structure.
78+
79+
### User-defined breakpoints
80+
81+
One of the best parts about CSS Grid Layout is that your users can rearrange the layout at any width in their own media query. Your grid will also support rearranging layout at custom breakpoints for the legacy implementation when the user compiles their own Sass. Just have them define the following map of rem widths before they import in your Sass file:
82+
83+
```
84+
$extraBreakpoints: (
85+
xsm: 10,
86+
whatever: 78,
87+
superxlarge: 1000,
88+
...
89+
);
90+
@import './css-gridish/scss/yourPrefix-legacy.scss;
91+
```
92+
93+
### Transitioning from Legacy
94+
95+
Once your experience can drop support for browsers like IE 11 and Edge <15, you can simply remove all legacy classes and switch over to the non-legacy files. This is a great progressive-enhancement for your performance when it happens.
96+
97+
## Future Updates
98+
99+
* [ ] Once Edge and Safari gain `display: subgrid` support, we can remove our dependence on `vw` units.
100+
* [ ] Once a solution in the CSS Grid spec is given for [one item to ignore](https://github.com/w3c/csswg-drafts/issues/2117) `grid-gap`, we can utilize `grid-gap` by default instead of opting in to padding classes.
101+
102+
## FAQs
103+
104+
### Why does none of the CSS Grid code use `grid-gap` for gutters?
105+
106+
A lot of times, you will want an item to break out of the gutters for background color, to extend media, or for another reason. Until the CSS Grid spec has a way to ignore that gutter, we use the padding classes (`.yourGrid-padding`) to opt-in to respecting the gutter. The padding classes are always half the size of a gutter for alignment.
107+
108+
### Why are columns using vw units and sometimes the calc function?
109+
110+
Until Edge and Safari support
111+
[`display: subgrid`](https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility),
112+
it will be difficult for you to write semantic HTML with CSS Grid Layout. We are
113+
able to take advantage of vw units and the calc function so you can embed your
114+
`.yourPrefix-grid` class inside of itself as much that is needed for you.
115+
116+
### Why are there no row classes for the legacy implementation?
117+
118+
Thanks to flexbox’s wrapping functionality, nodes that specify rows are not necessary. Only create a node for a row if it has semantic or accessibility significance. You can keep embedding `.yourPrefix-grid` as necessary to accomplish this.
119+
120+
### What happens in the legacy implementation if I specify the column width for one breakpoint, but not the next larger breakpoint?
121+
122+
To maintain a mobile-first opinion, column widths will scale to the next breakpoint if not specified. This means that a `.yourPrefix__col--sm--3` (with 6 total columns) would automatically grow into a `.yourPrefix__col--md--6` (with 12 total columns) if no `md` class was declared to maintain half of the screen size.

bin/index.js

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
4+
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
5+
6+
const gulp = require("gulp"),
7+
cleanCSS = require("gulp-clean-css"),
8+
del = require("del"),
9+
fs = require("fs"),
10+
handlebars = require("handlebars"),
11+
jeditor = require("gulp-json-editor"),
12+
jsonSass = require("json-sass"),
13+
map = require("through2-map"),
14+
rename = require("gulp-rename"),
15+
sass = require("gulp-sass"),
16+
source = require("vinyl-source-stream"),
17+
vinylPaths = require("vinyl-paths"),
18+
zip = require("gulp-zip");
19+
20+
const dirRoot = process.cwd();
21+
const routeConfig = `${dirRoot}/css-gridish.json`;
22+
const config = require(routeConfig);
23+
const intro = config.paths !== undefined && config.paths.intro !== undefined ? fs.readFileSync(`${dirRoot}/${config.paths.intro}`, "utf8") : "";
24+
const route = config.paths !== undefined && config.paths.route !== undefined ? config.paths.route : "css-gridish";
25+
const dirDest = `${dirRoot}/${route}`;
26+
const dirDestCss = `${dirDest}/\css`;
27+
const dirDestDesign = `${__dirname}/css-gridish-design.json`;
28+
const dirDestScss = `${dirDest}/s\css`;
29+
const dirDestSketch = `${dirDest}/sketch`;
30+
const prefix = config.prefix ? config.prefix : "gridish";
31+
32+
const artboard = require(`${__dirname}/src/sketch/artboard.json`);
33+
34+
const parseUnit = function (value, width) {
35+
let parsed = value;
36+
if (value !== 0) {
37+
if (value.includes("vw")) {
38+
parsed = value.slice(0, -2) * width * 0.01;
39+
} else if (value.includes("rem")) {
40+
parsed = value.slice(0, -3) * config.rem;
41+
} else if (value.includes("px")) {
42+
parsed = value.slice(0, -2);
43+
} else if (value.includes("%")) {
44+
parsed = value.slice(0, -1) * width * 0.01;
45+
}
46+
}
47+
return parsed;
48+
};
49+
50+
handlebars.registerHelper("length", function (json) {
51+
return Object.keys(json).length;
52+
});
53+
54+
handlebars.registerHelper("math", function (lvalue, operator, rvalue, options) {
55+
lvalue = parseFloat(lvalue);
56+
rvalue = parseFloat(rvalue);
57+
58+
return {
59+
"+": lvalue + rvalue,
60+
"-": lvalue - rvalue,
61+
"*": lvalue * rvalue,
62+
"/": lvalue / rvalue,
63+
"%": lvalue % rvalue
64+
}[operator];
65+
});
66+
67+
gulp.task("clean", function () {
68+
return del([dirDestCss, dirDestScss, `${dirDest}/${prefix}-grid.sketch`]);
69+
});
70+
71+
gulp.task("css", ["scssRenameLegacy"], function () {
72+
return gulp.src(`${dirDestScss}/${prefix}-grid.s\css`).pipe(sass().on("error", sass.logError)).pipe(rename(`${prefix}-grid.\css`)).pipe(gulp.dest(dirDestCss)).pipe(cleanCSS({
73+
level: 2
74+
})).pipe(rename(`${prefix}-grid.min.\css`)).pipe(gulp.dest(dirDestCss));
75+
});
76+
77+
gulp.task("css-legacy", ["css"], function () {
78+
return gulp.src(`${dirDestScss}/${prefix}-grid-legacy.s\css`).pipe(sass().on("error", sass.logError)).pipe(rename(`${prefix}-grid-legacy.\css`)).pipe(gulp.dest(dirDestCss)).pipe(cleanCSS({
79+
level: 2
80+
})).pipe(rename(`${prefix}-grid-legacy.min.\css`)).pipe(gulp.dest(dirDestCss));
81+
});
82+
83+
gulp.task("docs", ["css-legacy"], function () {
84+
return gulp.src(`${__dirname}/src/docs/*.hbs`).pipe(map.obj(chunk => {
85+
var template = handlebars.compile(chunk.contents.toString());
86+
chunk.contents = new Buffer(template({
87+
config: _extends({}, config, {
88+
classBreakpoints: Object.keys(config.breakpoints).slice(0, -1),
89+
intro
90+
})
91+
}));
92+
return chunk;
93+
})).pipe(rename(path => {
94+
// a template file of the form AAAA.BBB.hbs produces output file AAAA.BBB
95+
var dot = path.basename.lastIndexOf(".");
96+
path.extname = path.basename.substring(dot);
97+
path.basename = path.basename.substring(0, dot);
98+
})).pipe(gulp.dest(dirDest));
99+
});
100+
101+
gulp.task("scss", ["valuesClean"], function () {
102+
return gulp.src(`${__dirname}/src/scss/**/*.s\css`).pipe(gulp.dest(dirDestScss));
103+
});
104+
105+
gulp.task("scssRename", ["scss"], function () {
106+
return gulp.src(`${dirDestScss}/gridish-grid.s\css`).pipe(vinylPaths(del)).pipe(rename(`${prefix}-grid.s\css`)).pipe(gulp.dest(dirDestScss));
107+
});
108+
109+
gulp.task("scssRenameLegacy", ["scssRename"], function () {
110+
return gulp.src(`${dirDestScss}/gridish-grid-legacy.s\css`).pipe(vinylPaths(del)).pipe(rename(`${prefix}-grid-legacy.s\css`)).pipe(gulp.dest(dirDestScss));
111+
});
112+
113+
gulp.task("sketchClean", ["sketchZip"], function () {
114+
return del([dirDestSketch]);
115+
});
116+
117+
gulp.task("sketchFiles", ["docs"], function () {
118+
return gulp.src([`${__dirname}/src/sketch/files/**/*`, `!${__dirname}/src/sketch/files/pages/BC333699-815E-4E1B-9816-9836EDA5B291.json`]).pipe(gulp.dest(dirDestSketch));
119+
});
120+
121+
gulp.task("sketchPage", ["sketchFiles"], function () {
122+
// Add breakpoint values to extra artboards
123+
const originalBreakpoints = config.breakpoints;
124+
let allBreakpoints = originalBreakpoints;
125+
for (let i = 0; i < Object.values(config.extraArtboards).length; i++) {
126+
const name = Object.keys(config.extraArtboards)[i];
127+
const value = Object.values(config.extraArtboards)[i];
128+
let found = false;
129+
for (let j = 0; j < Object.values(originalBreakpoints).length; j++) {
130+
// should catch at max
131+
if (Object.values(originalBreakpoints)[j + 1] !== undefined && Object.values(originalBreakpoints)[j + 1].breakpoint > value && !found) {
132+
allBreakpoints[name] = _extends({}, Object.values(originalBreakpoints)[j], {
133+
breakpoint: value
134+
});
135+
found = true;
136+
} else if (Object.values(originalBreakpoints)[i + 1] === undefined) {
137+
allBreakpoints[name] = _extends({}, Object.values(originalBreakpoints)[i], {
138+
breakpoint: value
139+
});
140+
}
141+
}
142+
}
143+
144+
// Sort all breakpoints by size
145+
let sorted = Object.values(allBreakpoints);
146+
for (let i = 0; i < sorted.length; i++) {
147+
sorted[i] = _extends({}, sorted[i], {
148+
name: Object.keys(allBreakpoints)[i]
149+
});
150+
}
151+
sorted = sorted.sort(function (a, b) {
152+
return a.breakpoint - b.breakpoint;
153+
});
154+
155+
// Make artboards for each breakpoint
156+
let layers = [];
157+
let x = 0;
158+
for (let i = 0; i < sorted.length; i++) {
159+
x = i > 0 ? x + (sorted[i - 1].breakpoint + 1) * config.rem : 0;
160+
const values = sorted[i];
161+
const width = values.breakpoint * config.rem;
162+
const margin = parseUnit(values.margin, width);
163+
const gutter = parseUnit(values.gutter);
164+
const gridWidth = width - margin * 2;
165+
const gutterWidth = gutter;
166+
167+
layers.push(_extends({}, artboard, {
168+
name: `${values.name}-${width}px-${values.columns}`,
169+
do_objectID: artboard.do_objectID.slice(0, -1) + i,
170+
frame: _extends({}, artboard.frame, {
171+
width,
172+
x
173+
}),
174+
grid: _extends({}, artboard.grid, {
175+
gridSize: config.rowHeight * config.rem
176+
}),
177+
layout: _extends({}, artboard.layout, {
178+
columnWidth: (gridWidth - gutterWidth * values.columns) / values.columns,
179+
gutterWidth,
180+
horizontalOffset: margin,
181+
numberOfColumns: values.columns,
182+
totalWidth: gridWidth
183+
})
184+
}));
185+
}
186+
return gulp.src(`${__dirname}/src/sketch/files/pages/BC333699-815E-4E1B-9816-9836EDA5B291.json`).pipe(jeditor({
187+
layers
188+
})).pipe(gulp.dest(`${dirDestSketch}/pages`));
189+
});
190+
191+
gulp.task("sketchZip", ["sketchPage"], function () {
192+
return gulp.src(`${dirDestSketch}/**/*`).pipe(zip(`${prefix}-grid.zip`)).pipe(rename(`${prefix}-grid.sketch`)).pipe(vinylPaths(del)).pipe(gulp.dest(dirDest));
193+
});
194+
195+
gulp.task("values", ["valuesPrep"], function () {
196+
return fs.createReadStream(dirDestDesign).pipe(jsonSass({
197+
prefix: "$grid-values: "
198+
})).pipe(source(routeConfig)).pipe(rename("_values.scss")).pipe(gulp.dest(dirDestScss));
199+
});
200+
201+
gulp.task("valuesClean", ["values"], function () {
202+
return fs.unlinkSync(dirDestDesign);
203+
});
204+
205+
gulp.task("valuesPrep", ["clean"], function () {
206+
return fs.writeFileSync(dirDestDesign, JSON.stringify(_extends({}, config, {
207+
paths: null
208+
})));
209+
});
210+
211+
gulp.task("default", ["sketchClean"], function () {
212+
console.log(`CSS Gridish finished building your ${prefix} grid in ${dirDest}! 🏁`);
213+
});
214+
215+
gulp.start("default");

0 commit comments

Comments
 (0)