diff --git a/design/pages.graffle b/design/pages.graffle
index 36ad1149e..0772ad630 100644
Binary files a/design/pages.graffle and b/design/pages.graffle differ
diff --git a/scripts/server b/scripts/server
index 2b5678106..c3dcfbd12 100755
--- a/scripts/server
+++ b/scripts/server
@@ -31,6 +31,7 @@ serveIndex = (request, response)->
app.get '*/$', serveIndex
app.get '/craft/*', serveIndex
+app.get '/login', serveIndex
app.get '*', (request, response)->
extension = path.extname(request.path)
@@ -49,5 +50,5 @@ app.get '*', (request, response)->
response.set 'Content-Type', contentType
response.sendFile request.path, root:ROOT
-server = app.listen 8000, ->
+server = app.listen 8000, '127.0.0.1', ->
console.log "listening at: #{server.address().address}:#{server.address().port}"
diff --git a/src/coffee/constants.coffee b/src/coffee/constants.coffee
index 4341b9a53..9b8fdb404 100644
--- a/src/coffee/constants.coffee
+++ b/src/coffee/constants.coffee
@@ -61,6 +61,20 @@ Event.transitionEnd = (->
exports.Key = Key = {}
Key.Return = 13
+exports.Login = Login = {}
+Login.authorizeUrl = _.template "https://github.com/login/oauth/authorize" +
+ "?client_id=<%= clientId %>&scope=public_repo&state=<%= state %>"
+Login.clientIds =
+ 'local': 'a2a1c5f1bb2d7bd14ebb'
+ 'staging': '26e1e9a4a702cabe334f'
+ 'production': 'ea419abd2ab96c708815'
+
+exports.ModelState = ModelState = {}
+ModelState.unloaded = 'unloaded'
+ModelState.loading = 'loading'
+ModelState.loaded = 'loaded'
+ModelState.failed = 'failed'
+
exports.Opacity = Opacity = {}
Opacity.hidden = 1e-6
Opacity.shown = 1
@@ -69,12 +83,6 @@ exports.ProductionEnvs = [ 'staging', 'production' ]
exports.RequiredMods = [ 'minecraft' ]
-exports.ModelState = ModelState = {}
-ModelState.unloaded = 'unloaded'
-ModelState.loading = 'loading'
-ModelState.loaded = 'loaded'
-ModelState.failed = 'failed'
-
exports.Text = Text = {}
Text.title = 'The Ultimate Minecraft Crafting Guide'
@@ -83,10 +91,12 @@ Url.crafting = _.template "/craft/<%= inventoryText %>"
Url.item = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/"
Url.itemData = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/item.cg"
Url.itemIcon = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/icon.png"
+Url.login = _.template "/login"
Url.mod = _.template "/browse/<%= modSlug %>/"
Url.modData = _.template "/data/<%= modSlug %>/mod.cg"
Url.modIcon = _.template "/browse/<%= modSlug %>/icon.png"
Url.modVersionData = _.template "/data/<%= modSlug %>/<%= modVersion %>/mod-version.cg"
+Url.root = _.template "/"
Url.tutorial = _.template "/browse/<%= modSlug %>/tutorials/<%= tutorialSlug %>/"
Url.tutorialData = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>.cg"
Url.tutorialIcon = _.template "/browse/<%= modSlug %>/tutorials/<%= tutorialSlug %>/icon.png"
diff --git a/src/coffee/controllers/base_controller.coffee b/src/coffee/controllers/base_controller.coffee
index e3a50ffb9..d23886f43 100644
--- a/src/coffee/controllers/base_controller.coffee
+++ b/src/coffee/controllers/base_controller.coffee
@@ -14,14 +14,12 @@ module.exports = class BaseController extends Backbone.View
constructor: (options={})->
@tryRefresh = _.debounce @_tryRefresh, 100
- Object.defineProperty this, 'model', get:@getModel, set:@setModel
@_model = null
@_rendered = false
@_parent = options.parent
@_children = []
- Object.defineProperty this, 'rendered', get:-> return @_rendered
@_loadTemplate options.templateName
super options
@@ -122,6 +120,21 @@ module.exports = class BaseController extends Backbone.View
@_model = newModel
@tryRefresh()
+ isRendered: ->
+ return @_rendered
+
+ getUser: ->
+ return @_user
+
+ setUser: (user)->
+ @_user = user
+ @tryRefresh()
+
+ Object.defineProperties @prototype,
+ model: {get:@prototype.getModel, set:@prototype.setModel}
+ rendered: {get:@prototype.isRendered }
+ user: {get:@prototype.user, set:@prototype.setUser}
+
# Backbone.View Overrides ######################################################################
events: ->
diff --git a/src/coffee/controllers/header_controller.coffee b/src/coffee/controllers/header_controller.coffee
index bf2860878..5d8db72f2 100644
--- a/src/coffee/controllers/header_controller.coffee
+++ b/src/coffee/controllers/header_controller.coffee
@@ -7,17 +7,36 @@ All rights reserved.
BaseController = require './base_controller'
{Duration} = require '../constants'
+{Event} = require '../constants'
{ProductionEnvs} = require '../constants'
+{Url} = require '../constants'
########################################################################################################################
module.exports = class HeaderController extends BaseController
constructor: (options={})->
+ if not options.client? then throw new Error 'options.client is required'
super options
+ @client = options.client
+ @_user = options.user
+
# Event Methods ################################################################################
+ onLoginLinkClicked: (event)->
+ event.preventDefault()
+
+ if @user?
+ @client.logout()
+ .then ->
+ router.user = null
+ .catch (error)->
+ logger.error -> "Failed to log out: #{error}"
+ .done()
+ else
+ router.navigate Url.login(), trigger:true
+
onLogoClicked: ->
router.navigate '/', trigger:true
return false
@@ -28,6 +47,18 @@ module.exports = class HeaderController extends BaseController
router.navigate $(event.currentTarget).attr('href'), trigger:true
return false
+ # Property Methods #############################################################################
+
+ getUser: ->
+ return @_user
+
+ setUser: (user)->
+ @_user = user
+ @tryRefresh()
+
+ Object.defineProperties @prototype,
+ user: {get:@prototype.getUser, set:@prototype.setUser}
+
# BaseController Overrides #####################################################################
render: ->
@@ -36,6 +67,8 @@ module.exports = class HeaderController extends BaseController
@_rendered = true
@$navLinks = ($(el) for el in @$('.navBar a'))
+ @$loginName = @$('.login .name')
+ @$loginLink = @$('.login a')
zIndex = @$navLinks.length + 100
for $navLink in @$navLinks
@@ -46,6 +79,13 @@ module.exports = class HeaderController extends BaseController
$('body').append ""
refresh: ->
+ if @user?
+ @$loginName.html "Hello #{router.user.login}!"
+ @$loginLink.html "Logout"
+ else
+ @$loginName.html ''
+ @$loginLink.html 'Login'
+
for $navLink in @$navLinks
linkPage = $navLink.data('page')
if @model is linkPage
@@ -64,3 +104,4 @@ module.exports = class HeaderController extends BaseController
return _.extend super,
'click a.logo': 'onLogoClicked'
'click .navBar a': 'onNavItemClicked'
+ 'click .login a': 'onLoginLinkClicked'
diff --git a/src/coffee/controllers/login_page_controller.coffee b/src/coffee/controllers/login_page_controller.coffee
new file mode 100644
index 000000000..a62f7daf1
--- /dev/null
+++ b/src/coffee/controllers/login_page_controller.coffee
@@ -0,0 +1,130 @@
+###
+Crafting Guide - login_page_controller.coffee
+
+Copyright (c) 2015 by Redwood Labs
+All rights reserved.
+###
+
+PageController = require './page_controller'
+User = require '../models/user'
+{CraftingGuideClient} = require 'crafting-guide-common'
+{Event} = require '../constants'
+{Login} = require '../constants'
+{Url} = require '../constants'
+
+########################################################################################################################
+
+module.exports = class LoginPageController extends PageController
+
+ @State: State =
+ ReadyToLogin: 'ready-to-login'
+ ServerDown: 'server-down'
+ InvalidCallback: 'invalid-callback'
+ FetchingToken: 'fetching-token'
+ LoggedIn: 'logged-in'
+
+ constructor: (options={})->
+ options.templateName = 'login_page'
+
+ @client = options.client
+ @params = options.params
+ @storage = options.storage
+
+ @_accessTokenLookupFailed = false
+
+ @client.on Event.change + ':status', => @tryRefresh()
+
+ super options
+
+ # Event Methods ################################################################################
+
+ onExpandReadMore: (event)->
+ event.preventDefault()
+ @$readMoreLink.addClass 'hidden'
+ @$readMoreContent.removeClass 'closed'
+
+ onLoginButtonClicked: (event)->
+ event.preventDefault()
+ @_redirectToGitHub()
+
+ # Property Methods #############################################################################
+
+ getLoginSecurityToken: ->
+ @storage.load 'loginSecurityToken'
+
+ setLoginSecurityToken: (value)->
+ @storage.store 'loginSecurityToken', value
+
+ Object.defineProperties @prototype,
+ loginSecurityToken: {get:@prototype.getLoginSecurityToken, set:@prototype.setLoginSecurityToken}
+ serverStatus: {get:@prototype.getServerStatus, set:@prototype.setServerStatus}
+
+ # PageController Overrides #####################################################################
+
+ getTitle: ->
+ return "Login"
+
+ # BaseController Methods #######################################################################
+
+ onDidRender: ->
+ @$readMoreLink = @$('a.read-more')
+ @$readMoreContent = @$('.read-more-content')
+
+ if (not @user?) and (@params?.state is @loginSecurityToken)
+ @client.completeGitHubLogin code:@params.code
+ .then (response)->
+ attributes = response.json.data.user
+ if attributes?
+ router.user = new User attributes
+ router.navigate Url.root(), trigger:true
+ .catch (error)->
+ logger.error "Failed to get access token: #{error}"
+ @_accessTokenLookupFailed = true
+ @refresh()
+ .done()
+ super
+
+ refresh: ->
+ state = @_computeState()
+ @$('.two-thirds-content').addClass 'hidden'
+ @$(".two-thirds-content.#{state}").removeClass 'hidden'
+
+ super
+
+ # Backbone.View Overrides ######################################################################
+
+ events: ->
+ return _.extend super,
+ 'click a.read-more': 'onExpandReadMore'
+ 'click button': 'onLoginButtonClicked'
+
+ # Private Methods ##############################################################################
+
+ _computeState: ->
+ if @user? then return State.LoggedIn
+
+ if @client.status is CraftingGuideClient.Status.Down then return State.ServerDown
+
+ if @_accessTokenLookupFailed then return State.InvalidCallback
+
+ if @params?.state?
+ if @params.state is @loginSecurityToken
+ return State.FetchingToken
+ else
+ return State.InvalidCallback
+
+ return State.ReadyToLogin
+
+ _compeleteLogin: (code)->
+ @client.completeGitHubLogin code:code
+ .then (response)=>
+ router.user = new User response.json.user
+ .catch (error)->
+ @_accessTokenLookupFailed = true
+ @refresh
+ .done()
+
+ _redirectToGitHub: ->
+ @loginSecurityToken = _.uuid()
+ clientId = Login.clientIds[global.env]
+ window.location.href = Login.authorizeUrl clientId:clientId, state:@loginSecurityToken
diff --git a/src/coffee/crafting_guide_router.coffee b/src/coffee/crafting_guide_router.coffee
index 01dbccdcd..9c61c4821 100644
--- a/src/coffee/crafting_guide_router.coffee
+++ b/src/coffee/crafting_guide_router.coffee
@@ -13,12 +13,14 @@ HomePageController = require './controllers/home_page_controller'
ImageLoader = require './controllers/image_loader'
ItemPageController = require './controllers/item_page_controller'
ItemSlug = require './models/item_slug'
+LoginPageController = require './controllers/login_page_controller'
Mod = require './models/mod'
ModPack = require './models/mod_pack'
ModPageController = require './controllers/mod_page_controller'
TutorialPageController = require './controllers/tutorial_page_controller'
Storage = require './models/storage'
UrlParams = require './url_params'
+User = require './models/user'
{ProductionEnvs} = require './constants'
{DefaultMods} = require './constants'
{Duration} = require './constants'
@@ -34,15 +36,20 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
@_page = null
@_pageControllers = {}
@_lastReported = null
+ @_user = null
super options
- @imageLoader = new ImageLoader defaultUrl:'/images/unknown.png'
- @modPack = new ModPack
- @storage = new Storage storage:window.localStorage
- @_defaultOptions = imageLoader:@imageLoader, modPack:@modPack, storage:@storage
+ @client = options.client
+ @imageLoader = new ImageLoader defaultUrl:'/images/unknown.png'
+ @modPack = new ModPack
+ @storage = new Storage storage:window.localStorage
- @headerController = new HeaderController el:'.view__header'
+ @_defaultOptions = client:@client, imageLoader:@imageLoader, modPack:@modPack, storage:@storage, user:@_user
+ @on Event.change + ':user', => @_defaultOptions.user = @_user
+
+ @headerController = new HeaderController _.extend {el:'.view__header'}, @_defaultOptions
@headerController.render()
+ @on Event.change + ':user', => @headerController.user = @user
# Public Methods ###############################################################################
@@ -58,6 +65,38 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
@modPack.addMod mod
+ loadCurrentUser: ->
+ @client.fetchCurrentUser()
+ .then (response)=>
+ userData = response.json?.data?.user
+ @user = new User userData if userData?
+ .catch (error)=>
+ if error.response.statusCode isnt 401
+ logger.error "Failed to get current user: #{error}"
+ else
+ logger.info "User is not logged in"
+ .done()
+
+ # Property Methods #############################################################################
+
+ getUser: ->
+ return @_user
+
+ setUser: (newUser)->
+ oldUser = @_user
+ return if newUser is oldUser
+
+ @_user = newUser
+ logger.info -> "User changed to: #{newUser}"
+
+ @trigger Event.change + ':user', this, oldUser, newUser
+ @trigger Event.change, this
+
+ if @_controller? then @_controller.user = newUser
+
+ Object.defineProperties @prototype,
+ user: {get:@prototype.getUser, set:@prototype.setUser}
+
# Backbone.Router Overrides ####################################################################
navigate: ->
@@ -73,6 +112,7 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
'configure(/)': 'route__configure'
'craft(/)': 'route__craft'
'craft/:text': 'route__craft'
+ 'login(/)': 'route__login'
'item/:itemSlug': 'deprecated__item'
'crafting/(:text)': 'deprecated__crafting'
@@ -114,6 +154,11 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
controller.model.params = inventoryText:text
@_setPage 'craft', controller
+ route__login: ->
+ params = new UrlParams code:{type:'string'}, state:{type:'string'}
+ controller = new LoginPageController _.extend {params:params}, @_defaultOptions
+ @_setPage 'login', controller
+
# Deprecated Route Methods #####################################################################
deprecated__crafting: (text)->
@@ -175,6 +220,7 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
window.scrollTo 0, 0
+ controller.user = @user
controller.onWillShow()
controller.$el = $pageContent
controller.render()
diff --git a/src/coffee/main.coffee b/src/coffee/main.coffee
index 1a8fa2582..e306d950c 100644
--- a/src/coffee/main.coffee
+++ b/src/coffee/main.coffee
@@ -8,10 +8,11 @@ All rights reserved.
require './underscore_mixins'
require './polyfill'
-CraftingGuideRouter = require './crafting_guide_router'
-FeedbackController = require './controllers/feedback_controller'
-views = require './views'
-{Logger} = require 'crafting-guide-common'
+CraftingGuideRouter = require './crafting_guide_router'
+FeedbackController = require './controllers/feedback_controller'
+views = require './views'
+{CraftingGuideClient} = require 'crafting-guide-common'
+{Logger} = require 'crafting-guide-common'
########################################################################################################################
@@ -22,17 +23,29 @@ if typeof(global) is 'undefined'
global.logger = new Logger
switch window.location.hostname
- when 'localhost'
- global.env = 'development'
+ when 'local.crafting-guide.com'
+ global.env = 'local'
logger.level = Logger.DEBUG
+ apiBaseUrl = 'http://local.crafting-guide.com:8001'
when 'new.crafting-guide.com'
- global.env = 'staging'
+ global.env = 'staging'
logger.level = Logger.VERBOSE
+ apiBaseUrl = 'https://crafting-guide-production.herokuapp.com'
when 'crafting-guide.com'
- global.env = 'production'
+ global.env = 'production'
logger.level = Logger.INFO
+ apiBaseUrl = 'https://crafting-guide-production.herokuapp.com'
+ else
+ throw new Error "cannot determine the environment of: #{window.location.hostname}"
-global.router = new CraftingGuideRouter
+client = _.extend new CraftingGuideClient(baseUrl:apiBaseUrl), Backbone.Events
+client.onStatusChanged = (c, oldStatus, newStatus)->
+ logger.info "Crafting Guide server status changed from #{oldStatus} to #{newStatus}"
+ client.trigger 'change:status', client, oldStatus, newStatus
+ client.trigger 'change', client
+client.checkStatus()
+
+global.router = new CraftingGuideRouter client:client
global.util = require 'util'
global.views = views
global.markdown = global.markdown.markdown
@@ -40,6 +53,7 @@ global.markdown = global.markdown.markdown
global.feedbackController = new FeedbackController el:'.view__feedback'
feedbackController.render()
+global.router.loadCurrentUser()
global.router.loadDefaultModPack()
logger.info -> "CraftingGuide is ready"
diff --git a/src/coffee/models/user.coffee b/src/coffee/models/user.coffee
new file mode 100644
index 000000000..0d085bc2e
--- /dev/null
+++ b/src/coffee/models/user.coffee
@@ -0,0 +1,33 @@
+###
+Crafting Guide - user.coffee
+
+Copyright (c) 2015 by Redwood Labs
+All rights reserved.
+###
+
+BaseModel = require './base_model'
+{Url} = require '../constants'
+
+########################################################################################################################
+
+module.exports = class GitHubUser extends BaseModel
+
+ constructor: (attributes={}, options={})->
+ attributes.avatarUrl ?= null
+ attributes.email ?= null
+ attributes.login ?= null
+ attributes.name ?= null
+ super attributes, options
+
+ # Property Methods #############################################################################
+
+ isAuthenticated: ->
+ return @login?
+
+ Object.defineProperties @prototype,
+ authenticated: {get:@prototype.isAuthenticated}
+
+ # Object Overrides #############################################################################
+
+ toString: ->
+ return "#{@name} (#{@login})"
diff --git a/src/coffee/underscore_mixins.coffee b/src/coffee/underscore_mixins.coffee
index ac789d648..99fbfeb07 100644
--- a/src/coffee/underscore_mixins.coffee
+++ b/src/coffee/underscore_mixins.coffee
@@ -31,3 +31,5 @@ _.mixin
parts = [ null, parts[0] ]
return parts
+
+_.mixin require('crafting-guide-common').stringMixins
diff --git a/src/jade/includes/_header.jade b/src/jade/includes/_header.jade
index 676fcb48e..1ac8ce3dc 100644
--- a/src/jade/includes/_header.jade
+++ b/src/jade/includes/_header.jade
@@ -12,6 +12,9 @@
.addthis_sharing_toolbox
.divider.bottom
+ .login
+ span.name
+ a(href="/login") Login
.navBar
a(data-page="home", href="/")
diff --git a/src/jade/templates/login_page.jade b/src/jade/templates/login_page.jade
new file mode 100644
index 000000000..addd6bd1e
--- /dev/null
+++ b/src/jade/templates/login_page.jade
@@ -0,0 +1,63 @@
+//-
+//- Crafting Guide - login_page.jade
+//-
+//- Copyright (c) 2015 by Redwood Labs
+//- All rights reserved.
+//-
+
+.view__login_page
+ .two-thirds-content.hideable.ready-to-login
+ img.float-right(src="/images/github-login-large.png")
+ h1 Login with GitHub
+ p.
+ Use your GitHub credentials to login and make changes on Crafting Guide. No GitHub account? Don't worry, you
+ can get one for free. read more...
+ .read-more-content.slideable.closed
+ p.
+ All changes you make on the site get submitted directly to our GitHub repo, and then published through
+ the same automated deployment process as changes made by anyone else. That way, anyone can contribute
+ easily: whether developer or end user.
+ p.
+ To log in, we redirect you to GitHub's site, and once you're signed in, they'll send you back here. We
+ never see your credentials, and only get the minimum set of permissions necessary to submit changes to
+ the Crafting Guide repo on your behalf.
+ p.
+ GitHub offers free accounts, so even if you don't already have one, you've got nothing to lose!
+
+ .actions
+ button.huge Login with GitHub
+
+ .clear
+
+ .two-thirds-content.hideable.hidden.server-down
+ h1 Sorry!
+ p.
+ Our interactive editing service is down right now, but you can always submit a pull request over at
+ the Crafting Guide GitHub Repository. We'll have
+ the editing service back up soon!
+
+ .two-thirds-content.hideable.hidden.invalid-callback
+ img.float-right(src="/images/github-login-large.png")
+ h1 Oops!
+ p.
+ Sorry about that, but it looks like something went wrong with your GitHub login. Click the button below to
+ try again, or use the Feedback button on the left to let us know, and we'll try to help.
+
+ .actions
+ button.huge Login with GitHub
+
+ .clear
+
+ .two-thirds-content.hideable.hidden.fetching-token
+ img.float-right(src="/images/github-login-large.png")
+ h1 Completing log in...
+ p.
+ Hang on just a second while we finish logging you in...
+
+ .clear
+
+ .two-thirds-content.hideable.hidden.logged-in
+ img.float-right(src="/images/github-login-large.png")
+ h1 All set!
+
+ .clear
diff --git a/src/scss/classes.scss b/src/scss/classes.scss
index 74d7a4f7f..e651ea904 100644
--- a/src/scss/classes.scss
+++ b/src/scss/classes.scss
@@ -19,6 +19,10 @@ All rights reserved.
text-align: center;
}
+.clear {
+ clear: both;
+}
+
.content {
background: $color-content;
box-shadow: 10px 10px 20px #666;
@@ -32,6 +36,11 @@ All rights reserved.
.error-new { background: $color-error-new !important; }
+.float-right {
+ float: right;
+ margin-left: 3em;
+}
+
.hideable {
opacity: 1;
transition: opacity $animate-normal;
@@ -111,6 +120,21 @@ All rights reserved.
}
}
+.slideable {
+ max-height: 500px;
+ overflow: hidden;
+ transition: max-height $animate-normal;
+
+ &.closed {
+ max-height: 0;
+ }
+}
+
+.two-thirds-content {
+ position: relative; width: 66%;
+ margin: 5em 16.5%;
+}
+
.videos {
.panel {
text-align: center;
diff --git a/src/scss/main.scss b/src/scss/main.scss
index 8a09b65f9..36bd768a0 100644
--- a/src/scss/main.scss
+++ b/src/scss/main.scss
@@ -77,6 +77,7 @@ $color-footer-text: #FFFFFF;
$color-link: #0000FF;
$color-link-subtle: #545A99;
$color-text: #000000;
+$color-text-light: #FFFFFF;
// Z-Index Layers //////////////////////////////////////////////////////////////////////////////////
diff --git a/src/scss/tags.scss b/src/scss/tags.scss
index 93fbc2efe..0de72e9bb 100644
--- a/src/scss/tags.scss
+++ b/src/scss/tags.scss
@@ -29,6 +29,11 @@ button {
margin: 2em 0;
padding: 0.5em 0.5em;
}
+
+ &.huge {
+ font-size: $font-size-large;
+ padding: 0.5em 3em;
+ }
}
h1 {
diff --git a/src/scss/templates/index.scss b/src/scss/templates/index.scss
index 19bc6da1e..b009b69a3 100644
--- a/src/scss/templates/index.scss
+++ b/src/scss/templates/index.scss
@@ -17,6 +17,7 @@ All rights reserved.
@import 'inventory_table';
@import 'item';
@import 'item_page';
+@import 'login_page';
@import 'minimal_recipe';
@import 'mod';
@import 'mod_pack';
diff --git a/src/scss/templates/login_page.scss b/src/scss/templates/login_page.scss
new file mode 100644
index 000000000..9ce13fcd3
--- /dev/null
+++ b/src/scss/templates/login_page.scss
@@ -0,0 +1,33 @@
+/*
+Crafting Guide - login_page.scss
+
+Copyright (C) 2015 by Redwood Labs
+All rights reserved.
+*/
+
+.view__login_page {
+
+ button {
+ outline: none;
+ }
+
+ h1 {
+ font-family: $font-family-header;
+ font-size: $font-size-x-large;
+ padding-top: 2em;
+ }
+
+ p {
+ margin-bottom: 1em;
+ }
+
+ .actions {
+ margin-top: 4em;
+ width: 100%;
+ text-align: center;
+ }
+
+ .read-more {
+ font-size: 75%;
+ }
+}
diff --git a/src/scss/views.scss b/src/scss/views.scss
index f5e157e16..4af1215e7 100644
--- a/src/scss/views.scss
+++ b/src/scss/views.scss
@@ -55,6 +55,25 @@ All rights reserved.
.divider {
background: $color-brick-dark url('/images/stone_brick.png');
position: absolute; bottom: 5em; height: 3.2em; width: 100%;
+
+ .login {
+ position: absolute; top: 50%; right: 2em;
+ @include transform(translateY(-50%));
+
+ a {
+ color: $color-text-light;
+ font-family: $font-family-normal;
+ font-size: $font-size-normal;
+ font-weight: bold;
+ }
+
+ .name {
+ color: $color-text-light;
+ font-family: $font-family-normal;
+ font-size: $font-size-normal;
+ margin-right: 2em;
+ }
+ }
}
.navBar {
diff --git a/static/images/github-login-large.png b/static/images/github-login-large.png
new file mode 100644
index 000000000..c7b76b960
Binary files /dev/null and b/static/images/github-login-large.png differ