├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .gitignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── babel.config.js ├── gulpfile.js ├── package-lock.json ├── package.json ├── src ├── assets │ ├── fabricator │ │ ├── scripts │ │ │ ├── fabricator.js │ │ │ └── prism.js │ │ └── styles │ │ │ ├── fabricator.scss │ │ │ └── partials │ │ │ ├── _code.scss │ │ │ ├── _color-chips.scss │ │ │ ├── _controls.scss │ │ │ ├── _item.scss │ │ │ ├── _layout.scss │ │ │ ├── _menu.scss │ │ │ └── _variables.scss │ └── toolkit │ │ ├── scripts │ │ └── toolkit.js │ │ └── styles │ │ └── toolkit.scss ├── data │ └── toolkit.yml ├── docs │ └── javascript.md ├── favicon.ico ├── materials │ ├── components │ │ ├── button.html │ │ ├── checkbox.html │ │ ├── lists │ │ │ ├── ordered.html │ │ │ └── unordered.html │ │ ├── paragraph.html │ │ ├── radio.html │ │ ├── select.html │ │ ├── table.html │ │ ├── text-input.html │ │ └── textarea.html │ └── structures │ │ └── form.html └── views │ ├── components.html │ ├── docs.html │ ├── index.html │ ├── layouts │ ├── default.html │ └── includes │ │ ├── f-control-bar.html │ │ ├── f-controls.html │ │ ├── f-icons.html │ │ ├── f-item-content.html │ │ ├── f-item-controls.html │ │ ├── f-item.html │ │ └── f-menu.html │ ├── pages.html │ ├── pages │ └── home.html │ └── structures.html └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea 4 | src/assets/fabricator 5 | src/docs 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "plugin:prettier/recommended"], 3 | "rules": { 4 | "arrow-body-style": 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .sass-cache 4 | .idea 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | 4 | node_js: 5 | - "8.14" 6 | 7 | notifications: 8 | webhooks: 9 | on_success: change 10 | on_failure: always 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Luke Askew 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub release](https://img.shields.io/github/release/fbrctr/fabricator.svg)]() 2 | [![Build Status](https://travis-ci.org/fbrctr/fabricator.svg)](https://travis-ci.org/fbrctr/fabricator) 3 | [![devDependency Status](https://david-dm.org/fbrctr/fabricator/dev-status.svg)](https://david-dm.org/fbrctr/fabricator#info=devDependencies) 4 | [![Join the chat at https://gitter.im/fbrctr/fabricator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fbrctr/fabricator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 |

7 | 8 |

9 | 10 | # Fabricator 11 | 12 | > _fabricate_ - to make by assembling parts or sections. 13 | 14 | Fabricator is a tool for building website UI toolkits - _think ["Tiny Bootstraps, for Every Client"](http://daverupert.com/2013/04/responsive-deliverables/#tiny-bootstraps-for-every-client)_ 15 | 16 | ## Quick Start 17 | 18 | ```shell 19 | $ curl -L https://github.com/fbrctr/fabricator/archive/master.tar.gz | tar zx --strip 1 20 | $ npm start 21 | ``` 22 | 23 | ## Documentation 24 | 25 | #### [Read the docs →](http://fbrctr.github.io/docs) 26 | 27 | ## Demo 28 | 29 | #### [Default Fabricator Instance →](http://fbrctr.github.io/demo) 30 | 31 | ## Credits 32 | 33 | Created by [Luke Askew](http://twitter.com/lukeaskew). 34 | 35 | Logo by [Abby Putinski](https://abbyputinski.com/) 36 | 37 | ## License 38 | 39 | [The MIT License (MIT)](http://opensource.org/licenses/mit-license.php) 40 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function babelConfig(api) { 2 | api.cache.using(() => process.env.NODE_ENV === 'development'); 3 | 4 | return { 5 | presets: [ 6 | [ 7 | '@babel/preset-env', 8 | { 9 | modules: false, 10 | useBuiltIns: 'usage', 11 | }, 12 | ], 13 | ], 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const fabAssemble = require('fabricator-assemble'); 2 | const browserSync = require('browser-sync'); 3 | const csso = require('gulp-csso'); 4 | const del = require('del'); 5 | const gulp = require('gulp'); 6 | const argv = require('minimist')(process.argv.slice(2)); 7 | const log = require('fancy-log'); 8 | const gulpif = require('gulp-if'); 9 | const imagemin = require('gulp-imagemin'); 10 | const prefix = require('gulp-autoprefixer'); 11 | const rename = require('gulp-rename'); 12 | const sass = require('gulp-sass'); 13 | const sourcemaps = require('gulp-sourcemaps'); 14 | const webpack = require('webpack'); 15 | sass.compiler = require('node-sass'); 16 | 17 | let server = false; 18 | function reload(done) { 19 | if (server) server.reload(); 20 | done(); 21 | } 22 | 23 | // configuration 24 | const config = { 25 | dev: !!argv.dev, 26 | styles: { 27 | browsers: [ 28 | 'ie 11', 29 | 'edge >= 16', 30 | 'chrome >= 70', 31 | 'firefox >= 63', 32 | 'safari >= 11', 33 | 'iOS >= 12', 34 | 'ChromeAndroid >= 70', 35 | ], 36 | fabricator: { 37 | src: 'src/assets/fabricator/styles/fabricator.scss', 38 | dest: 'dist/assets/fabricator/styles', 39 | watch: 'src/assets/fabricator/styles/**/*.scss', 40 | }, 41 | toolkit: { 42 | src: 'src/assets/toolkit/styles/toolkit.scss', 43 | dest: 'dist/assets/toolkit/styles', 44 | watch: 'src/assets/toolkit/styles/**/*.scss', 45 | }, 46 | }, 47 | scripts: { 48 | fabricator: { 49 | src: './src/assets/fabricator/scripts/fabricator.js', 50 | dest: 'dist/assets/fabricator/scripts', 51 | watch: 'src/assets/fabricator/scripts/**/*', 52 | }, 53 | toolkit: { 54 | src: './src/assets/toolkit/scripts/toolkit.js', 55 | dest: 'dist/assets/toolkit/scripts', 56 | watch: 'src/assets/toolkit/scripts/**/*', 57 | }, 58 | }, 59 | images: { 60 | toolkit: { 61 | src: ['src/assets/toolkit/images/**/*', 'src/favicon.ico'], 62 | dest: 'dist/assets/toolkit/images', 63 | watch: 'src/assets/toolkit/images/**/*', 64 | }, 65 | }, 66 | templates: { 67 | watch: 'src/**/*.{html,md,json,yml}', 68 | }, 69 | dest: 'dist', 70 | }; 71 | 72 | // clean 73 | const clean = () => del([config.dest]); 74 | 75 | // styles 76 | function stylesFabricator() { 77 | return gulp 78 | .src(config.styles.fabricator.src) 79 | .pipe(sourcemaps.init()) 80 | .pipe(sass().on('error', sass.logError)) 81 | .pipe(prefix(config.styles.browsers)) 82 | .pipe(gulpif(!config.dev, csso())) 83 | .pipe(rename('f.css')) 84 | .pipe(sourcemaps.write()) 85 | .pipe(gulp.dest(config.styles.fabricator.dest)); 86 | } 87 | 88 | function stylesToolkit() { 89 | return gulp 90 | .src(config.styles.toolkit.src) 91 | .pipe(gulpif(config.dev, sourcemaps.init())) 92 | .pipe( 93 | sass({ 94 | includePaths: './node_modules', 95 | }).on('error', sass.logError) 96 | ) 97 | .pipe(prefix(config.styles.browsers)) 98 | .pipe(gulpif(!config.dev, csso())) 99 | .pipe(gulpif(config.dev, sourcemaps.write())) 100 | .pipe(gulp.dest(config.styles.toolkit.dest)); 101 | } 102 | 103 | const styles = gulp.parallel(stylesFabricator, stylesToolkit); 104 | 105 | // scripts 106 | const webpackConfig = require('./webpack.config')(config); 107 | 108 | function scripts(done) { 109 | webpack(webpackConfig, (err, stats) => { 110 | if (err) { 111 | log.error(err()); 112 | } 113 | const result = stats.toJson(); 114 | if (result.errors.length) { 115 | result.errors.forEach(error => { 116 | log.error(error); 117 | }); 118 | } 119 | done(); 120 | }); 121 | } 122 | 123 | // images 124 | function imgFavicon() { 125 | return gulp.src('src/favicon.ico').pipe(gulp.dest(config.dest)); 126 | } 127 | 128 | function imgMinification() { 129 | return gulp 130 | .src(config.images.toolkit.src) 131 | .pipe(imagemin()) 132 | .pipe(gulp.dest(config.images.toolkit.dest)); 133 | } 134 | const images = gulp.series(imgFavicon, imgMinification); 135 | 136 | // assembly 137 | function assembler(done) { 138 | fabAssemble({ 139 | logErrors: config.dev, 140 | dest: config.dest, 141 | helpers: { 142 | // {{ default description "string of content used if description var is undefined" }} 143 | default: function defaultFn(...args) { 144 | return args.find(value => !!value); 145 | }, 146 | // {{ concat str1 "string 2" }} 147 | concat: function concat(...args) { 148 | return args.slice(0, args.length - 1).join(''); 149 | }, 150 | // {{> (dynamicPartial name) }} ---- name = 'nameOfComponent' 151 | dynamicPartial: function dynamicPartial(name) { 152 | return name; 153 | }, 154 | eq: function eq(v1, v2) { 155 | return v1 === v2; 156 | }, 157 | ne: function ne(v1, v2) { 158 | return v1 !== v2; 159 | }, 160 | and: function and(v1, v2) { 161 | return v1 && v2; 162 | }, 163 | or: function or(v1, v2) { 164 | return v1 || v2; 165 | }, 166 | not: function not(v1) { 167 | return !v1; 168 | }, 169 | gte: function gte(a, b) { 170 | return +a >= +b; 171 | }, 172 | lte: function lte(a, b) { 173 | return +a <= +b; 174 | }, 175 | plus: function plus(a, b) { 176 | return +a + +b; 177 | }, 178 | minus: function minus(a, b) { 179 | return +a - +b; 180 | }, 181 | divide: function divide(a, b) { 182 | return +a / +b; 183 | }, 184 | multiply: function multiply(a, b) { 185 | return +a * +b; 186 | }, 187 | abs: function abs(a) { 188 | return Math.abs(a); 189 | }, 190 | mod: function mod(a, b) { 191 | return +a % +b; 192 | }, 193 | }, 194 | }); 195 | done(); 196 | } 197 | 198 | // server 199 | function serve(done) { 200 | server = browserSync.create(); 201 | server.init({ 202 | server: { 203 | baseDir: config.dest, 204 | }, 205 | notify: false, 206 | logPrefix: 'FABRICATOR', 207 | }); 208 | done(); 209 | } 210 | 211 | function watch() { 212 | gulp.watch( 213 | config.templates.watch, 214 | { interval: 500 }, 215 | gulp.series(assembler, reload) 216 | ); 217 | gulp.watch( 218 | [config.scripts.fabricator.watch, config.scripts.toolkit.watch], 219 | { interval: 500 }, 220 | gulp.series(scripts, reload) 221 | ); 222 | gulp.watch( 223 | config.images.toolkit.watch, 224 | { interval: 500 }, 225 | gulp.series(images, reload) 226 | ); 227 | gulp.watch( 228 | [config.styles.fabricator.watch, config.styles.toolkit.watch], 229 | { interval: 500 }, 230 | gulp.series(styles, reload) 231 | ); 232 | } 233 | 234 | // default build task 235 | let tasks = [clean, styles, scripts, images, assembler]; 236 | if (config.dev) tasks = tasks.concat([serve, watch]); 237 | gulp.task('default', gulp.series(tasks)); 238 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fabricator", 3 | "version": "2.0.2", 4 | "description": "A tool for building website UI toolkits", 5 | "license": "MIT", 6 | "homepage": "http://fbrctr.github.io/", 7 | "scripts": { 8 | "prestart": "npm install", 9 | "build": "npm run prestart && gulp", 10 | "start": "gulp --dev", 11 | "test": "eslint --fix . && npm run build", 12 | "lint": "eslint ." 13 | }, 14 | "browserslist": [ 15 | "ie 11", 16 | "edge >= 16", 17 | "chrome >= 70", 18 | "firefox >= 63", 19 | "safari >= 11", 20 | "iOS >= 12", 21 | "ChromeAndroid >= 70" 22 | ], 23 | "keywords": [ 24 | "toolkit", 25 | "styleguide", 26 | "framework", 27 | "scaffold", 28 | "patterns", 29 | "library" 30 | ], 31 | "author": "Luke Askew", 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/fbrctr/fabricator.git" 35 | }, 36 | "engines": { 37 | "node": ">=8.14", 38 | "npm": ">=6.4.1" 39 | }, 40 | "devDependencies": { 41 | "@babel/core": "^7.1.6", 42 | "@babel/preset-env": "^7.1.6", 43 | "babel-loader": "^8.0.4", 44 | "browser-sync": "^2.26.5", 45 | "del": "^3.0.0", 46 | "eslint": "^5.9.0", 47 | "eslint-config-airbnb-base": "^13.1.0", 48 | "eslint-config-prettier": "^3.3.0", 49 | "eslint-plugin-import": "^2.14.0", 50 | "eslint-plugin-prettier": "^3.0.0", 51 | "fabricator-assemble": "^1.4.0", 52 | "fancy-log": "^1.3.2", 53 | "gulp": "^4.0.0", 54 | "gulp-autoprefixer": "^6.0.0", 55 | "gulp-csso": "^3.0.1", 56 | "gulp-if": "^2.0.2", 57 | "gulp-imagemin": "^5.0.3", 58 | "gulp-rename": "^1.4.0", 59 | "gulp-sass": "^4.0.2", 60 | "gulp-sourcemaps": "^2.6.4", 61 | "minimist": "^1.2.0", 62 | "node-sass": "^4.12.0", 63 | "prettier": "1.15.2", 64 | "webpack": "^4.26.0" 65 | }, 66 | "dependencies": { 67 | "@babel/polyfill": "^7.0.0" 68 | }, 69 | "optionalDependencies": {} 70 | } 71 | -------------------------------------------------------------------------------- /src/assets/fabricator/scripts/fabricator.js: -------------------------------------------------------------------------------- 1 | require('./prism'); 2 | 3 | /** 4 | * Global `fabricator` object 5 | * @namespace 6 | */ 7 | const fabricator = window.fabricator = {}; 8 | 9 | 10 | /** 11 | * Default options 12 | * @type {Object} 13 | */ 14 | fabricator.options = { 15 | toggles: { 16 | labels: true, 17 | notes: true, 18 | code: false, 19 | }, 20 | menu: false, 21 | mq: '(min-width: 60em)', 22 | }; 23 | 24 | // open menu by default if large screen 25 | fabricator.options.menu = window.matchMedia(fabricator.options.mq).matches; 26 | 27 | /** 28 | * Feature detection 29 | * @type {Object} 30 | */ 31 | fabricator.test = {}; 32 | 33 | // test for sessionStorage 34 | fabricator.test.sessionStorage = (() => { 35 | const test = '_f'; 36 | try { 37 | sessionStorage.setItem(test, test); 38 | sessionStorage.removeItem(test); 39 | return true; 40 | } catch (e) { 41 | return false; 42 | } 43 | })(); 44 | 45 | // create storage object if it doesn't exist; store options 46 | if (fabricator.test.sessionStorage) { 47 | sessionStorage.fabricator = sessionStorage.fabricator || JSON.stringify(fabricator.options); 48 | } 49 | 50 | 51 | /** 52 | * Cache DOM 53 | * @type {Object} 54 | */ 55 | fabricator.dom = { 56 | root: document.querySelector('html'), 57 | primaryMenu: document.querySelector('.f-menu'), 58 | menuItems: document.querySelectorAll('.f-menu li a'), 59 | menuToggle: document.querySelector('.f-menu-toggle'), 60 | }; 61 | 62 | 63 | /** 64 | * Get current option values from session storage 65 | * @return {Object} 66 | */ 67 | fabricator.getOptions = () => { 68 | return (fabricator.test.sessionStorage) ? JSON.parse(sessionStorage.fabricator) : fabricator.options; 69 | }; 70 | 71 | 72 | /** 73 | * Build color chips 74 | */ 75 | fabricator.buildColorChips = () => { 76 | 77 | const chips = document.querySelectorAll('.f-color-chip'); 78 | 79 | for (let i = chips.length - 1; i >= 0; i--) { 80 | const color = chips[i].querySelector('.f-color-chip__color').innerHTML; 81 | chips[i].style.borderTopColor = color; 82 | chips[i].style.borderBottomColor = color; 83 | } 84 | 85 | return fabricator; 86 | 87 | }; 88 | 89 | 90 | /** 91 | * Add `f-active` class to active menu item 92 | */ 93 | fabricator.setActiveItem = () => { 94 | 95 | /** 96 | * Match the window location with the menu item, set menu item as active 97 | */ 98 | const setActive = () => { 99 | 100 | // get current file and hash without first slash 101 | const loc = (window.location.pathname + window.location.hash); 102 | const current = loc.replace(/(^\/)([^#]+)?(#[\w\-\.]+)?$/ig, (match, slash, file, hash) => { 103 | return (file || '') + (hash || '').split('.')[0]; 104 | }) || 'index.html'; 105 | 106 | 107 | // find the current section in the items array 108 | for (let i = fabricator.dom.menuItems.length - 1; i >= 0; i--) { 109 | 110 | const item = fabricator.dom.menuItems[i]; 111 | 112 | // get item href without first slash 113 | const href = item.getAttribute('href').replace(/^\//g, ''); 114 | 115 | if (href === current) { 116 | item.classList.add('f-active'); 117 | } else { 118 | item.classList.remove('f-active'); 119 | } 120 | 121 | } 122 | 123 | }; 124 | 125 | window.addEventListener('hashchange', setActive); 126 | 127 | setActive(); 128 | 129 | return fabricator; 130 | 131 | }; 132 | 133 | 134 | /** 135 | * Click handler to primary menu toggle 136 | * @return {Object} fabricator 137 | */ 138 | fabricator.menuToggle = () => { 139 | 140 | // shortcut menu DOM 141 | const toggle = fabricator.dom.menuToggle; 142 | const options = fabricator.getOptions(); 143 | 144 | // toggle classes on certain elements 145 | const toggleClasses = () => { 146 | options.menu = !fabricator.dom.root.classList.contains('f-menu-active'); 147 | fabricator.dom.root.classList.toggle('f-menu-active'); 148 | 149 | if (fabricator.test.sessionStorage) { 150 | sessionStorage.setItem('fabricator', JSON.stringify(options)); 151 | } 152 | }; 153 | 154 | // toggle classes on ctrl + m press 155 | document.onkeydown = (e) => { 156 | if (e.ctrlKey && e.keyCode === 'M'.charCodeAt(0)) { 157 | toggleClasses(); 158 | } 159 | }; 160 | 161 | // toggle classes on click 162 | toggle.addEventListener('click', () => { 163 | toggleClasses(); 164 | }); 165 | 166 | // close menu when clicking on item (for collapsed menu view) 167 | const closeMenu = () => { 168 | if (!window.matchMedia(fabricator.options.mq).matches) { 169 | toggleClasses(); 170 | } 171 | }; 172 | 173 | for (let i = 0; i < fabricator.dom.menuItems.length; i++) { 174 | fabricator.dom.menuItems[i].addEventListener('click', closeMenu); 175 | } 176 | 177 | return fabricator; 178 | 179 | }; 180 | 181 | 182 | /** 183 | * Handler for preview and code toggles 184 | * @return {Object} fabricator 185 | */ 186 | fabricator.allItemsToggles = () => { 187 | 188 | const itemCache = { 189 | labels: document.querySelectorAll('[data-f-toggle="labels"]'), 190 | notes: document.querySelectorAll('[data-f-toggle="notes"]'), 191 | code: document.querySelectorAll('[data-f-toggle="code"]'), 192 | }; 193 | 194 | const toggleAllControls = document.querySelectorAll('.f-controls [data-f-toggle-control]'); 195 | const options = fabricator.getOptions(); 196 | 197 | // toggle all 198 | const toggleAllItems = (type, value) => { 199 | 200 | const button = document.querySelector(`.f-controls [data-f-toggle-control=${type}]`); 201 | const items = itemCache[type]; 202 | 203 | for (let i = 0; i < items.length; i++) { 204 | if (value) { 205 | items[i].classList.remove('f-item-hidden'); 206 | } else { 207 | items[i].classList.add('f-item-hidden'); 208 | } 209 | } 210 | 211 | // toggle styles 212 | if (value) { 213 | button.classList.add('f-active'); 214 | } else { 215 | button.classList.remove('f-active'); 216 | } 217 | 218 | // update options 219 | options.toggles[type] = value; 220 | 221 | if (fabricator.test.sessionStorage) { 222 | sessionStorage.setItem('fabricator', JSON.stringify(options)); 223 | } 224 | 225 | }; 226 | 227 | for (let i = 0; i < toggleAllControls.length; i++) { 228 | 229 | toggleAllControls[i].addEventListener('click', (e) => { 230 | 231 | // extract info from target node 232 | const type = e.currentTarget.getAttribute('data-f-toggle-control'); 233 | const value = e.currentTarget.className.indexOf('f-active') < 0; 234 | 235 | // toggle the items 236 | toggleAllItems(type, value); 237 | 238 | }); 239 | 240 | } 241 | 242 | // persist toggle options from page to page 243 | Object.keys(options.toggles).forEach((key) => { 244 | toggleAllItems(key, options.toggles[key]); 245 | }); 246 | 247 | return fabricator; 248 | 249 | }; 250 | 251 | 252 | /** 253 | * Handler for single item code toggling 254 | */ 255 | fabricator.singleItemToggle = () => { 256 | 257 | const itemToggleSingle = document.querySelectorAll('.f-item-group [data-f-toggle-control]'); 258 | 259 | // toggle single 260 | const toggleSingleItemCode = (e) => { 261 | const group = e.currentTarget.parentNode.parentNode.parentNode; 262 | const type = e.currentTarget.getAttribute('data-f-toggle-control'); 263 | group.querySelector(`[data-f-toggle=${type}]`).classList.toggle('f-item-hidden'); 264 | }; 265 | 266 | for (let i = 0; i < itemToggleSingle.length; i++) { 267 | itemToggleSingle[i].addEventListener('click', toggleSingleItemCode); 268 | } 269 | 270 | return fabricator; 271 | 272 | }; 273 | 274 | 275 | /** 276 | * Automatically select code when code block is clicked 277 | */ 278 | fabricator.bindCodeAutoSelect = () => { 279 | 280 | const codeBlocks = document.querySelectorAll('.f-item-code'); 281 | 282 | const select = (block) => { 283 | const selection = window.getSelection(); 284 | const range = document.createRange(); 285 | range.selectNodeContents(block.querySelector('code')); 286 | selection.removeAllRanges(); 287 | selection.addRange(range); 288 | }; 289 | 290 | for (let i = codeBlocks.length - 1; i >= 0; i--) { 291 | codeBlocks[i].addEventListener('click', select.bind(this, codeBlocks[i])); 292 | } 293 | 294 | }; 295 | 296 | 297 | /** 298 | * Open/Close menu based on session var. 299 | * Also attach a media query listener to close the menu when resizing to smaller screen. 300 | */ 301 | fabricator.setInitialMenuState = () => { 302 | 303 | // root element 304 | const root = document.querySelector('html'); 305 | 306 | const mq = window.matchMedia(fabricator.options.mq); 307 | 308 | // if small screen 309 | const mediaChangeHandler = (list) => { 310 | if (!list.matches) { 311 | root.classList.remove('f-menu-active'); 312 | } else { 313 | if (fabricator.getOptions().menu) { 314 | root.classList.add('f-menu-active'); 315 | } else { 316 | root.classList.remove('f-menu-active'); 317 | } 318 | } 319 | }; 320 | 321 | mq.addListener(mediaChangeHandler); 322 | mediaChangeHandler(mq); 323 | 324 | return fabricator; 325 | 326 | }; 327 | 328 | 329 | /** 330 | * Initialization 331 | */ 332 | fabricator 333 | .setInitialMenuState() 334 | .menuToggle() 335 | .allItemsToggles() 336 | .singleItemToggle() 337 | .buildColorChips() 338 | .setActiveItem() 339 | .bindCodeAutoSelect(); 340 | -------------------------------------------------------------------------------- /src/assets/fabricator/scripts/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ 2 | self = (typeof window !== 'undefined') 3 | ? window // if in browser 4 | : ( 5 | (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) 6 | ? self // if in worker 7 | : {} // if in node js 8 | ); 9 | 10 | /** 11 | * Prism: Lightweight, robust, elegant syntax highlighting 12 | * MIT license http://www.opensource.org/licenses/mit-license.php/ 13 | * @author Lea Verou http://lea.verou.me 14 | */ 15 | 16 | var Prism = (function(){ 17 | 18 | // Private helper vars 19 | var lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i; 20 | 21 | var _ = self.Prism = { 22 | util: { 23 | encode: function (tokens) { 24 | if (tokens instanceof Token) { 25 | return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias); 26 | } else if (_.util.type(tokens) === 'Array') { 27 | return tokens.map(_.util.encode); 28 | } else { 29 | return tokens.replace(/&/g, '&').replace(/ text.length) { 270 | // Something went terribly wrong, ABORT, ABORT! 271 | break tokenloop; 272 | } 273 | 274 | if (str instanceof Token) { 275 | continue; 276 | } 277 | 278 | pattern.lastIndex = 0; 279 | 280 | var match = pattern.exec(str); 281 | 282 | if (match) { 283 | if(lookbehind) { 284 | lookbehindLength = match[1].length; 285 | } 286 | 287 | var from = match.index - 1 + lookbehindLength, 288 | match = match[0].slice(lookbehindLength), 289 | len = match.length, 290 | to = from + len, 291 | before = str.slice(0, from + 1), 292 | after = str.slice(to + 1); 293 | 294 | var args = [i, 1]; 295 | 296 | if (before) { 297 | args.push(before); 298 | } 299 | 300 | var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias); 301 | 302 | args.push(wrapped); 303 | 304 | if (after) { 305 | args.push(after); 306 | } 307 | 308 | Array.prototype.splice.apply(strarr, args); 309 | } 310 | } 311 | } 312 | } 313 | 314 | return strarr; 315 | }, 316 | 317 | hooks: { 318 | all: {}, 319 | 320 | add: function (name, callback) { 321 | var hooks = _.hooks.all; 322 | 323 | hooks[name] = hooks[name] || []; 324 | 325 | hooks[name].push(callback); 326 | }, 327 | 328 | run: function (name, env) { 329 | var callbacks = _.hooks.all[name]; 330 | 331 | if (!callbacks || !callbacks.length) { 332 | return; 333 | } 334 | 335 | for (var i=0, callback; callback = callbacks[i++];) { 336 | callback(env); 337 | } 338 | } 339 | } 340 | }; 341 | 342 | var Token = _.Token = function(type, content, alias) { 343 | this.type = type; 344 | this.content = content; 345 | this.alias = alias; 346 | }; 347 | 348 | Token.stringify = function(o, language, parent) { 349 | if (typeof o == 'string') { 350 | return o; 351 | } 352 | 353 | if (_.util.type(o) === 'Array') { 354 | return o.map(function(element) { 355 | return Token.stringify(element, language, o); 356 | }).join(''); 357 | } 358 | 359 | var env = { 360 | type: o.type, 361 | content: Token.stringify(o.content, language, parent), 362 | tag: 'span', 363 | classes: ['token', o.type], 364 | attributes: {}, 365 | language: language, 366 | parent: parent 367 | }; 368 | 369 | if (env.type == 'comment') { 370 | env.attributes['spellcheck'] = 'true'; 371 | } 372 | 373 | if (o.alias) { 374 | var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias]; 375 | Array.prototype.push.apply(env.classes, aliases); 376 | } 377 | 378 | _.hooks.run('wrap', env); 379 | 380 | var attributes = ''; 381 | 382 | for (var name in env.attributes) { 383 | attributes += name + '="' + (env.attributes[name] || '') + '"'; 384 | } 385 | 386 | return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + ''; 387 | 388 | }; 389 | 390 | if (!self.document) { 391 | if (!self.addEventListener) { 392 | // in Node.js 393 | return self.Prism; 394 | } 395 | // In worker 396 | self.addEventListener('message', function(evt) { 397 | var message = JSON.parse(evt.data), 398 | lang = message.language, 399 | code = message.code; 400 | 401 | self.postMessage(JSON.stringify(_.util.encode(_.tokenize(code, _.languages[lang])))); 402 | self.close(); 403 | }, false); 404 | 405 | return self.Prism; 406 | } 407 | 408 | // Get current script and highlight 409 | var script = document.getElementsByTagName('script'); 410 | 411 | script = script[script.length - 1]; 412 | 413 | if (script) { 414 | _.filename = script.src; 415 | 416 | if (document.addEventListener && !script.hasAttribute('data-manual')) { 417 | document.addEventListener('DOMContentLoaded', _.highlightAll); 418 | } 419 | } 420 | 421 | return self.Prism; 422 | 423 | })(); 424 | 425 | if (typeof module !== 'undefined' && module.exports) { 426 | module.exports = Prism; 427 | } 428 | ; 429 | Prism.languages.markup = { 430 | 'comment': //, 431 | 'prolog': /<\?.+?\?>/, 432 | 'doctype': //, 433 | 'cdata': //i, 434 | 'tag': { 435 | pattern: /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/i, 436 | inside: { 437 | 'tag': { 438 | pattern: /^<\/?[\w:-]+/i, 439 | inside: { 440 | 'punctuation': /^<\/?/, 441 | 'namespace': /^[\w-]+?:/ 442 | } 443 | }, 444 | 'attr-value': { 445 | pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i, 446 | inside: { 447 | 'punctuation': /=|>|"/ 448 | } 449 | }, 450 | 'punctuation': /\/?>/, 451 | 'attr-name': { 452 | pattern: /[\w:-]+/, 453 | inside: { 454 | 'namespace': /^[\w-]+?:/ 455 | } 456 | } 457 | 458 | } 459 | }, 460 | 'entity': /&#?[\da-z]{1,8};/i 461 | }; 462 | 463 | // Plugin to make entity title show the real entity, idea by Roman Komarov 464 | Prism.hooks.add('wrap', function(env) { 465 | 466 | if (env.type === 'entity') { 467 | env.attributes['title'] = env.content.replace(/&/, '&'); 468 | } 469 | }); 470 | ; 471 | Prism.languages.css = { 472 | 'comment': /\/\*[\w\W]*?\*\//, 473 | 'atrule': { 474 | pattern: /@[\w-]+?.*?(;|(?=\s*\{))/i, 475 | inside: { 476 | 'punctuation': /[;:]/ 477 | } 478 | }, 479 | 'url': /url\((?:(["'])(\\\n|\\?.)*?\1|.*?)\)/i, 480 | 'selector': /[^\{\}\s][^\{\};]*(?=\s*\{)/, 481 | 'string': /("|')(\\\n|\\?.)*?\1/, 482 | 'property': /(\b|\B)[\w-]+(?=\s*:)/i, 483 | 'important': /\B!important\b/i, 484 | 'punctuation': /[\{\};:]/, 485 | 'function': /[-a-z0-9]+(?=\()/i 486 | }; 487 | 488 | if (Prism.languages.markup) { 489 | Prism.languages.insertBefore('markup', 'tag', { 490 | 'style': { 491 | pattern: /[\w\W]*?<\/style>/i, 492 | inside: { 493 | 'tag': { 494 | pattern: /|<\/style>/i, 495 | inside: Prism.languages.markup.tag.inside 496 | }, 497 | rest: Prism.languages.css 498 | }, 499 | alias: 'language-css' 500 | } 501 | }); 502 | 503 | Prism.languages.insertBefore('inside', 'attr-value', { 504 | 'style-attr': { 505 | pattern: /\s*style=("|').*?\1/i, 506 | inside: { 507 | 'attr-name': { 508 | pattern: /^\s*style/i, 509 | inside: Prism.languages.markup.tag.inside 510 | }, 511 | 'punctuation': /^\s*=\s*['"]|['"]\s*$/, 512 | 'attr-value': { 513 | pattern: /.+/i, 514 | inside: Prism.languages.css 515 | } 516 | }, 517 | alias: 'language-css' 518 | } 519 | }, Prism.languages.markup.tag); 520 | }; 521 | Prism.languages.clike = { 522 | 'comment': [ 523 | { 524 | pattern: /(^|[^\\])\/\*[\w\W]*?\*\//, 525 | lookbehind: true 526 | }, 527 | { 528 | pattern: /(^|[^\\:])\/\/.+/, 529 | lookbehind: true 530 | } 531 | ], 532 | 'string': /("|')(\\\n|\\?.)*?\1/, 533 | 'class-name': { 534 | pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i, 535 | lookbehind: true, 536 | inside: { 537 | punctuation: /(\.|\\)/ 538 | } 539 | }, 540 | 'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, 541 | 'boolean': /\b(true|false)\b/, 542 | 'function': { 543 | pattern: /[a-z0-9_]+\(/i, 544 | inside: { 545 | punctuation: /\(/ 546 | } 547 | }, 548 | 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/, 549 | 'operator': /[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/, 550 | 'ignore': /&(lt|gt|amp);/i, 551 | 'punctuation': /[{}[\];(),.:]/ 552 | }; 553 | ; 554 | Prism.languages.javascript = Prism.languages.extend('clike', { 555 | 'keyword': /\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/, 556 | 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/, 557 | 'function': /(?!\d)[a-z0-9_$]+(?=\()/i 558 | }); 559 | 560 | Prism.languages.insertBefore('javascript', 'keyword', { 561 | 'regex': { 562 | pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/, 563 | lookbehind: true 564 | } 565 | }); 566 | 567 | if (Prism.languages.markup) { 568 | Prism.languages.insertBefore('markup', 'tag', { 569 | 'script': { 570 | pattern: /[\w\W]*?<\/script>/i, 571 | inside: { 572 | 'tag': { 573 | pattern: /|<\/script>/i, 574 | inside: Prism.languages.markup.tag.inside 575 | }, 576 | rest: Prism.languages.javascript 577 | }, 578 | alias: 'language-javascript' 579 | } 580 | }); 581 | } 582 | ; 583 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/fabricator.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Fabricator styles 3 | * @author Luke Askew 4 | * Class selectors are namespaced with "f-" 5 | */ 6 | 7 | $settings: ( 8 | theme: light, 9 | accent: hsl(0, 0%, 46%), 10 | menu-width: 14rem 11 | ); 12 | 13 | @import 'partials/variables'; 14 | @import 'partials/code'; 15 | @import 'partials/color-chips'; 16 | @import 'partials/controls'; 17 | @import 'partials/item'; 18 | @import 'partials/layout'; 19 | @import 'partials/menu'; 20 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/partials/_code.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Github-like theme for Prism.js 3 | * @author Luke Askew http://github.com/lukeaskew 4 | */ 5 | 6 | // color vars 7 | $code-colors: ( 8 | blue: #183691, 9 | teal: #0086b3, 10 | black: #333, 11 | purple: #a71d5d, 12 | maroon: #a71d5d, 13 | green: #63a35c, 14 | light-gray: #f7f7f7, 15 | dark-gray: #969896 16 | ); 17 | 18 | 19 | // base 20 | code, 21 | code[class*='language-'], 22 | pre[class*='language-'] { 23 | color: map-get($code-colors, black); 24 | text-align: left; 25 | white-space: pre; 26 | word-spacing: normal; 27 | tab-size: 4; 28 | hyphens: none; 29 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 30 | line-height: 1.4; 31 | direction: ltr; 32 | cursor: text; 33 | letter-spacing: normal; 34 | } 35 | 36 | // code blocks 37 | pre[class*='language-'] { 38 | overflow: auto; 39 | margin: 1em 0; 40 | padding: 1.2em; 41 | border-radius: 3px; 42 | font-size: 85%; 43 | } 44 | 45 | p code, 46 | li code, 47 | table code { 48 | margin: 0; 49 | border-radius: 3px; 50 | padding: 0.2em 0; 51 | font-size: 85%; 52 | 53 | &:before, 54 | &:after { 55 | letter-spacing: -0.2em; 56 | content: '\00a0'; 57 | } 58 | } 59 | 60 | code, 61 | :not(pre) > code[class*='language-'], 62 | pre[class*='language-'] { 63 | background: map-get($code-colors, light-gray); 64 | } 65 | 66 | // inline code 67 | :not(pre) > code[class*='language-'] { 68 | padding: 0.1em; 69 | border-radius: 0.3em; 70 | } 71 | 72 | 73 | // token colors 74 | .token { 75 | 76 | &.comment, 77 | &.prolog, 78 | &.doctype, 79 | &.cdata { 80 | color: map-get($code-colors, dark-gray); 81 | } 82 | 83 | &.punctuation, 84 | &.string, 85 | &.atrule, 86 | &.attr-value { 87 | color: map-get($code-colors, blue); 88 | } 89 | 90 | &.property, 91 | &.tag { 92 | color: map-get($code-colors, green); 93 | } 94 | 95 | &.boolean, 96 | &.number { 97 | color: map-get($code-colors, teal); 98 | } 99 | 100 | &.selector, 101 | &.attr-name, 102 | &.attr-value .punctuation:first-child, 103 | &.keyword, 104 | &.regex, 105 | &.important { 106 | color: map-get($code-colors, maroon); 107 | } 108 | 109 | 110 | &.operator, 111 | &.entity, 112 | &.url, 113 | .language-css &.string { 114 | color: map-get($code-colors, purple); 115 | } 116 | 117 | &.entity { 118 | cursor: help; 119 | } 120 | 121 | } 122 | 123 | .namespace { 124 | opacity: 0.7; 125 | } 126 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/partials/_color-chips.scss: -------------------------------------------------------------------------------- 1 | .f-color-chips { 2 | display: flex; 3 | flex-wrap: wrap; 4 | } 5 | 6 | .f-color-chip { 7 | flex-grow: 1; 8 | flex-shrink: 0; 9 | flex-basis: 100%; 10 | border-top-width: 8em; 11 | border-top-style: solid; 12 | border-bottom-width: 0; 13 | border-bottom-style: solid; 14 | background-color: #fff; 15 | font-weight: 700; 16 | font-size: 0.75em; 17 | padding: 1em; 18 | margin-bottom: 2em; 19 | box-sizing: border-box; 20 | 21 | @media (min-width: 60em) { 22 | flex-basis: 13em; 23 | } 24 | 25 | .f-color-chip__color { 26 | background-color: #fff; 27 | content: attr(data-color); 28 | font-weight: 400; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/partials/_controls.scss: -------------------------------------------------------------------------------- 1 | .f-controls { 2 | @include clearfix; 3 | @include border-box; 4 | 5 | .f-control { 6 | display: block; 7 | float: left; 8 | text-align: center; 9 | width: percentage(1/3); 10 | margin: 0; 11 | padding: 1rem 0; 12 | 13 | &.f-active { 14 | box-shadow: inset 0 0.25rem 0 0 color(accent); 15 | 16 | use { 17 | fill: color(menu-text); 18 | } 19 | } 20 | 21 | svg { 22 | vertical-align: middle; 23 | 24 | use { 25 | fill: if($theme == $theme-dark, color(normal), color(light) ); 26 | } 27 | } 28 | } 29 | } 30 | 31 | .f-control { 32 | @include border-box; 33 | display: inline-block; 34 | cursor: pointer; 35 | margin-left: 0.66rem; 36 | 37 | &:first-child { 38 | margin-left: 0; 39 | } 40 | 41 | svg { 42 | width: 0.875rem; 43 | height: 0.875rem; 44 | 45 | use { 46 | fill: color(light); 47 | } 48 | } 49 | } 50 | 51 | .f-control-bar { 52 | @include clearfix; 53 | padding: 1rem 0; 54 | } 55 | 56 | .f-menu-toggle { 57 | cursor: pointer; 58 | vertical-align: middle; 59 | 60 | svg { 61 | display: block; 62 | float: left; 63 | margin-bottom: -1px; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/partials/_item.scss: -------------------------------------------------------------------------------- 1 | .f-item-group { 2 | @include clearfix; 3 | margin-top: 3rem; 4 | margin-bottom: 3rem; 5 | padding-bottom: 3rem; 6 | border-bottom: 1px solid color(light); 7 | 8 | &:last-child { 9 | border-bottom: 0; 10 | margin-bottom: 0; 11 | } 12 | 13 | & ~ & { 14 | margin-top: 0; 15 | } 16 | 17 | & > & { 18 | padding-bottom: 1.5rem; 19 | margin-bottom: 1.5rem; 20 | border-bottom: 0; 21 | 22 | &:first-of-type { 23 | margin-top: 0; 24 | } 25 | 26 | &:last-child { 27 | margin-bottom: 0; 28 | padding-bottom: 0; 29 | } 30 | } 31 | } 32 | 33 | .f-item-code { 34 | margin-top: 2rem; 35 | } 36 | 37 | .f-item-preview { 38 | @include clearfix; 39 | } 40 | 41 | .f-item-border-bottom { 42 | border-bottom: 1px solid color(light); 43 | } 44 | 45 | .f-item-heading-group { 46 | @include clearfix; 47 | margin-bottom: 2rem; 48 | vertical-align: middle; 49 | } 50 | 51 | .f-item-heading { 52 | margin-top: 0; 53 | margin-bottom: 0; 54 | display: inline-block; 55 | vertical-align: middle; 56 | line-height: 1; 57 | } 58 | 59 | 60 | .f-item-controls { 61 | display: inline-block; 62 | vertical-align: middle; 63 | margin-left: 0.5rem; 64 | 65 | .f-control { 66 | width: 0.875rem; 67 | height: 0.875rem; 68 | display: block; 69 | float: left; 70 | 71 | &:hover { 72 | use { 73 | fill: color(normal); 74 | } 75 | } 76 | 77 | use { 78 | fill: color(light); 79 | } 80 | } 81 | } 82 | 83 | .f-item-hidden { 84 | display: none; 85 | } 86 | 87 | .f-item-notes { 88 | font-size: 0.875rem; 89 | } 90 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/partials/_layout.scss: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | 4 | &.f-menu-active { 5 | overflow: hidden; 6 | 7 | @media (min-width: 60em) { 8 | overflow: auto; 9 | } 10 | } 11 | } 12 | 13 | body { 14 | margin: 0; 15 | position: relative; 16 | 17 | .f-menu-active & { 18 | width:100%; 19 | height: 100%; 20 | overflow: hidden; 21 | 22 | @media (min-width: 60em) { 23 | overflow: auto; 24 | height: auto; 25 | } 26 | } 27 | } 28 | 29 | .f-container { 30 | @include clearfix; 31 | @include border-box; 32 | position: relative; 33 | padding: 0 1em; 34 | z-index: 0; 35 | min-height: 100vh; 36 | 37 | .f-menu-active & { 38 | transform: translate($menu-width, 0); 39 | 40 | @media (min-width: 60em) { 41 | margin-left: $menu-width; 42 | transform: translate(0, 0); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/partials/_menu.scss: -------------------------------------------------------------------------------- 1 | // menu 2 | .f-menu { 3 | @include border-box; 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | transform: translate(-$menu-width, 0); 8 | width: $menu-width; 9 | height: 100%; 10 | z-index: 1; 11 | background-color: color(menu-background); 12 | overflow-x: hidden; 13 | overflow-y: auto; 14 | -webkit-overflow-scrolling: touch; 15 | 16 | .f-menu-active & { 17 | transform: translate(0, 0); 18 | } 19 | 20 | ul { 21 | margin-top: 0; 22 | margin-bottom: 0; 23 | padding-left: 0; 24 | } 25 | 26 | > ul { 27 | margin-top: 0; 28 | margin-bottom: 1rem; 29 | 30 | > li { 31 | margin-top: 1rem; 32 | } 33 | } 34 | 35 | li { 36 | list-style-type: none; 37 | margin-top: 0; 38 | margin-bottom: 0; 39 | } 40 | 41 | a { 42 | display: block; 43 | padding: 0.33rem 2rem; 44 | color: color(menu-text) !important; 45 | text-decoration: none; 46 | font-size: 0.875rem; 47 | line-height: 1.5; 48 | 49 | &:hover { 50 | color: color(menu-text) !important; 51 | text-decoration: underline; 52 | } 53 | 54 | &.f-active { 55 | box-shadow: inset 0.25rem 0 0 0 color(accent); 56 | } 57 | } 58 | 59 | .f-menu__heading { 60 | padding-left: 1.5rem; 61 | font-weight: 700; 62 | font-size: 0.875rem; 63 | 64 | &:hover { 65 | color: color(normal); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/assets/fabricator/styles/partials/_variables.scss: -------------------------------------------------------------------------------- 1 | $base-colors: ( 2 | dark: hsl(0, 0%, 12%), 3 | medium: hsl(0, 0%, 46%), 4 | light: hsl(0, 0%, 80%), 5 | accent: map-get($settings, accent) 6 | ); 7 | 8 | $theme-dark: ( 9 | menu-background: map-get($base-colors, dark), 10 | menu-text: map-get($base-colors, light), 11 | normal: map-get($base-colors, medium) 12 | ); 13 | 14 | $theme-light: ( 15 | menu-background: hsl(0, 0%, 100%), 16 | menu-text: map-get($base-colors, medium), 17 | normal: map-get($base-colors, medium) 18 | ); 19 | 20 | $theme: $theme-light !default; 21 | 22 | @if (map-get($settings, theme) == 'dark') { 23 | $theme: $theme-dark; 24 | } 25 | 26 | $colors: map-merge($base-colors, $theme); 27 | 28 | $menu-width: map-get($settings, menu-width); 29 | 30 | 31 | // functions 32 | @function color($color) { 33 | @return map-get($colors, $color); 34 | } 35 | 36 | 37 | // mixins 38 | @mixin clearfix { 39 | &:after { 40 | clear: both; 41 | } 42 | 43 | &:before, 44 | &:after { 45 | display: table; 46 | content: ' '; 47 | } 48 | } 49 | 50 | @mixin border-box { 51 | box-sizing: border-box; 52 | } 53 | -------------------------------------------------------------------------------- /src/assets/toolkit/scripts/toolkit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Toolkit JavaScript 3 | */ 4 | -------------------------------------------------------------------------------- /src/assets/toolkit/styles/toolkit.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Toolkit styles 3 | */ 4 | 5 | body { 6 | font-family: sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /src/data/toolkit.yml: -------------------------------------------------------------------------------- 1 | colors: 2 | primary: 3 | Dark: "rgb(30, 30, 30)" 4 | Light: "rgb(242, 242, 242)" 5 | secondary: 6 | Red: "rgb(201, 55, 6)" 7 | Yellow: "rgb(252, 207, 80)" 8 | Blue: "rgb(39, 102, 143)" 9 | -------------------------------------------------------------------------------- /src/docs/javascript.md: -------------------------------------------------------------------------------- 1 | This is a markdown document. 2 | 3 | ```javascript 4 | var foo = 'bar' 5 | ``` 6 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbrctr/fabricator/a08488996fb899cf960933b40944e789bfe20405/src/favicon.ico -------------------------------------------------------------------------------- /src/materials/components/button.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/materials/components/checkbox.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/materials/components/lists/ordered.html: -------------------------------------------------------------------------------- 1 |
    2 |
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. 3 |
  3. Aliquam tincidunt mauris eu risus.
  4. 4 |
  5. Vestibulum auctor dapibus neque.
  6. 5 |
-------------------------------------------------------------------------------- /src/materials/components/lists/unordered.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • 3 |
  • Aliquam tincidunt mauris eu risus.
  • 4 |
  • Vestibulum auctor dapibus neque.
  • 5 |
-------------------------------------------------------------------------------- /src/materials/components/paragraph.html: -------------------------------------------------------------------------------- 1 |

Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ullamcorper nulla non metus auctor fringilla. Donec id elit non mi porta gravida at eget metus.

-------------------------------------------------------------------------------- /src/materials/components/radio.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/materials/components/select.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/materials/components/table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
Table Heading 1Table Heading 2Table Heading 3
Table Footer 1Table Footer 2Table Footer 3
Table Cell 1Table Cell 2Table Cell 3
Table Cell 1Table Cell 2Table Cell 3
Table Cell 1Table Cell 2Table Cell 3
Table Cell 1Table Cell 2Table Cell 3
-------------------------------------------------------------------------------- /src/materials/components/text-input.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/materials/components/textarea.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/materials/structures/form.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{> text-input}} 4 |
5 | {{> button}} 6 |
7 | -------------------------------------------------------------------------------- /src/views/components.html: -------------------------------------------------------------------------------- 1 | --- 2 | fabricator: true 3 | --- 4 | 5 |

Components

6 | 7 | {{#each materials.components.items}} 8 | 9 | {{> f-item this}} 10 | 11 | {{/each}} 12 | -------------------------------------------------------------------------------- /src/views/docs.html: -------------------------------------------------------------------------------- 1 | --- 2 | fabricator: true 3 | --- 4 | 5 |

Docs

6 | 7 | {{#each docs}} 8 | 9 |
10 |

{{name}}

11 | {{{content}}} 12 |
13 | 14 | {{/each}} 15 | -------------------------------------------------------------------------------- /src/views/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview | Fabricator 3 | fabricator: true 4 | --- 5 | 6 |

Overview

7 | 8 |

This UI toolkit is a highly-modular design system for rapid web page development. It contains different materials that can be assembled into more complex page layouts.

9 | 10 |

This guide contains real working examples, code snippets, documentation, and style guidelines.

11 | 12 |

Usage

13 | 14 |

Start by adding the following files to your page:

15 | 16 |
<link rel="stylesheet" href="assets/toolkit/styles/toolkit.css">
17 | 18 |
<script src="assets/toolkit/scripts/toolkit.js"></script>
19 | 20 | 21 |

Color

22 | 23 | {{#each toolkit.colors}} 24 |
25 | {{#each this}} 26 |
27 |
{{@key}}
28 |
{{this}}
29 |
30 | {{/each}} 31 |
32 | {{/each}} 33 | 34 |

Typography

35 | 36 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam enim sunt sapiente molestias, sed dicta inventore consectetur beatae asperiores, aliquid laboriosam animi, praesentium repudiandae et, quam saepe sint cupiditate reiciendis.

37 | 38 |

Heading Level 1

39 |

Heading Level 2

40 |

Heading Level 3

41 |

Heading Level 4

42 |
Heading Level 5
43 |
Heading Level 6
44 | -------------------------------------------------------------------------------- /src/views/layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{title}} 10 | 11 | {{#if fabricator}}{{/if}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {{#if fabricator}} 21 | {{> f-icons}} 22 | {{> f-menu}} 23 |
24 | {{> f-control-bar}} 25 | {{/if}} 26 | 27 | 28 | {% body %} 29 | 30 | 31 | {{#if fabricator}} 32 |
33 | 34 | {{/if}} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/views/layouts/includes/f-control-bar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /src/views/layouts/includes/f-controls.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /src/views/layouts/includes/f-icons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/views/layouts/includes/f-item-content.html: -------------------------------------------------------------------------------- 1 | {{#if notes}} 2 |
3 | {{{notes}}} 4 |
5 | {{/if}} 6 |
7 | {{{material @key @root}}} 8 |
9 |
10 |
{{material @key @root}}
11 |
12 | -------------------------------------------------------------------------------- /src/views/layouts/includes/f-item-controls.html: -------------------------------------------------------------------------------- 1 |
2 | {{#if notes}} 3 | 4 | 5 | 6 | 7 | 8 | {{/if}} 9 | 10 | 11 | 12 | 13 | 14 |
15 | -------------------------------------------------------------------------------- /src/views/layouts/includes/f-item.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{#if items}} 4 |
5 |

{{name}}

6 |
7 | {{#each items}} 8 |
9 |
10 |

{{name}}

11 | {{> f-item-controls}} 12 |
{{> f-item-content this}} 13 |
14 | {{/each}} 15 | {{else}} 16 |
17 |

{{name}}

18 | {{> f-item-controls}} 19 |
{{> f-item-content this}} 20 | {{/if}} 21 | 22 |
23 | -------------------------------------------------------------------------------- /src/views/layouts/includes/f-menu.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | {{> f-controls}} 5 | 6 |
    7 | 8 |
  • 9 | Overview 10 |
  • 11 | 12 | {{#each materials}} 13 |
  • 14 | {{name}} 15 |
      16 | {{#each items}} 17 |
    • 18 | {{name}} 19 |
    • 20 | {{/each}} 21 |
    22 |
  • 23 | {{/each}} 24 | 25 | {{#each views}} 26 |
  • 27 | {{name}} 28 |
      29 | {{#each items}} 30 |
    • 31 | {{name}} 32 |
    • 33 | {{/each}} 34 |
    35 |
  • 36 | {{/each}} 37 | 38 |
  • 39 | Docs 40 |
      41 | {{#each docs}} 42 |
    • 43 | {{name}} 44 |
    • 45 | {{/each}} 46 |
    47 |
  • 48 | 49 |
50 | 51 | 52 |
53 | 54 | -------------------------------------------------------------------------------- /src/views/pages.html: -------------------------------------------------------------------------------- 1 | --- 2 | fabricator: true 3 | --- 4 | 5 |

Pages

6 | 7 |
    8 | {{#each views.pages.items}} 9 | 10 |
  • 11 | {{name}} 12 |
  • 13 | 14 | {{/each}} 15 |
16 | -------------------------------------------------------------------------------- /src/views/pages/home.html: -------------------------------------------------------------------------------- 1 |

Hello, World!

2 | 3 | {{> paragraph}} 4 | 5 |
6 | {{> form}} 7 |
8 | -------------------------------------------------------------------------------- /src/views/structures.html: -------------------------------------------------------------------------------- 1 | --- 2 | fabricator: true 3 | --- 4 | 5 |

Structures

6 | 7 | {{#each materials.structures.items}} 8 | 9 | {{> f-item this}} 10 | 11 | {{/each}} 12 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | /** 4 | * Define loaders 5 | * @return {Array} 6 | */ 7 | function getRules() { 8 | return [ 9 | { 10 | test: /(\.js)/, 11 | exclude: /(node_modules)/, 12 | use: { 13 | loader: 'babel-loader', 14 | }, 15 | }, 16 | { 17 | test: /(\.jpg|\.png)$/, 18 | use: [ 19 | { 20 | loader: 'url-loader', 21 | options: { 22 | limit: 10000, 23 | }, 24 | }, 25 | ], 26 | }, 27 | { 28 | test: /\.json/, 29 | loader: 'json-loader', 30 | }, 31 | ]; 32 | } 33 | 34 | module.exports = ({ 35 | dev, 36 | scripts: { 37 | fabricator: { src: fabSrc }, 38 | toolkit: { src: scriptSrc }, 39 | }, 40 | dest, 41 | }) => { 42 | return { 43 | mode: dev ? 'development' : 'production', 44 | entry: { 45 | 'fabricator/scripts/f': fabSrc, 46 | 'toolkit/scripts/toolkit': scriptSrc, 47 | }, 48 | output: { 49 | path: path.resolve(__dirname, dest, 'assets'), 50 | filename: '[name].js', 51 | pathinfo: dev, 52 | }, 53 | devtool: dev ? 'cheap-module-eval-source-map' : false, 54 | module: { 55 | rules: getRules(), 56 | }, 57 | }; 58 | }; 59 | --------------------------------------------------------------------------------