diff --git a/.gitignore b/.gitignore index 123ae94..cf09d83 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,14 @@ coverage # node-waf configuration .lock-wscript -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release +# Compiled files +public/js/main.js +public/css/main.css +public/views # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules + +# Angular config +src/app/token.js diff --git a/assignment-rules.md b/assignment-rules.md deleted file mode 100644 index a5d1eb0..0000000 --- a/assignment-rules.md +++ /dev/null @@ -1,15 +0,0 @@ -# Rules for this assignment - -Please be sure to read carefully as this will be crucial for achieving the goals for this week's assignment project. - -1. I expect everyone to show up or be with their pair partner as soon as possible in the morning to review and start on the project, there is A LOT to do -1. Fork the assignment repo directly, no cross forking this time -1. Once someone has forked the repo, create the PR right away - 1. Give the PR a proper title and place `(wip)` in the title - 1. You can keep making updates to the `master` branch and push that to your repo, the PR will keep updating - 1. DO NOT submit a feature branch to the PR, only submit `master` - 1. Work in your feature branches and commit often until you are ready to merge in the code from the feature branch to `master` and the owner of the -1. In the PR, please be sure to write up a description of the project you are completing and submit the PR - 1. This will be the only PR that is used throughout this week's assignment -1. For the final assignment submission, update the PR title to remove `(wip)` and use `(final)`. Pushes to `master` after the title has been updated will be ignored, make sure you are done before you say you are done. -1. Make sure to submit the URL of the PR to Canvas for final grading diff --git a/comps/intelly-blog-III@2x.psd b/comps/intelly-blog-III@2x.psd deleted file mode 100644 index ebfdb1a..0000000 Binary files a/comps/intelly-blog-III@2x.psd and /dev/null differ diff --git a/comps/intelly-blog-post@2x.psd b/comps/intelly-blog-post@2x.psd deleted file mode 100644 index 0964434..0000000 Binary files a/comps/intelly-blog-post@2x.psd and /dev/null differ diff --git a/gulp/config.js b/gulp/config.js new file mode 100644 index 0000000..47bb6f9 --- /dev/null +++ b/gulp/config.js @@ -0,0 +1,18 @@ +var dest = "./public"; +var src = "./src"; + +module.exports = { + javascript: { + entryPoint: src + "/app/entry.js", + src: src + "/app/**/*.js", + dest: dest + "/js/" + }, + sass: { + src: src + "/sass/**/*.scss", + dest: dest + "/css/" + }, + html: { + src: src + "/app/**/*.html", + dest: dest + "/views/", + }, +}; diff --git a/gulp/tasks/csslint.js b/gulp/tasks/csslint.js new file mode 100644 index 0000000..4c02e9d --- /dev/null +++ b/gulp/tasks/csslint.js @@ -0,0 +1,9 @@ +var gulp = require("gulp"); +var config = require("../config").sass; +var csslint = require("gulp-csslint"); + +gulp.task("csslint", function () { + return gulp.src(config.dest + "**/*.css") + .pipe(csslint()) + .pipe(csslint.reporter()); +}); diff --git a/gulp/tasks/default.js b/gulp/tasks/default.js new file mode 100644 index 0000000..36c9f55 --- /dev/null +++ b/gulp/tasks/default.js @@ -0,0 +1,3 @@ +var gulp = require("gulp"); + +gulp.task("default", ["sass", "html", "webpack", "watch", "serve", "jshint"]); diff --git a/gulp/tasks/html.js b/gulp/tasks/html.js new file mode 100644 index 0000000..0b29aff --- /dev/null +++ b/gulp/tasks/html.js @@ -0,0 +1,7 @@ +var gulp = require("gulp"); +var config = require('../config').html; + +gulp.task('html', function() { + return gulp.src(config.src) + .pipe(gulp.dest(config.dest)); +}); diff --git a/gulp/tasks/jshint.js b/gulp/tasks/jshint.js new file mode 100644 index 0000000..8958d19 --- /dev/null +++ b/gulp/tasks/jshint.js @@ -0,0 +1,10 @@ +var gulp = require("gulp"); +var config = require("../config").javascript; +var jshint = require("gulp-jshint"); +var stylish = require("jshint-stylish"); + +gulp.task("jshint", function () { + return gulp.src(config.src) + .pipe(jshint()) + .pipe(jshint.reporter(stylish)); +}); diff --git a/gulp/tasks/sass.js b/gulp/tasks/sass.js new file mode 100644 index 0000000..6263a79 --- /dev/null +++ b/gulp/tasks/sass.js @@ -0,0 +1,14 @@ +var gulp = require('gulp'); +var sass = require('gulp-sass'); +var sourcemaps = require('gulp-sourcemaps'); +var handleErrors = require('../util/handleErrors'); +var config = require('../config').sass; + +gulp.task('sass', function () { + return gulp.src(config.src) + .pipe(sourcemaps.init()) + .pipe(sass()) + .on('error', handleErrors) + .pipe(sourcemaps.write()) + .pipe(gulp.dest(config.dest)); +}); diff --git a/gulp/tasks/serve.js b/gulp/tasks/serve.js new file mode 100644 index 0000000..0943cdb --- /dev/null +++ b/gulp/tasks/serve.js @@ -0,0 +1,7 @@ +var gulp = require("gulp"); +var server = require("gulp-express"); + +gulp.task("serve", function () { + server.run(["server.js"]); + gulp.watch(["server.js"], [server.run]); +}); diff --git a/gulp/tasks/watch.js b/gulp/tasks/watch.js new file mode 100644 index 0000000..47706af --- /dev/null +++ b/gulp/tasks/watch.js @@ -0,0 +1,9 @@ +var gulp = require("gulp"); +var config = require("../config"); + +gulp.task('watch', function() { + gulp.watch(config.html.src, ["html"]); + gulp.watch(config.javascript.src, ["webpack", "jshint"]); + gulp.watch(config.sass.src, ["sass"]); + // gulp.watch(config.sass.dest + "**/*.css", ["csslint"]); +}); diff --git a/gulp/tasks/webpack.js b/gulp/tasks/webpack.js new file mode 100644 index 0000000..12fa06a --- /dev/null +++ b/gulp/tasks/webpack.js @@ -0,0 +1,13 @@ +var gulp = require("gulp"); +var config = require("../config").javascript; +var webpack = require("webpack-stream"); + +gulp.task("webpack", function (callback) { + return gulp.src(config.entryPoint) + .pipe(webpack({ + output: { + filename: "main.js", + } + })) + .pipe(gulp.dest(config.dest)); +}); diff --git a/gulp/util/handleErrors.js b/gulp/util/handleErrors.js new file mode 100644 index 0000000..3c65f4f --- /dev/null +++ b/gulp/util/handleErrors.js @@ -0,0 +1,15 @@ +var notify = require("gulp-notify"); + +module.exports = function () { + + var args = Array.prototype.slice.call(arguments); + + // Send error to notification center with gulp-notify + notify.onError({ + title: "Compile Error", + message: "<%= error %>" + }).apply(this, args); + + // Keep gulp from hanging on this task + this.emit("end"); +}; diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..1dbda05 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,19 @@ +/* + gulpfile.js + =========== + Rather than manage one giant configuration file responsible + for creating multiple tasks, each task has been broken out into + its own file in gulp/tasks. Any files in that directory get + automatically required below. + + To add a new task, simply add a new task file that directory. + gulp/tasks/default.js specifies the default set of tasks to run + when you run `gulp`. +*/ + +var requireDir = require('require-dir'); + +// Require all tasks in gulp/tasks, including subfolders +requireDir('./gulp/tasks', { recurse: true }); + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..7feb6e1 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "mean-stack-1", + "version": "1.0.0", + "description": "Intelly - a simple MEAN stack blog app", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/hollislau/mean-stack-1.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/hollislau/mean-stack-1/issues" + }, + "homepage": "https://github.com/hollislau/mean-stack-1", + "dependencies": { + "angular": "^1.4.7", + "angular-route": "^1.4.7", + "body-parser": "^1.14.1", + "express": "^4.13.3", + "gulp": "^3.9.0", + "gulp-csslint": "^0.2.0", + "gulp-express": "^0.3.5", + "gulp-jshint": "^1.11.2", + "gulp-notify": "^2.2.0", + "gulp-sass": "^2.0.4", + "gulp-sourcemaps": "^1.6.0", + "jshint-stylish": "^2.0.1", + "require-dir": "^0.3.0", + "webpack-stream": "^2.1.1" + } +} diff --git a/public/css/fonts/icomoon.eot b/public/css/fonts/icomoon.eot new file mode 100644 index 0000000..2761603 Binary files /dev/null and b/public/css/fonts/icomoon.eot differ diff --git a/public/css/fonts/icomoon.svg b/public/css/fonts/icomoon.svg new file mode 100644 index 0000000..ec48438 --- /dev/null +++ b/public/css/fonts/icomoon.svg @@ -0,0 +1,16 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + \ No newline at end of file diff --git a/public/css/fonts/icomoon.ttf b/public/css/fonts/icomoon.ttf new file mode 100644 index 0000000..e8bc90d Binary files /dev/null and b/public/css/fonts/icomoon.ttf differ diff --git a/public/css/fonts/icomoon.woff b/public/css/fonts/icomoon.woff new file mode 100644 index 0000000..efeebf8 Binary files /dev/null and b/public/css/fonts/icomoon.woff differ diff --git a/readme.md b/readme.md index dde636c..68eed07 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,9 @@ +# NOTE TO INSTRUCTORS FOR GRADING: + +You will need Hollis' Github API token to make this app work properly. Please refer to the comments attached to his Canvas assignment to find it. Create a "token.js" file in the "src" directory and create a module.exports object with a property and value of "oauthToken" and the API key, respectively. Just run "npm install" followed by "gulp", and you should now be able to CRUD some sample blogs. Enjoy! + + + # Week Six - MEAN Stack I Last week we focussed on JavaScript basics. This week we're going to build up a function web application. You'll be expected to build on knowledge you've already learned (semantic HTML, Sass instead of CSS, in addition to all of the Angular work we'll be doing) and produce a "production-ready" blog for your assignment this week. @@ -11,15 +17,15 @@ You can download the view that you are to complete [here](https://github.com/SEA ## The spec -As a product owner, I need a functional prototype of the application. The final prototype must be visible in a desktop browser. +As a product owner, I need a functional prototype of the application. The final prototype must be visible in a desktop browser. -Prototype is to use best practices in coding HTML/CSS as there is the potential that some or all of the code will made it into production. +Prototype is to use best practices in coding HTML/CSS as there is the potential that some or all of the code will made it into production. -Aside from images as content, all visual assets should be produced via CSS or typography. The only background image asset allowed will be the main page header. +Aside from images as content, all visual assets should be produced via CSS or typography. The only background image asset allowed will be the main page header. The user should be able to create, read, update, and delete blog posts from the application. There should be at least two different views - one a list of available blog posts, the other a detail view of the blog post. You do not need to worry about authentication or user management for this assignment. -### Requirements +### Requirements 1. Semantically correct HTML is required as this will be the model for prod app integration 1. Think in terms of '*components*'; if all parts of the UI were lego blocks, who would you code that? @@ -30,15 +36,15 @@ The user should be able to create, read, update, and delete blog posts from the 1. Write up a description for every plugin used (no limit, but you must justify them) 1. jQuery is not allowed. Angular's built-in DOM manipulation can manage most of what you'd need jQuery for. -### Constraints +### Constraints 1. Must work in all major browsers of latest versions; * Desktop (IE Edge, Safari, Chrome and Firefox) 1. All interactions must be clearly functional 1. All code must pass HTML Tidy, CSS Lint, and JSHint. -__DO NOT__ fence yourself in with invisible constraints. Unless it is specifically listed and/or we discussed it in lecture, there is not an expectation to meet an objective that has not been set. +__DO NOT__ fence yourself in with invisible constraints. Unless it is specifically listed and/or we discussed it in lecture, there is not an expectation to meet an objective that has not been set. -## The expectation +## The expectation In this assignment, you should be able to demonstrate mastery over the basics of Angular as well as creating a simple CRUD application. We're looking for you to build on best practices that you've already learned (proper HTML and CSS) as well as incorporate the new practices discussed during the course of the week. diff --git a/server.js b/server.js new file mode 100644 index 0000000..867dc97 --- /dev/null +++ b/server.js @@ -0,0 +1,18 @@ +"use strict"; + +var express = require("express"); +var app = express(); +var router = express.Router(); + +app.set("port", (process.env.PORT || 5000)); + +router.get("/", function (req, res) { + res.sendFile(__dirname + "/public/views/index.html"); +}); + +app.use(express.static(__dirname + "/public")); +app.use("/", router); + +app.listen(app.get("port"), function() { + console.log("Express server is running on port", app.get("port")); +}); diff --git a/src/app/app.js b/src/app/app.js new file mode 100644 index 0000000..4f95155 --- /dev/null +++ b/src/app/app.js @@ -0,0 +1,32 @@ +"use strict"; + +require("angular"); +require("angular-route"); + +(function () { + + var app = angular.module("intellyBlog", ["ngRoute"]); + + app.config(["$routeProvider", function ($routeProvider) { + $routeProvider.when("/blogs", { + templateUrl: "views/blogs/blogs_list.html", + controller: "BlogsCtrl as vm", + }) + .when("/blogs/new", { + templateUrl: "views/blog/blog_form.html", + controller: "BlogFormCtrl as vm", + }) + .when("/blogs/:blog_id/edit", { + templateUrl: "views/blog/blog_form.html", + controller: "BlogFormCtrl as vm", + }) + .when("/blogs/:blog_id", { + templateUrl: "views/blog/blog_detail.html", + controller: "BlogCtrl as vm", + }) + .otherwise({ + redirectTo: "/blogs", + }); + }]); + +})(); diff --git a/src/app/blog/blog.ctrl.js b/src/app/blog/blog.ctrl.js new file mode 100644 index 0000000..6a2836a --- /dev/null +++ b/src/app/blog/blog.ctrl.js @@ -0,0 +1,62 @@ +"use strict"; + +require("../app.js"); + +(function () { + + angular.module("intellyBlog").controller("BlogCtrl", ["BlogsService", "$routeParams", "$http", "$filter", "$log", "$q", "$location", function (BlogsService, $routeParams, $http, $filter, $log, $q, $location) { + var vm = this; + + vm.delete = deleteBlog; + + initialize(); + + function initialize() { + BlogsService + .get($routeParams.blog_id) + .then(successHandler, errorHandler); + } + + function successHandler (resp) { + var data = resp.data; + var blogObj = data.files.blog; + + $http + .get(blogObj.raw_url) + .then(function (resp) { + setBlogInfo(data, resp.data); + }, function (resp) { + $log.error("Could not request " + blogObj.raw_url, resp); + }); + + $log.info("read", resp); + } + + function errorHandler (resp) { + vm.error = resp.data; + $log.error("Could not read " + resp); + } + + function setBlogInfo (gist, content) { + vm.blog = { + title: gist.description, + content: content, + author: gist.owner.login, + date: $filter("date")(gist.updated_at), + comments: gist.comments + " comments", + id: gist.id + }; + } + + function deleteBlog (blog) { + BlogsService.delete(blog).then(function (resp) { + $location.path("/blogs"); + $log.info("deleted", resp); + }, function (resp) { + $log.error("Could not delete " + resp); + }); + } + + }]); + +})(); diff --git a/src/app/blog/blog_detail.html b/src/app/blog/blog_detail.html new file mode 100644 index 0000000..56701e6 --- /dev/null +++ b/src/app/blog/blog_detail.html @@ -0,0 +1,15 @@ +
+
+

{{vm.blog.title}}

+

{{vm.blog.content}}

+ +
+
diff --git a/src/app/blog/blog_form.ctrl.js b/src/app/blog/blog_form.ctrl.js new file mode 100644 index 0000000..3ba8dc3 --- /dev/null +++ b/src/app/blog/blog_form.ctrl.js @@ -0,0 +1,99 @@ +"use strict"; + +require("../app.js"); + +(function () { + + angular.module("intellyBlog").controller("BlogFormCtrl", ["BlogsService", "$routeParams", "$http", "$log", "$q", "$location", "$filter", function (BlogsService, $routeParams, $http, $log, $q, $location, $filter) { + var vm = this; + + vm.save = saveForm; + + vm.blog = {}; + + initialize(); + + function initialize() { + if ($routeParams.blog_id) { + BlogsService + .get($routeParams.blog_id) + .then(successHandler, errorHandler); + } + } + + function successHandler (resp) { + var data = resp.data; + var blogObj = data.files.blog; + + $http + .get(blogObj.raw_url) + .then(function (resp) { + setBlogInfo(data, resp); + }, function (resp) { + $log.error("Could not request " + blogObj.raw_url, resp); + }); + + $log.info("response", resp); + } + + function errorHandler (resp) { + vm.error = resp.data; + $log.error("response", resp); + } + + function setBlogInfo (gist, resp) { + vm.blog = { + title: gist.description, + content: resp.data, + author: gist.owner.login, + date: $filter("date")(gist.updated_at), + comments: gist.comments + " comments", + id: gist.id + }; + } + + function saveForm () { + var mode; + var updateInfo = { + id: vm.blog.id, + method: "update", + successMsg: "updated", + errorMsg: "Could not update ", + model: { + "description": vm.blog.title, + "files": { + "blog": { + "content": vm.blog.content + } + } + } + }; + var createInfo = { + method: "create", + successMsg: "created", + errorMsg: "Could not create ", + model: { + "description": vm.blog.title, + "public": true, + "files": { + "blog": { + "content": vm.blog.content + } + } + } + }; + + mode = $routeParams.blog_id ? updateInfo : createInfo; + + console.log(mode.id, mode.model); + + BlogsService[mode.method](mode).then(function (resp) { + $location.path("/blogs/" + resp.data.id); + $log.info(mode.successMsg, resp); + }, function (resp) { + $log.error(mode.errorMsg + resp); + }); + } + }]); + +})(); diff --git a/src/app/blog/blog_form.html b/src/app/blog/blog_form.html new file mode 100644 index 0000000..f6dd7b5 --- /dev/null +++ b/src/app/blog/blog_form.html @@ -0,0 +1,9 @@ +
+
+ + + +
+
diff --git a/src/app/blogs/blogs.ctrl.js b/src/app/blogs/blogs.ctrl.js new file mode 100644 index 0000000..133f3b0 --- /dev/null +++ b/src/app/blogs/blogs.ctrl.js @@ -0,0 +1,74 @@ +"use strict"; + +require("../app.js"); +require("../filters/pager.filter.js"); + +(function () { + + angular.module("intellyBlog").controller("BlogsCtrl", ["BlogsService", "$http", "$filter", "$log", "$q", function (BlogsService, $http, $filter, $log, $q) { + var vm = this; + + vm.blogs = []; + + vm.pagination = { + currentPage: 0, + perPage: 4, + getOffset: function () { + return vm.pagination.currentPage * vm.pagination.perPage; + }, + prevPage: function () { + vm.pagination.currentPage--; + }, + nextPage: function () { + vm.pagination.currentPage++; + } + }; + + initialize(); + + function initialize () { + BlogsService + .get() + .then(successHandler, errorHandler); + } + + function successHandler (resp) { + var data = resp.data; + + angular.forEach(data, function(gist) { + var blogObj = gist.files.blog; + + if (angular.isDefined(blogObj)) { + $http + .get(blogObj.raw_url) + .then(function (resp) { + setBlogInfo(gist, resp.data); + }, function (resp) { + $log.error("Could not request " + blogObj.raw_url, resp); + }); + } + }); + + $log.info("read", resp); + } + + function errorHandler (resp) { + vm.error = resp.data; + $log.error("Could not read " + resp); + } + + function setBlogInfo (gist, content) { + var blog = { + title: gist.description, + content: content, + author: gist.owner.login, + date: $filter("date")(gist.updated_at), + comments: gist.comments + " comments", + id: gist.id + }; + vm.blogs.push(blog); + } + + }]); + +})(); diff --git a/src/app/blogs/blogs.service.js b/src/app/blogs/blogs.service.js new file mode 100644 index 0000000..7d6cfb8 --- /dev/null +++ b/src/app/blogs/blogs.service.js @@ -0,0 +1,44 @@ +"use strict"; + +require("../app.js"); + +var token = require("../token.js"); +var rootUrl = "https://api.github.com"; +var headerAuth = {"Authorization": "token " + token.oauthToken}; + +(function () { + + angular.module("intellyBlog").service("BlogsService", ["$http", function ($http) { + + var Blog = { + get: function (id) { + if (angular.isDefined(id)) { + return $http.get(rootUrl + "/gists/" + id, { + headers: headerAuth + }); + } else { + return $http.get(rootUrl + "/users/hollislau/gists", { + headers: headerAuth + }); + } + }, + update: function (model) { + return $http.patch(rootUrl + "/gists/" + model.id, model.model, { + headers: headerAuth + }); + }, + create: function (model) { + return $http.post(rootUrl + "/gists", model.model, { + headers: headerAuth + }); + }, + delete: function (model) { + return $http.delete(rootUrl + "/gists/" + model.id, { + headers: headerAuth + }); + } + }; + return Blog; + }]); + +})(); diff --git a/src/app/blogs/blogs_list.html b/src/app/blogs/blogs_list.html new file mode 100644 index 0000000..37a8ba4 --- /dev/null +++ b/src/app/blogs/blogs_list.html @@ -0,0 +1,25 @@ +
+
+ Prev + Next +
+
+

{{blog.title}}

+

{{blog.content}}

+ +
+
+ Add Post +
+
+ Prev + Next +
+
diff --git a/src/app/entry.js b/src/app/entry.js new file mode 100644 index 0000000..661c4d3 --- /dev/null +++ b/src/app/entry.js @@ -0,0 +1,5 @@ +require("./app.js"); +require("./blog/blog.ctrl.js"); +require("./blog/blog_form.ctrl.js"); +require("./blogs/blogs.ctrl.js"); +require("./blogs/blogs.service.js"); diff --git a/src/app/filters/offset.filter.js b/src/app/filters/offset.filter.js new file mode 100644 index 0000000..fdfc389 --- /dev/null +++ b/src/app/filters/offset.filter.js @@ -0,0 +1,11 @@ +require("../app.js"); + +angular.module("intellyBlog").filter("offset", ["$filter", function ($filter) { + return function (input, start) { + if (input) { + start = parseInt(start, 10); + console.log(input.slice(start)); + return input.slice(start); + } + }; +}]); diff --git a/src/app/filters/pager.filter.js b/src/app/filters/pager.filter.js new file mode 100644 index 0000000..e6d87d3 --- /dev/null +++ b/src/app/filters/pager.filter.js @@ -0,0 +1,12 @@ +require("../app.js"); +require("./offset.filter.js"); + +angular.module("intellyBlog").filter("pager", ["$filter", function ($filter) { + return function (results, pagerObj) { + var filteredResults; + + filteredResults = $filter("offset")(results, pagerObj.getOffset()); + filteredResults = $filter("limitTo")(filteredResults, pagerObj.perPage); + return filteredResults; + }; +}]); diff --git a/src/app/index.html b/src/app/index.html new file mode 100644 index 0000000..cf8958b --- /dev/null +++ b/src/app/index.html @@ -0,0 +1,95 @@ + + + + Blog + + + + + + + + + +
+
+

Blog

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque aliquam, ligula ut lacinia sollicitudin, orci ex mollis ante.

+
+
+ +
+ +
+
+ +
+ + +
+ + + + + diff --git a/src/sass/base/_INDEX.scss b/src/sass/base/_INDEX.scss new file mode 100644 index 0000000..f284713 --- /dev/null +++ b/src/sass/base/_INDEX.scss @@ -0,0 +1,3 @@ +@import "reset"; +@import "typography"; +@import "base"; diff --git a/src/sass/base/_base.scss b/src/sass/base/_base.scss new file mode 100644 index 0000000..c632e59 --- /dev/null +++ b/src/sass/base/_base.scss @@ -0,0 +1,3 @@ +body { + font-family: $primary-font; +} diff --git a/src/sass/base/_reset.scss b/src/sass/base/_reset.scss new file mode 100644 index 0000000..e79ed65 --- /dev/null +++ b/src/sass/base/_reset.scss @@ -0,0 +1,23 @@ +html { + box-sizing: border-box; +} + +*, *:before, *:after { + box-sizing: inherit; +} + +body { + width: 100%; +} + +h1, h2, ul, li, p, a, address { + margin: 0; + padding: 0; + font-weight: normal; + text-decoration: none; +} + +li { + list-style: none; +} + diff --git a/src/sass/base/_typography.scss b/src/sass/base/_typography.scss new file mode 100644 index 0000000..d58af41 --- /dev/null +++ b/src/sass/base/_typography.scss @@ -0,0 +1,11 @@ +@font-face { + font-family: 'intelly-icons'; + src:url('fonts/icomoon.eot?ckurm8'); + src:url('fonts/icomoon.eot?ckurm8#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?ckurm8') format('truetype'), + url('fonts/icomoon.woff?ckurm8') format('woff'), + url('fonts/icomoon.svg?ckurm8#icomoon') format('svg'); + font-weight: normal; + font-style: normal; +} + diff --git a/src/sass/components/_INDEX.scss b/src/sass/components/_INDEX.scss new file mode 100644 index 0000000..c8633a5 --- /dev/null +++ b/src/sass/components/_INDEX.scss @@ -0,0 +1,5 @@ +@import "social-share"; +@import "social-contact"; +@import "content-header"; +@import "content-posts"; +@import "content-footer"; diff --git a/src/sass/components/_content-footer.scss b/src/sass/components/_content-footer.scss new file mode 100644 index 0000000..866f2d3 --- /dev/null +++ b/src/sass/components/_content-footer.scss @@ -0,0 +1,119 @@ +.contact .button { + @include button($color-low-contrast, $color-highlight, $font-smaller, $color-high-contrast, $color-background); + margin-left: 5%; + margin-right: 5%; +} + +.contact { + border-top: 1px solid $color-low-contrast; + & h3 { + @include set-font ($color-background, $font-largest, $font-medium); + @media (max-width: 500px) { + font-size: $font-large; + } + } + & p { + @include set-font ($color-background, $font-small); + } + & div { + padding-bottom: 5%; + } +} + +.links { + padding-bottom: 5%; + width: auto; + margin-left: auto; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; +} + +.contact-info { + @include set-font ($color-high-contrast, $font-normal); + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + min-width: 58%; + -webkit-box-align: end; + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + & div { + margin: 0 3% 0 3%; + } + & address { + min-width: 30%; + font-style: normal; + } + @media (max-width: 500px) { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + & div, & address { + width: 100%; + margin: 5%; + } + } +} + +.social-media-contact { + width: 38%; +} + +.small-print { + @include set-font ($color-high-contrast, $font-tiny); + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + padding: 0.25% 30% 0.25% 30%; + border-top: 1px solid $color-low-contrast; + & a, & p { + -webkit-box-flex: 0.01; + -webkit-flex-grow: 0.01; + -ms-flex-positive: 0.01; + flex-grow: 0.01; + padding: 0 2% 0 2%; + } + & p { + text-transform: capitalize; + } + @media (max-width: 500px) { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + & a, & p { + padding-top: 2%; + } + } +} diff --git a/src/sass/components/_content-header.scss b/src/sass/components/_content-header.scss new file mode 100644 index 0000000..09f86ab --- /dev/null +++ b/src/sass/components/_content-header.scss @@ -0,0 +1,64 @@ +.site-nav { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + list-style: none; + -webkit-transition: color 0.5s, background-color 0.5s; + transition: color 0.5s, background-color 0.5s; + & a { + padding: 0 5% 0 5%; + } + & span { + padding: 0 13% 0 13%; + } + & a:visited { + color: $color-low-contrast; + } + @media (max-width: 500px) { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + & span { + display: none; + } + } +} + +.content { + & header, & footer { + background-color: $color-highlight; + } + & header { + & h1 { + @include set-font ($color-high-contrast, $font-huge, $font-regular, $secondary-font); + } + & h2 { + @include set-font ($color-highlight-contrast, $font-larger, $font-light, $secondary-font); + } + } + & .site-nav a { + @include set-font ($color-high-contrast, $font-smallest, $font-bold, auto, uppercase); + } + & .site-nav a:hover{ + background-color: $color-high-contrast; + color: $color-background; + } + @media (max-width: 500px) { + & header { + & h1 { + font-size: $font-larger; + } + & h2 { + font-size: $font-large; + } + } + } +} \ No newline at end of file diff --git a/src/sass/components/_content-posts.scss b/src/sass/components/_content-posts.scss new file mode 100644 index 0000000..c1da10d --- /dev/null +++ b/src/sass/components/_content-posts.scss @@ -0,0 +1,102 @@ +.posts { + padding-left: 20%; + padding-right: 20%; + & .title { + @include set-font ($color-high-contrast, $font-largest, $font-medium); + } + & .copy { + @include set-font ($color-low-contrast, $font-large, $font-light); + } + & article { + padding-top: 7%; + } + & .button { + @include button($color-background, $color-high-contrast, $font-smallest); + min-width: 25%; + line-height: 2; + } + & input, & textarea { + border: 0; + } + & textarea, & .title { + width: 100%; + } + & textarea { + height: 5em; + } + @media (max-width: 500px) { + & .title { + font-size: $font-larger; + } + & .copy { + font-size: $font-large; + } + } +} + +.l-pagination-button { + width: 30%; + height: 4em; + display: flex; + flex-direction: row; + justify-content: space-between; + text-align: center; + align-items: center; + & .l-pagination-button-prev { + margin-right: auto; + min-width: 35%; + min-height: 50%; + line-height: 2; + } + & .l-pagination-button-next { + margin-left: auto; + min-width: 35%; + min-height: 50%; + line-height: 2; + } +} + +.blog-post-footer { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + height: 5em; + width: 100%; + padding-bottom: 5%; + border-bottom: 1px solid $color-low-contrast; + @media (max-width: 500px) { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } + @media (max-width: 500px) { + height: 8em; + } +} + +.credits { + @include set-font ($color-low-contrast, $font-small, $font-light); + font-style: italic; +} + +.l-credits { + & li { + padding-right: 2%; + } + & span { + padding-right: 2%; + } + @media (max-width: 500px) { + & span { + display: none; + } + } +} \ No newline at end of file diff --git a/src/sass/components/_social-contact.scss b/src/sass/components/_social-contact.scss new file mode 100644 index 0000000..87f48f3 --- /dev/null +++ b/src/sass/components/_social-contact.scss @@ -0,0 +1,30 @@ +.social-contact { + display: inline; + p { + display: inline-block; + position: relative; + color: $color-high-contrast; + font-weight: $font-bold; + font-size: $font-smallest; + text-align: center; + text-transform: uppercase; + border: 1px solid $color-low-contrast; + border-radius: 100%; + width: 8.5em; + padding: 3.45em 0; + margin-left: 2%; + margin-right: 2%; + &:hover { + color: $color-highlight; + } + } + span { + @extend %social-icons; + position: absolute; + top: -5px; + left: -5px; + font-size: $font-large; + background-color: $color-high-contrast; + padding: 0.5em; + } +} diff --git a/src/sass/components/_social-share.scss b/src/sass/components/_social-share.scss new file mode 100644 index 0000000..4af8e04 --- /dev/null +++ b/src/sass/components/_social-share.scss @@ -0,0 +1,34 @@ +.social-share { + @extend %social-icons; + background-color: $color-low-contrast; + padding: 0.5em; + font-size: $font-tiny; +} + +.pinterest-container { + border-radius: 100%; + background-color: $color-low-contrast; + padding: 0.17em 0.25em; + margin-top: -2px; + .icon-pinterest { + @extend %social-icons; + background-color: $color-background; + color: $color-low-contrast; + font-size: $font-smaller; + &:hover { + background-color: $color-highlight; + color: $color-low-contrast; + } + } +} + +.social-share, .pinterest-container { + &:hover { + cursor: pointer; + } +} + +.social-share span, .pinterest-container span { + display: none; +} + diff --git a/src/sass/layout/_INDEX.scss b/src/sass/layout/_INDEX.scss new file mode 100644 index 0000000..ddcbaa1 --- /dev/null +++ b/src/sass/layout/_INDEX.scss @@ -0,0 +1,3 @@ +@import "header"; +@import "content"; +@import "footer"; diff --git a/src/sass/layout/_content.scss b/src/sass/layout/_content.scss new file mode 100644 index 0000000..649f4aa --- /dev/null +++ b/src/sass/layout/_content.scss @@ -0,0 +1,80 @@ +.l-content { + & header h1, & footer { + margin: 0; + padding: 5% 5% 2% 10%; + } + & header h2 { + padding: 0 35% 10% 10%; + } + & .site-nav { + padding-left: 10%; + } + & .l-social-media-share { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + list-style: none; + padding-right: 10%; + margin-left: auto; + & li { + margin-left: 5%; + margin-right: 5%; + } + @media (max-width: 500px) { + margin-left: 10%; + } + } +} + +.l-posts { + margin: auto; + & h1 { + margin-bottom: 1.5%; + } + & .copy { + margin-bottom: 1.5%; + } + .l-credits { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + @media (max-width: 500px) { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } + } +} + +.l-add { + margin: 3% 0 3% 0; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + & a { + margin-left: auto; + } +} diff --git a/src/sass/layout/_footer.scss b/src/sass/layout/_footer.scss new file mode 100644 index 0000000..4e0624b --- /dev/null +++ b/src/sass/layout/_footer.scss @@ -0,0 +1,22 @@ +.footer-main { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + margin: 2% 7% 2% 7%; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + @media (max-width: 500px) { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + + } +} \ No newline at end of file diff --git a/src/sass/layout/_header.scss b/src/sass/layout/_header.scss new file mode 100644 index 0000000..7edb6d3 --- /dev/null +++ b/src/sass/layout/_header.scss @@ -0,0 +1,72 @@ +.page-header { + background-color: $color-high-contrast; + & h1 { + color: $color-background; + font-size: $font-larger; + } + & .site-nav { + @include set-font ($color-background, $font-smallest, $font-regular, auto, uppercase); + } + & .site-nav a:hover { + background-color: $color-background; + color: $color-high-contrast; + } + & .site-nav a:active { + color: $color-highlight; + } +} + +.content-header-bar { + background-color: $color-background; + border-bottom: 1px solid $color-low-contrast; + border-top: 1px solid $color-low-contrast; +} + +.l-content-header-bar { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + padding-left: 10%; + padding-top: 2%; + padding-bottom: 2%; +} + +.l-page-header { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin: 0; + padding-top: 5%; + padding-bottom: 5%; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + & h1 { + padding-left: 10%; + margin-right: auto; + } + & .site-nav { + margin: auto; + padding-right: 10%; + } +} \ No newline at end of file diff --git a/src/sass/main.scss b/src/sass/main.scss new file mode 100644 index 0000000..fc0e187 --- /dev/null +++ b/src/sass/main.scss @@ -0,0 +1,4 @@ +@import "utils/INDEX"; +@import "base/INDEX"; +@import "layout/INDEX"; +@import "components/INDEX"; diff --git a/src/sass/utils/_INDEX.scss b/src/sass/utils/_INDEX.scss new file mode 100644 index 0000000..094d7a0 --- /dev/null +++ b/src/sass/utils/_INDEX.scss @@ -0,0 +1,3 @@ +@import "variables"; +@import "mixins"; +@import "placeholders"; diff --git a/src/sass/utils/_mixins.scss b/src/sass/utils/_mixins.scss new file mode 100644 index 0000000..4ef2f4e --- /dev/null +++ b/src/sass/utils/_mixins.scss @@ -0,0 +1,40 @@ +@each $icon in $icons { + .icon-#{nth($icon, 1)}:before { + content:nth($icon, 2); + } +} + +@mixin set-font ($color, $font-size: $font-smallest, $weight: $font-regular, $font: "auto", $upper: "auto") { + font-size: $font-size; + color: $color; + font-weight: $weight; + @if $font != "auto" { + font-family: $font; + } + @if $upper != "auto" { + text-transform: uppercase; + } +} + +@mixin button($text-color, $background-color, $font-size, $hover-text: $background-color, $hover-background: $text-color) { + @include set-font ($text-color, $font-size, $font-medium, auto, uppercase); + text-align: center; + width: 18%; + padding: 1% 3% 1% 3%; + border: 1px solid $color-low-contrast; + border-radius: 5%/20%; + background-color: $background-color; + -webkit-transition: color 0.3s, background-color 0.3s; + transition: color 0.3s, background-color 0.3s; + &:hover { + color: $hover-text; + background-color: $hover-background; + } + &:active { + border: 2px solid $text-color; + } + @media (max-width: 500px) { + width: 50%; + height: 2em; + } +} \ No newline at end of file diff --git a/src/sass/utils/_placeholders.scss b/src/sass/utils/_placeholders.scss new file mode 100644 index 0000000..e299388 --- /dev/null +++ b/src/sass/utils/_placeholders.scss @@ -0,0 +1,18 @@ +%social-icons { + font-family: 'intelly-icons'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + border-radius: 100%; + -webkit-transition: color 0.25s, background-color 0.25s; + transition: color 0.25s, background-color 0.25s; + color: $color-background; + &:hover { + color: $color-highlight; + } +} diff --git a/src/sass/utils/_variables.scss b/src/sass/utils/_variables.scss new file mode 100644 index 0000000..fc9e5f2 --- /dev/null +++ b/src/sass/utils/_variables.scss @@ -0,0 +1,71 @@ +// Header: +// H1 - Ubuntu, Medium, 56pt +// Nav - Ubuntu, Regular, 24pt + +// Content: +// H1 - Roboto Slab, Regular, 80pt +// Text - Roboto Slab, Light, 56pt + +// Links - Ubuntu, Bold, 24pt +// Share - Ubuntu, Medium, 24pt + +// Blog H1 - Ubuntu, Regular, 72pt +// Blog text - Ubuntu, Light, 42pt +// Blog credits - Ubuntu, Light, Italic, 32pt +// Blog buttons - Ubuntu, Medium, 24pt + +// Contact H1 - Ubuntu, Medium, 72pt +// Contact text - Ubuntu, Regular, 32pt +// Contact buttons - Ubuntu, Medium, 28pt + +// Footer: +// Info - Ubuntu, Regular, 36pt +// Social - Ubuntu, Bold, 24pt +// Copyright - Ubuntu, Regular, 22pt + +// ***** Font-Families ***** +$primary-font: "Ubuntu", sans-serif; +$secondary-font: "Roboto Slab", serif; + +// ***** Font-Weights ***** +$font-bold: 700; +$font-medium: 500; +$font-regular: 400; +$font-light: 300; + +// ***** Font-Styles ***** +$font-italic: italic; + +// ***** Font-Sizes ***** +$font-base: 16px; + +$font-size-modifier: 0.4167; + +$font-tiny: 1.8em * $font-size-modifier; // 22pt +$font-smallest: 2em * $font-size-modifier; // 24pt +$font-smaller: 2.3em * $font-size-modifier; // 28pt +$font-small: 2.55em * $font-size-modifier; // 32pt +$font-normal: 3em * $font-size-modifier; // 36pt +$font-large: 3.5em * $font-size-modifier; // 42pt +$font-larger: 4.67em * $font-size-modifier; // 56pt +$font-largest: 6em * $font-size-modifier; // 72pt +$font-huge: 6.67em * $font-size-modifier; // 80pt + +// ***** Icons ***** +$icons: +(google-plus "\e600") +(facebook "\e601") +(twitter "\e602") +(linkedin2 "\e603") +(pinterest "\e604") +(pinterest2 "\e605"); + +// ***** Colors ***** +$color-background: #ffffff; // white +$color-high-contrast: #1e1d24; // off black +$color-low-contrast: #b4b3b5; // light grey +$color-highlight: #97f1e7; // aqua +$color-highlight-contrast: #5a8785; // dark grey +$color-invalid: #FAAFBE; // pink + +