├── src
├── models
│ ├── mod.rs
│ └── city.rs
├── routes
│ ├── mod.rs
│ └── cities.rs
└── main.rs
├── images
└── morthdemo.gif
├── views
├── welcome.hbs
├── cities
│ ├── show.hbs
│ ├── form.hbs
│ └── list.hbs
└── layouts
│ └── main.hbs
├── .gitignore
├── Dockerfile
├── Cargo.toml
├── public
├── js
│ └── site.js
└── styles
│ ├── custom.css
│ ├── normalize.css
│ └── skeleton.css
├── README.md
└── LICENSE
/src/models/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod city;
2 |
--------------------------------------------------------------------------------
/images/morthdemo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/No9/tide-morth-example/HEAD/images/morthdemo.gif
--------------------------------------------------------------------------------
/views/welcome.hbs:
--------------------------------------------------------------------------------
1 | {{#*inline "page"}}
2 |
3 |
4 |
5 |
6 | Want to see some cities?
7 |
8 | {{/inline}}
9 | {{! remove whitespaces with ~ }}
10 | {{~> (parent)~}}
--------------------------------------------------------------------------------
/views/cities/show.hbs:
--------------------------------------------------------------------------------
1 | {{#*inline "page"}}
2 |
3 | {{city.country}}
4 | {{city.description}}
5 |
6 | Back
7 |
8 | {{/inline}}
9 | {{! remove whitespaces with ~ }}
10 | {{~> (parent)~}}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
7 | Cargo.lock
8 |
9 | # These are backup files generated by rustfmt
10 | **/*.rs.bk
11 |
--------------------------------------------------------------------------------
/src/models/city.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 | use wither::bson::{doc, oid::ObjectId};
3 | use wither::prelude::*;
4 |
5 | // Define a model. Simple as deriving a few traits.
6 | #[derive(Debug, Model, Serialize, Deserialize)]
7 | pub struct City {
8 | #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
9 | pub id: Option,
10 | #[model(index(index = "dsc", with(field = "country", index = "dsc")))]
11 | pub name: String,
12 | pub country: String,
13 | pub description: String,
14 | }
15 |
--------------------------------------------------------------------------------
/src/routes/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod cities;
2 |
3 | use super::*;
4 | use handlebars::to_json;
5 | use serde_json::value::Map;
6 | use tide::{http::mime, Request, Response};
7 |
8 | pub async fn welcome(req: Request) -> tide::Result> {
9 | let state = &req.state();
10 | let hb = &state.registry;
11 | let mut data = Map::new();
12 | data.insert("name".to_string(), to_json("Tider!"));
13 | data.insert("parent".to_string(), to_json("layouts/main"));
14 | Ok(Response::builder(200)
15 | .body(hb.render("welcome", &data)?)
16 | .content_type(mime::HTML))
17 | }
18 |
--------------------------------------------------------------------------------
/views/cities/form.hbs:
--------------------------------------------------------------------------------
1 | {{#*inline "page"}}
2 |
3 |
21 | {{/inline}}
22 | {{! remove whitespaces with ~ }}
23 | {{~> (parent)~}}
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Docker file to build an image with just the executable in.
2 | FROM rust:1.47.0-alpine as builder
3 |
4 | COPY . /app-build
5 |
6 | WORKDIR "/app-build"
7 |
8 | RUN \
9 | apk add --no-cache musl-dev && \
10 | cargo build --release \
11 | && echo "#!/bin/bash" > run.sh \
12 | && bin=$(find ./target/release -maxdepth 1 -perm -111 -type f| head -n 1) \
13 | && echo ./${bin##*/} >> run.sh \
14 | && chmod 755 run.sh
15 |
16 | FROM alpine
17 |
18 | RUN useradd -S rust
19 |
20 | WORKDIR "/app"
21 |
22 | # get files and built binary from previous image
23 | COPY --from=builder --chown=rust /app-build/run.sh /app-build/public/ /app-build/Cargo.toml /app-build/target/release/tide-morth-example ./
24 |
25 | WORKDIR "/app/public"
26 |
27 | COPY --from=builder --chown=rust /app-build/run.sh /app-build/public/ ./
28 |
29 | WORKDIR "/app"
30 |
31 | USER rust
32 |
33 | ENV PORT 8080
34 |
35 | EXPOSE 8080
36 |
37 | CMD ["./run.sh"]
38 |
--------------------------------------------------------------------------------
/views/cities/list.hbs:
--------------------------------------------------------------------------------
1 | {{#*inline "page"}}
2 |
3 | {{#if cities}}
4 |
5 |
6 |
7 | | Id |
8 | Name |
9 | Country |
10 | |
11 | |
12 | |
13 |
14 |
15 |
16 | {{#each cities}}
17 |
18 | | {{this._id.[$oid]}} |
19 | {{this.name}} |
20 | {{this.country}} |
21 | Show |
22 | Edit |
23 | Delete |
24 |
25 | {{/each}}
26 |
27 |
28 | {{/if}}
29 |
30 | Create new city
31 |
32 |
33 |
34 | {{/inline}}
35 | {{! remove whitespaces with ~ }}
36 | {{~> (parent)~}}
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tide-morth-example"
3 | version = "0.3.0"
4 | authors = ["Anton Whalley "]
5 | edition = "2018"
6 |
7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8 |
9 | [dependencies]
10 | tide = "0.14"
11 | tide-handlebars="0.6"
12 | handlebars="3.2.1"
13 | async-std = { version = "1.6.0", features = ["unstable", "attributes"] }
14 | mongodb = { version = "1", default-features=false }
15 | serde = "1.0.0"
16 | serde_json = "1.0.39"
17 | lazy_static = "1"
18 | wither = { version = "0.9.0-alpha.2", features = ["async-std-runtime"], default-features = false }
19 | tracing = { version = "0.1", default-features = true, features = ["log", "std"] }
20 | tracing-futures = { version = "0.2", default-features = false, features = ["std-future"] }
21 | tracing-subscriber = "0.2.7"
22 | tracing-log = "0.1"
23 | tracing-attributes = { version = "0.1.2"}
24 |
25 | tracing-opentelemetry = "*"
26 | # opentelemetry example
27 | opentelemetry = "0.6"
28 | opentelemetry-jaeger = "0.5"
29 |
30 | [profile.release]
31 | debug = true
--------------------------------------------------------------------------------
/views/layouts/main.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tide MoRTH Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
35 |
36 | {{~> page}}
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/js/site.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | // Variables
4 | var $codeSnippets = $('.code-example-body'),
5 | $nav = $('.navbar'),
6 | $body = $('body'),
7 | $window = $(window),
8 | $popoverLink = $('[data-popover]'),
9 | navOffsetTop = $nav.offset().top,
10 | $document = $(document),
11 | entityMap = {
12 | "&": "&",
13 | "<": "<",
14 | ">": ">",
15 | '"': '"',
16 | "'": ''',
17 | "/": '/'
18 | }
19 |
20 | function init() {
21 | $window.on('scroll', onScroll)
22 | $window.on('resize', resize)
23 | $popoverLink.on('click', openPopover)
24 | $document.on('click', closePopover)
25 | $('a[href^="#"]').on('click', smoothScroll)
26 | buildSnippets();
27 | }
28 |
29 | function smoothScroll(e) {
30 | e.preventDefault();
31 | $(document).off("scroll");
32 | var target = this.hash,
33 | menu = target;
34 | $target = $(target);
35 | $('html, body').stop().animate({
36 | 'scrollTop': $target.offset().top-40
37 | }, 0, 'swing', function () {
38 | window.location.hash = target;
39 | $(document).on("scroll", onScroll);
40 | });
41 | }
42 |
43 | function openPopover(e) {
44 | e.preventDefault()
45 | closePopover();
46 | var popover = $($(this).data('popover'));
47 | popover.toggleClass('open')
48 | e.stopImmediatePropagation();
49 | }
50 |
51 | function closePopover(e) {
52 | if($('.popover.open').length > 0) {
53 | $('.popover').removeClass('open')
54 | }
55 | }
56 |
57 | $("#button").click(function() {
58 | $('html, body').animate({
59 | scrollTop: $("#elementtoScrollToID").offset().top
60 | }, 2000);
61 | });
62 |
63 | function resize() {
64 | $body.removeClass('has-docked-nav')
65 | navOffsetTop = $nav.offset().top
66 | onScroll()
67 | }
68 |
69 | function onScroll() {
70 | if(navOffsetTop < $window.scrollTop() && !$body.hasClass('has-docked-nav')) {
71 | $body.addClass('has-docked-nav')
72 | }
73 | if(navOffsetTop > $window.scrollTop() && $body.hasClass('has-docked-nav')) {
74 | $body.removeClass('has-docked-nav')
75 | }
76 | }
77 |
78 | function escapeHtml(string) {
79 | return String(string).replace(/[&<>"'\/]/g, function (s) {
80 | return entityMap[s];
81 | });
82 | }
83 |
84 | function buildSnippets() {
85 | $codeSnippets.each(function() {
86 | var newContent = escapeHtml($(this).html())
87 | $(this).html(newContent)
88 | })
89 | }
90 |
91 |
92 | init();
93 |
94 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tide-morth-example
2 |
3 | An example application using mongodb rust tide and handlebars A.K.A MoRTH stack
4 |
5 | It's prime focus is to provde a project structure that should feel familiar to folks who have used JS Handlebars, pybars or Java Handlebars projects.
6 |
7 | 
8 |
9 | If you are evaluating tide patterns please look at [Rust Tide Example](https://github.com/jbr/tide-example).
10 |
11 | Once this project has matured it's intended that it will enter the [Rust Tide Example Contributions](https://github.com/jbr/tide-example#contributing) section in some form.
12 |
13 | ## usage
14 |
15 | This example requires a local mongo instance which can be ran with:
16 |
17 | ```
18 | $ docker run -d -p 27017:27017 -v ~/data:/data/db mongo:4.2
19 | ```
20 |
21 | If you wish to use a different mongodb please modify the [connection string in main.rs](https://github.com/No9/tide-morth-example/blob/master/src/main.rs#L21)
22 |
23 | It also requires the options dependency of jaeger a local instance can be ran with:
24 |
25 | ```
26 | $ docker run -d --name jaeger \
27 | -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
28 | -p 5775:5775/udp \
29 | -p 6831:6831/udp \
30 | -p 6832:6832/udp \
31 | -p 5778:5778 \
32 | -p 16686:16686 \
33 | -p 14268:14268 \
34 | -p 14250:14250 \
35 | -p 9411:9411 \
36 | jaegertracing/all-in-one:1.18
37 | ```
38 |
39 | Then start the app
40 |
41 | ```
42 | $ cargo run
43 | ```
44 |
45 | ## Comments
46 |
47 | * Fast development for simple CRUD apps as no data access layer is required just as a decorated struct - See models/cities.rs
48 |
49 | ```rust
50 | use serde::{Deserialize, Serialize};
51 | use wither::bson::{doc, oid::ObjectId};
52 | use wither::prelude::*;
53 |
54 | // Define a model. Simple as deriving a few traits.
55 | #[derive(Debug, Model, Serialize, Deserialize)]
56 | pub struct City {
57 | #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
58 | pub id: Option,
59 | #[model(index(index = "dsc", with(field = "country", index = "dsc")))]
60 | pub name: String,
61 | pub country: String,
62 | pub description: String,
63 | }
64 | ```
65 |
66 | * Accessible to developers familiar with Javascript constructs
67 |
68 | ```html
69 |
70 | {{#each cities}}
71 |
72 | | {{this._id.[$oid]}} |
73 | {{this.name}} |
74 | {{this.country}} |
75 | Show |
76 | Edit |
77 |
78 |
79 | {{/each}}
80 |
81 | ```
82 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | pub mod models;
2 |
3 | extern crate serde_json;
4 |
5 | use async_std::sync::Arc;
6 | use handlebars::Handlebars;
7 | use mongodb::{options::ClientOptions, Client};
8 | use tide_handlebars::prelude::*;
9 |
10 | // use opentelemetry::api::Provider;
11 | // use opentelemetry::sdk;
12 | // use tracing_subscriber::prelude::*;
13 | use lazy_static::lazy_static;
14 | use std::env;
15 |
16 | mod routes;
17 |
18 | #[derive(Clone, Debug)]
19 | pub struct State {
20 | registry: Arc>,
21 | client: Arc,
22 | }
23 |
24 | lazy_static! {
25 | static ref HOST: String = env::var("HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
26 | static ref PORT: String = env::var("PORT").unwrap_or_else(|_| "27017".to_string());
27 | static ref CONNECTION_STRING: String =
28 | format!("mongodb://{}:{}/", HOST.as_str(), PORT.as_str());
29 | }
30 |
31 | #[async_std::main]
32 | async fn main() -> Result<(), std::io::Error> {
33 | let mut client_options = match ClientOptions::parse(&CONNECTION_STRING).await {
34 | Ok(c) => c,
35 | Err(e) => panic!("Client Options Failed: {}", e),
36 | };
37 | //.with_env_filter("async_fn=trace")
38 |
39 | // match init_tracer() {
40 | // Ok(o) => o,
41 | // Err(e) => panic!("tracing failed {}", e),
42 | // };
43 | // Manually set an option.
44 | client_options.app_name = Some("MoRTH".to_string());
45 |
46 | // Get a handle to the deployment.
47 | let client = match Client::with_options(client_options) {
48 | Ok(c) => c,
49 | Err(e) => panic!("Client Creation Failed: {}", e),
50 | };
51 | let mut hb = Handlebars::new();
52 | hb.register_templates_directory(".hbs", "./views").unwrap();
53 |
54 | let engine = State {
55 | registry: Arc::new(hb),
56 | client: Arc::new(client),
57 | };
58 |
59 | let mut app = tide::with_state(engine);
60 | app.at("/").get(tide::Redirect::new("/welcome"));
61 |
62 | app.at("/welcome").get(routes::welcome);
63 |
64 | let mut cities = app.at("/cities");
65 | cities.get(routes::cities::index);
66 | cities
67 | .at("/new")
68 | .get(routes::cities::new)
69 | .post(routes::cities::create);
70 |
71 | cities.at("/:city_id").get(routes::cities::show);
72 | cities
73 | .at("/:city_id/edit")
74 | .get(routes::cities::edit)
75 | .post(routes::cities::update);
76 | cities
77 | .at("/:city_id/delete")
78 | .get(routes::cities::delete);
79 |
80 | app.at("/public").serve_dir("public/")?;
81 | app.listen("127.0.0.1:8080").await?;
82 | Ok(())
83 | }
84 |
85 | // fn init_tracer() -> Result<(), Box> {
86 | // let exporter = opentelemetry_jaeger::Exporter::builder()
87 | // .with_agent_endpoint("127.0.0.1:6831".parse().unwrap())
88 | // .with_process(opentelemetry_jaeger::Process {
89 | // service_name: "morth_example".to_string(),
90 | // tags: Vec::new(),
91 | // })
92 | // .init()?;
93 | // let provider = sdk::Provider::builder()
94 | // .with_simple_exporter(exporter)
95 | // .with_config(sdk::Config {
96 | // default_sampler: Box::new(sdk::Sampler::Always),
97 | // ..Default::default()
98 | // })
99 | // .build();
100 | // let tracer = provider.get_tracer("tracing");
101 |
102 | // let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer);
103 | // tracing_subscriber::registry()
104 | // .with(opentelemetry)
105 | // .try_init()?;
106 |
107 | // Ok(())
108 | // }
109 |
--------------------------------------------------------------------------------
/src/routes/cities.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use crate::models::city::City;
3 | use async_std::stream::StreamExt;
4 | use handlebars::to_json;
5 | use serde_json::value::Map;
6 | use tide::Request;
7 | use tracing::{info, instrument};
8 | use wither::bson::{doc, oid::ObjectId};
9 | use wither::Model;
10 |
11 | #[instrument]
12 | pub async fn index(req: Request) -> tide::Result {
13 | info!("Tracing Started");
14 | let state = &req.state();
15 | let db = &state.client.database("test");
16 |
17 | let mut cursor = City::find(&db.clone(), None, None).await?;
18 | let mut docs: Vec = Vec::new();
19 |
20 | while let Some(city) = cursor.next().await {
21 | docs.push(city?);
22 | }
23 |
24 | let hb = &state.registry;
25 | let mut data = Map::new();
26 | data.insert("title".to_string(), to_json("Cities"));
27 | data.insert("parent".to_string(), to_json("layouts/main"));
28 |
29 | data.insert("cities".to_string(), to_json(&docs));
30 | Ok(hb.render_response_ext("cities/list", &data, "html")?)
31 | }
32 |
33 | #[instrument]
34 | pub async fn show(req: Request) -> tide::Result {
35 | let state = &req.state();
36 | let db = &state.client.database("test");
37 | let id = req.param("city_id")?;
38 |
39 | let object_id = ObjectId::with_string(&id).unwrap();
40 | let filter = doc! { "_id": object_id };
41 |
42 | let doc = City::find_one(&db.clone(), filter, None).await?;
43 | let hb = &state.registry;
44 | let mut data = Map::new();
45 | data.insert("title".to_string(), to_json("Cities"));
46 | data.insert("parent".to_string(), to_json("layouts/main"));
47 |
48 | data.insert("city".to_string(), to_json(&doc));
49 | Ok(hb.render_response_ext("cities/show", &data, "html")?)
50 | }
51 |
52 | #[instrument]
53 | pub async fn edit(req: Request) -> tide::Result {
54 | let state = &req.state();
55 | let db = &state.client.database("test");
56 | let id = req.param("city_id")?;
57 |
58 | let object_id = ObjectId::with_string(&id).unwrap();
59 | let filter = doc! { "_id": object_id };
60 |
61 | let doc = City::find_one(&db.clone(), filter, None).await?;
62 | let hb = &state.registry;
63 | let mut data = Map::new();
64 | data.insert("title".to_string(), to_json("Cities"));
65 | data.insert("parent".to_string(), to_json("layouts/main"));
66 | data.insert(
67 | "action".to_string(),
68 | to_json(format!("/cities/{}/edit", id)),
69 | );
70 | data.insert("city".to_string(), to_json(&doc));
71 | Ok(hb.render_response_ext("cities/form", &data, "html")?)
72 | }
73 |
74 | #[instrument]
75 | pub async fn delete(req: Request) -> tide::Result {
76 | let state = &req.state();
77 | let db = &state.client.database("test");
78 | let id = req.param("city_id")?;
79 |
80 | let object_id = ObjectId::with_string(&id).unwrap();
81 | let filter = doc! { "_id": object_id };
82 |
83 | let _doc = City::find_one_and_delete(&db.clone(), filter, None).await?;
84 |
85 | Ok(tide::Redirect::new(format!("/cities")).into())
86 |
87 | }
88 |
89 | #[instrument]
90 | pub async fn new(req: Request) -> tide::Result {
91 | let hb = &req.state().registry;
92 | let mut data = Map::new();
93 | data.insert("title".to_string(), to_json("New City"));
94 | data.insert("parent".to_string(), to_json("layouts/main"));
95 | data.insert("action".to_string(), to_json("/cities/new"));
96 | Ok(hb.render_response_ext("cities/form", &data, "html")?)
97 | }
98 |
99 | #[instrument]
100 | pub async fn create(mut req: Request) -> tide::Result {
101 | let mut city: City = req.body_form().await?;
102 | let state = &req.state();
103 | let db = &state.client.database("test");
104 | city.save(&db.clone(), None).await?;
105 |
106 | let city_id = city.id.unwrap();
107 |
108 | Ok(tide::Redirect::new(format!("/cities/{}", city_id.to_hex())).into())
109 | }
110 |
111 | #[instrument]
112 | pub async fn update(mut req: Request) -> tide::Result {
113 | let mut city: City = req.body_form().await?;
114 | let state = &req.state();
115 | let db = &state.client.database("test");
116 | let id = req.param("city_id")?;
117 | city.id = Some(ObjectId::with_string(&id).unwrap());
118 |
119 | city.save(&db.clone(), None).await?;
120 |
121 | let city_id = city.id.unwrap();
122 |
123 | Ok(tide::Redirect::new(format!("/cities/{}", city_id.to_hex())).into())
124 | }
125 |
--------------------------------------------------------------------------------
/public/styles/custom.css:
--------------------------------------------------------------------------------
1 | .container {
2 | max-width: 800px; }
3 | .header {
4 | margin-top: 6rem;
5 | text-align: center; }
6 | .value-prop {
7 | margin-top: 1rem; }
8 | .value-props {
9 | margin-top: 4rem;
10 | margin-bottom: 4rem; }
11 | .docs-header {
12 | text-transform: uppercase;
13 | font-size: 1.4rem;
14 | letter-spacing: .2rem;
15 | font-weight: 600; }
16 | .docs-section {
17 | border-top: 1px solid #eee;
18 | padding: 4rem 0;
19 | margin-bottom: 0;}
20 | .value-img {
21 | display: block;
22 | text-align: center;
23 | margin: 2.5rem auto 0; }
24 | .example-grid .column,
25 | .example-grid .columns {
26 | background: #EEE;
27 | text-align: center;
28 | border-radius: 4px;
29 | font-size: 1rem;
30 | text-transform: uppercase;
31 | height: 30px;
32 | line-height: 30px;
33 | margin-bottom: .75rem;
34 | font-weight: 600;
35 | letter-spacing: .1rem; }
36 | .docs-example .row,
37 | .docs-example.row,
38 | .docs-example form {
39 | margin-bottom: 0; }
40 | .docs-example h1,
41 | .docs-example h2,
42 | .docs-example h3,
43 | .docs-example h4,
44 | .docs-example h5,
45 | .docs-example h6 {
46 | margin-bottom: 1rem; }
47 | .heading-font-size {
48 | font-size: 1.2rem;
49 | color: #999;
50 | letter-spacing: normal; }
51 | .code-example {
52 | margin-top: 1.5rem;
53 | margin-bottom: 0; }
54 | .code-example-body {
55 | white-space: pre;
56 | word-wrap: break-word }
57 | .example {
58 | position: relative;
59 | margin-top: 4rem; }
60 | .example-header {
61 | font-weight: 600;
62 | margin-top: 1.5rem;
63 | margin-bottom: .5rem; }
64 | .example-description {
65 | margin-bottom: 1.5rem; }
66 | .example-screenshot-wrapper {
67 | display: block;
68 | position: relative;
69 | overflow: hidden;
70 | border-radius: 6px;
71 | border: 1px solid #eee;
72 | height: 250px; }
73 | .example-screenshot {
74 | width: 100%;
75 | height: auto; }
76 | .example-screenshot.coming-soon {
77 | width: auto;
78 | position: absolute;
79 | background: #eee;
80 | top: 5px;
81 | right: 5px;
82 | bottom: 5px;
83 | left: 5px; }
84 | .navbar {
85 | display: none; }
86 |
87 | /* Larger than phone */
88 | @media (min-width: 550px) {
89 | .header {
90 | margin-top: 18rem; }
91 | .value-props {
92 | margin-top: 9rem;
93 | margin-bottom: 7rem; }
94 | .value-img {
95 | margin-bottom: 1rem; }
96 | .example-grid .column,
97 | .example-grid .columns {
98 | margin-bottom: 1.5rem; }
99 | .docs-section {
100 | padding: 6rem 0; }
101 | .example-send-yourself-copy {
102 | float: right;
103 | margin-top: 12px; }
104 | .example-screenshot-wrapper {
105 | position: absolute;
106 | width: 48%;
107 | height: 100%;
108 | left: 0;
109 | max-height: none; }
110 | }
111 |
112 | /* Larger than tablet */
113 | @media (min-width: 750px) {
114 | /* Navbar */
115 | .navbar + .docs-section {
116 | border-top-width: 0; }
117 | .navbar,
118 | .navbar-spacer {
119 | display: block;
120 | width: 100%;
121 | height: 6.5rem;
122 | background: #fff;
123 | z-index: 99;
124 | border-top: 1px solid #eee;
125 | border-bottom: 1px solid #eee; }
126 | .navbar-spacer {
127 | display: none; }
128 | .navbar > .container {
129 | width: 100%; }
130 | .navbar-list {
131 | list-style: none;
132 | margin-bottom: 0; }
133 | .navbar-item {
134 | position: relative;
135 | float: left;
136 | margin-bottom: 0; }
137 | .navbar-link {
138 | text-transform: uppercase;
139 | font-size: 11px;
140 | font-weight: 600;
141 | letter-spacing: .2rem;
142 | margin-right: 35px;
143 | text-decoration: none;
144 | line-height: 6.5rem;
145 | color: #222; }
146 | .navbar-link.active {
147 | color: #33C3F0; }
148 | .has-docked-nav .navbar {
149 | position: fixed;
150 | top: 0;
151 | left: 0; }
152 | .has-docked-nav .navbar-spacer {
153 | display: block; }
154 | /* Re-overiding the width 100% declaration to match size of % based container */
155 | .has-docked-nav .navbar > .container {
156 | width: 80%; }
157 |
158 | /* Popover */
159 | .popover.open {
160 | display: block;
161 | }
162 | .popover {
163 | display: none;
164 | position: absolute;
165 | top: 0;
166 | left: 0;
167 | background: #fff;
168 | border: 1px solid #eee;
169 | border-radius: 4px;
170 | top: 92%;
171 | left: -50%;
172 | -webkit-filter: drop-shadow(0 0 6px rgba(0,0,0,.1));
173 | -moz-filter: drop-shadow(0 0 6px rgba(0,0,0,.1));
174 | filter: drop-shadow(0 0 6px rgba(0,0,0,.1)); }
175 | .popover-item:first-child .popover-link:after,
176 | .popover-item:first-child .popover-link:before {
177 | bottom: 100%;
178 | left: 50%;
179 | border: solid transparent;
180 | content: " ";
181 | height: 0;
182 | width: 0;
183 | position: absolute;
184 | pointer-events: none; }
185 | .popover-item:first-child .popover-link:after {
186 | border-color: rgba(255, 255, 255, 0);
187 | border-bottom-color: #fff;
188 | border-width: 10px;
189 | margin-left: -10px; }
190 | .popover-item:first-child .popover-link:before {
191 | border-color: rgba(238, 238, 238, 0);
192 | border-bottom-color: #eee;
193 | border-width: 11px;
194 | margin-left: -11px; }
195 | .popover-list {
196 | padding: 0;
197 | margin: 0;
198 | list-style: none; }
199 | .popover-item {
200 | padding: 0;
201 | margin: 0; }
202 | .popover-link {
203 | position: relative;
204 | color: #222;
205 | display: block;
206 | padding: 8px 20px;
207 | border-bottom: 1px solid #eee;
208 | text-decoration: none;
209 | text-transform: uppercase;
210 | font-size: 1.0rem;
211 | font-weight: 600;
212 | text-align: center;
213 | letter-spacing: .1rem; }
214 | .popover-item:first-child .popover-link {
215 | border-radius: 4px 4px 0 0; }
216 | .popover-item:last-child .popover-link {
217 | border-radius: 0 0 4px 4px;
218 | border-bottom-width: 0; }
219 | .popover-link:hover {
220 | color: #fff;
221 | background: #33C3F0; }
222 | .popover-link:hover,
223 | .popover-item:first-child .popover-link:hover:after {
224 | border-bottom-color: #33C3F0; }
225 | }
--------------------------------------------------------------------------------
/public/styles/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */
2 |
3 | /**
4 | * 1. Set default font family to sans-serif.
5 | * 2. Prevent iOS text size adjust after orientation change, without disabling
6 | * user zoom.
7 | */
8 |
9 | html {
10 | font-family: sans-serif; /* 1 */
11 | -ms-text-size-adjust: 100%; /* 2 */
12 | -webkit-text-size-adjust: 100%; /* 2 */
13 | }
14 |
15 | /**
16 | * Remove default margin.
17 | */
18 |
19 | body {
20 | margin: 0;
21 | }
22 |
23 | /* HTML5 display definitions
24 | ========================================================================== */
25 |
26 | /**
27 | * Correct `block` display not defined for any HTML5 element in IE 8/9.
28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11
29 | * and Firefox.
30 | * Correct `block` display not defined for `main` in IE 11.
31 | */
32 |
33 | article,
34 | aside,
35 | details,
36 | figcaption,
37 | figure,
38 | footer,
39 | header,
40 | hgroup,
41 | main,
42 | menu,
43 | nav,
44 | section,
45 | summary {
46 | display: block;
47 | }
48 |
49 | /**
50 | * 1. Correct `inline-block` display not defined in IE 8/9.
51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
52 | */
53 |
54 | audio,
55 | canvas,
56 | progress,
57 | video {
58 | display: inline-block; /* 1 */
59 | vertical-align: baseline; /* 2 */
60 | }
61 |
62 | /**
63 | * Prevent modern browsers from displaying `audio` without controls.
64 | * Remove excess height in iOS 5 devices.
65 | */
66 |
67 | audio:not([controls]) {
68 | display: none;
69 | height: 0;
70 | }
71 |
72 | /**
73 | * Address `[hidden]` styling not present in IE 8/9/10.
74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
75 | */
76 |
77 | [hidden],
78 | template {
79 | display: none;
80 | }
81 |
82 | /* Links
83 | ========================================================================== */
84 |
85 | /**
86 | * Remove the gray background color from active links in IE 10.
87 | */
88 |
89 | a {
90 | background-color: transparent;
91 | }
92 |
93 | /**
94 | * Improve readability when focused and also mouse hovered in all browsers.
95 | */
96 |
97 | a:active,
98 | a:hover {
99 | outline: 0;
100 | }
101 |
102 | /* Text-level semantics
103 | ========================================================================== */
104 |
105 | /**
106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
107 | */
108 |
109 | abbr[title] {
110 | border-bottom: 1px dotted;
111 | }
112 |
113 | /**
114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
115 | */
116 |
117 | b,
118 | strong {
119 | font-weight: bold;
120 | }
121 |
122 | /**
123 | * Address styling not present in Safari and Chrome.
124 | */
125 |
126 | dfn {
127 | font-style: italic;
128 | }
129 |
130 | /**
131 | * Address variable `h1` font-size and margin within `section` and `article`
132 | * contexts in Firefox 4+, Safari, and Chrome.
133 | */
134 |
135 | h1 {
136 | font-size: 2em;
137 | margin: 0.67em 0;
138 | }
139 |
140 | /**
141 | * Address styling not present in IE 8/9.
142 | */
143 |
144 | mark {
145 | background: #ff0;
146 | color: #000;
147 | }
148 |
149 | /**
150 | * Address inconsistent and variable font size in all browsers.
151 | */
152 |
153 | small {
154 | font-size: 80%;
155 | }
156 |
157 | /**
158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
159 | */
160 |
161 | sub,
162 | sup {
163 | font-size: 75%;
164 | line-height: 0;
165 | position: relative;
166 | vertical-align: baseline;
167 | }
168 |
169 | sup {
170 | top: -0.5em;
171 | }
172 |
173 | sub {
174 | bottom: -0.25em;
175 | }
176 |
177 | /* Embedded content
178 | ========================================================================== */
179 |
180 | /**
181 | * Remove border when inside `a` element in IE 8/9/10.
182 | */
183 |
184 | img {
185 | border: 0;
186 | }
187 |
188 | /**
189 | * Correct overflow not hidden in IE 9/10/11.
190 | */
191 |
192 | svg:not(:root) {
193 | overflow: hidden;
194 | }
195 |
196 | /* Grouping content
197 | ========================================================================== */
198 |
199 | /**
200 | * Address margin not present in IE 8/9 and Safari.
201 | */
202 |
203 | figure {
204 | margin: 1em 40px;
205 | }
206 |
207 | /**
208 | * Address differences between Firefox and other browsers.
209 | */
210 |
211 | hr {
212 | -moz-box-sizing: content-box;
213 | box-sizing: content-box;
214 | height: 0;
215 | }
216 |
217 | /**
218 | * Contain overflow in all browsers.
219 | */
220 |
221 | pre {
222 | overflow: auto;
223 | }
224 |
225 | /**
226 | * Address odd `em`-unit font size rendering in all browsers.
227 | */
228 |
229 | code,
230 | kbd,
231 | pre,
232 | samp {
233 | font-family: monospace, monospace;
234 | font-size: 1em;
235 | }
236 |
237 | /* Forms
238 | ========================================================================== */
239 |
240 | /**
241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited
242 | * styling of `select`, unless a `border` property is set.
243 | */
244 |
245 | /**
246 | * 1. Correct color not being inherited.
247 | * Known issue: affects color of disabled elements.
248 | * 2. Correct font properties not being inherited.
249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
250 | */
251 |
252 | button,
253 | input,
254 | optgroup,
255 | select,
256 | textarea {
257 | color: inherit; /* 1 */
258 | font: inherit; /* 2 */
259 | margin: 0; /* 3 */
260 | }
261 |
262 | /**
263 | * Address `overflow` set to `hidden` in IE 8/9/10/11.
264 | */
265 |
266 | button {
267 | overflow: visible;
268 | }
269 |
270 | /**
271 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
272 | * All other form control elements do not inherit `text-transform` values.
273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
274 | * Correct `select` style inheritance in Firefox.
275 | */
276 |
277 | button,
278 | select {
279 | text-transform: none;
280 | }
281 |
282 | /**
283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
284 | * and `video` controls.
285 | * 2. Correct inability to style clickable `input` types in iOS.
286 | * 3. Improve usability and consistency of cursor style between image-type
287 | * `input` and others.
288 | */
289 |
290 | button,
291 | html input[type="button"], /* 1 */
292 | input[type="reset"],
293 | input[type="submit"] {
294 | -webkit-appearance: button; /* 2 */
295 | cursor: pointer; /* 3 */
296 | }
297 |
298 | /**
299 | * Re-set default cursor for disabled elements.
300 | */
301 |
302 | button[disabled],
303 | html input[disabled] {
304 | cursor: default;
305 | }
306 |
307 | /**
308 | * Remove inner padding and border in Firefox 4+.
309 | */
310 |
311 | button::-moz-focus-inner,
312 | input::-moz-focus-inner {
313 | border: 0;
314 | padding: 0;
315 | }
316 |
317 | /**
318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
319 | * the UA stylesheet.
320 | */
321 |
322 | input {
323 | line-height: normal;
324 | }
325 |
326 | /**
327 | * It's recommended that you don't attempt to style these elements.
328 | * Firefox's implementation doesn't respect box-sizing, padding, or width.
329 | *
330 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
331 | * 2. Remove excess padding in IE 8/9/10.
332 | */
333 |
334 | input[type="checkbox"],
335 | input[type="radio"] {
336 | box-sizing: border-box; /* 1 */
337 | padding: 0; /* 2 */
338 | }
339 |
340 | /**
341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain
342 | * `font-size` values of the `input`, it causes the cursor style of the
343 | * decrement button to change from `default` to `text`.
344 | */
345 |
346 | input[type="number"]::-webkit-inner-spin-button,
347 | input[type="number"]::-webkit-outer-spin-button {
348 | height: auto;
349 | }
350 |
351 | /**
352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
354 | * (include `-moz` to future-proof).
355 | */
356 |
357 | input[type="search"] {
358 | -webkit-appearance: textfield; /* 1 */
359 | -moz-box-sizing: content-box;
360 | -webkit-box-sizing: content-box; /* 2 */
361 | box-sizing: content-box;
362 | }
363 |
364 | /**
365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X.
366 | * Safari (but not Chrome) clips the cancel button when the search input has
367 | * padding (and `textfield` appearance).
368 | */
369 |
370 | input[type="search"]::-webkit-search-cancel-button,
371 | input[type="search"]::-webkit-search-decoration {
372 | -webkit-appearance: none;
373 | }
374 |
375 | /**
376 | * Define consistent border, margin, and padding.
377 | */
378 |
379 | fieldset {
380 | border: 1px solid #c0c0c0;
381 | margin: 0 2px;
382 | padding: 0.35em 0.625em 0.75em;
383 | }
384 |
385 | /**
386 | * 1. Correct `color` not being inherited in IE 8/9/10/11.
387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
388 | */
389 |
390 | legend {
391 | border: 0; /* 1 */
392 | padding: 0; /* 2 */
393 | }
394 |
395 | /**
396 | * Remove default vertical scrollbar in IE 8/9/10/11.
397 | */
398 |
399 | textarea {
400 | overflow: auto;
401 | }
402 |
403 | /**
404 | * Don't inherit the `font-weight` (applied by a rule above).
405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
406 | */
407 |
408 | optgroup {
409 | font-weight: bold;
410 | }
411 |
412 | /* Tables
413 | ========================================================================== */
414 |
415 | /**
416 | * Remove most spacing between table cells.
417 | */
418 |
419 | table {
420 | border-collapse: collapse;
421 | border-spacing: 0;
422 | }
423 |
424 | td,
425 | th {
426 | padding: 0;
427 | }
--------------------------------------------------------------------------------
/public/styles/skeleton.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Skeleton V2.0.4
3 | * Copyright 2014, Dave Gamache
4 | * www.getskeleton.com
5 | * Free to use under the MIT license.
6 | * http://www.opensource.org/licenses/mit-license.php
7 | * 12/29/2014
8 | */
9 |
10 |
11 | /* Table of contents
12 | ––––––––––––––––––––––––––––––––––––––––––––––––––
13 | - Grid
14 | - Base Styles
15 | - Typography
16 | - Links
17 | - Buttons
18 | - Forms
19 | - Lists
20 | - Code
21 | - Tables
22 | - Spacing
23 | - Utilities
24 | - Clearing
25 | - Media Queries
26 | */
27 |
28 |
29 | /* Grid
30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
31 | .container {
32 | position: relative;
33 | width: 100%;
34 | max-width: 960px;
35 | margin: 0 auto;
36 | padding: 0 20px;
37 | box-sizing: border-box; }
38 | .column,
39 | .columns {
40 | width: 100%;
41 | float: left;
42 | box-sizing: border-box; }
43 |
44 | /* For devices larger than 400px */
45 | @media (min-width: 400px) {
46 | .container {
47 | width: 85%;
48 | padding: 0; }
49 | }
50 |
51 | /* For devices larger than 550px */
52 | @media (min-width: 550px) {
53 | .container {
54 | width: 80%; }
55 | .column,
56 | .columns {
57 | margin-left: 4%; }
58 | .column:first-child,
59 | .columns:first-child {
60 | margin-left: 0; }
61 |
62 | .one.column,
63 | .one.columns { width: 4.66666666667%; }
64 | .two.columns { width: 13.3333333333%; }
65 | .three.columns { width: 22%; }
66 | .four.columns { width: 30.6666666667%; }
67 | .five.columns { width: 39.3333333333%; }
68 | .six.columns { width: 48%; }
69 | .seven.columns { width: 56.6666666667%; }
70 | .eight.columns { width: 65.3333333333%; }
71 | .nine.columns { width: 74.0%; }
72 | .ten.columns { width: 82.6666666667%; }
73 | .eleven.columns { width: 91.3333333333%; }
74 | .twelve.columns { width: 100%; margin-left: 0; }
75 |
76 | .one-third.column { width: 30.6666666667%; }
77 | .two-thirds.column { width: 65.3333333333%; }
78 |
79 | .one-half.column { width: 48%; }
80 |
81 | /* Offsets */
82 | .offset-by-one.column,
83 | .offset-by-one.columns { margin-left: 8.66666666667%; }
84 | .offset-by-two.column,
85 | .offset-by-two.columns { margin-left: 17.3333333333%; }
86 | .offset-by-three.column,
87 | .offset-by-three.columns { margin-left: 26%; }
88 | .offset-by-four.column,
89 | .offset-by-four.columns { margin-left: 34.6666666667%; }
90 | .offset-by-five.column,
91 | .offset-by-five.columns { margin-left: 43.3333333333%; }
92 | .offset-by-six.column,
93 | .offset-by-six.columns { margin-left: 52%; }
94 | .offset-by-seven.column,
95 | .offset-by-seven.columns { margin-left: 60.6666666667%; }
96 | .offset-by-eight.column,
97 | .offset-by-eight.columns { margin-left: 69.3333333333%; }
98 | .offset-by-nine.column,
99 | .offset-by-nine.columns { margin-left: 78.0%; }
100 | .offset-by-ten.column,
101 | .offset-by-ten.columns { margin-left: 86.6666666667%; }
102 | .offset-by-eleven.column,
103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; }
104 |
105 | .offset-by-one-third.column,
106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; }
107 | .offset-by-two-thirds.column,
108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; }
109 |
110 | .offset-by-one-half.column,
111 | .offset-by-one-half.columns { margin-left: 52%; }
112 |
113 | }
114 |
115 |
116 | /* Base Styles
117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
118 | /* NOTE
119 | html is set to 62.5% so that all the REM measurements throughout Skeleton
120 | are based on 10px sizing. So basically 1.5rem = 15px :) */
121 | html {
122 | font-size: 62.5%; }
123 | body {
124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */
125 | line-height: 1.6;
126 | font-weight: 400;
127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
128 | color: #222; }
129 |
130 |
131 | /* Typography
132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
133 | h1, h2, h3, h4, h5, h6 {
134 | margin-top: 0;
135 | margin-bottom: 2rem;
136 | font-weight: 300; }
137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;}
138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; }
139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; }
140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; }
141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; }
142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; }
143 |
144 | /* Larger than phablet */
145 | @media (min-width: 550px) {
146 | h1 { font-size: 5.0rem; }
147 | h2 { font-size: 4.2rem; }
148 | h3 { font-size: 3.6rem; }
149 | h4 { font-size: 3.0rem; }
150 | h5 { font-size: 2.4rem; }
151 | h6 { font-size: 1.5rem; }
152 | }
153 |
154 | p {
155 | margin-top: 0; }
156 |
157 |
158 | /* Links
159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
160 | a {
161 | color: #1EAEDB; }
162 | a:hover {
163 | color: #0FA0CE; }
164 |
165 |
166 | /* Buttons
167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
168 | .button,
169 | button,
170 | input[type="submit"],
171 | input[type="reset"],
172 | input[type="button"] {
173 | display: inline-block;
174 | height: 38px;
175 | padding: 0 30px;
176 | color: #555;
177 | text-align: center;
178 | font-size: 11px;
179 | font-weight: 600;
180 | line-height: 38px;
181 | letter-spacing: .1rem;
182 | text-transform: uppercase;
183 | text-decoration: none;
184 | white-space: nowrap;
185 | background-color: transparent;
186 | border-radius: 4px;
187 | border: 1px solid #bbb;
188 | cursor: pointer;
189 | box-sizing: border-box; }
190 | .button:hover,
191 | button:hover,
192 | input[type="submit"]:hover,
193 | input[type="reset"]:hover,
194 | input[type="button"]:hover,
195 | .button:focus,
196 | button:focus,
197 | input[type="submit"]:focus,
198 | input[type="reset"]:focus,
199 | input[type="button"]:focus {
200 | color: #333;
201 | border-color: #888;
202 | outline: 0; }
203 | .button.button-primary,
204 | button.button-primary,
205 | input[type="submit"].button-primary,
206 | input[type="reset"].button-primary,
207 | input[type="button"].button-primary {
208 | color: #FFF;
209 | background-color: #33C3F0;
210 | border-color: #33C3F0; }
211 | .button.button-primary:hover,
212 | button.button-primary:hover,
213 | input[type="submit"].button-primary:hover,
214 | input[type="reset"].button-primary:hover,
215 | input[type="button"].button-primary:hover,
216 | .button.button-primary:focus,
217 | button.button-primary:focus,
218 | input[type="submit"].button-primary:focus,
219 | input[type="reset"].button-primary:focus,
220 | input[type="button"].button-primary:focus {
221 | color: #FFF;
222 | background-color: #1EAEDB;
223 | border-color: #1EAEDB; }
224 |
225 |
226 | /* Forms
227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
228 | input[type="email"],
229 | input[type="number"],
230 | input[type="search"],
231 | input[type="text"],
232 | input[type="tel"],
233 | input[type="url"],
234 | input[type="password"],
235 | textarea,
236 | select {
237 | height: 38px;
238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
239 | background-color: #fff;
240 | border: 1px solid #D1D1D1;
241 | border-radius: 4px;
242 | box-shadow: none;
243 | box-sizing: border-box; }
244 | /* Removes awkward default styles on some inputs for iOS */
245 | input[type="email"],
246 | input[type="number"],
247 | input[type="search"],
248 | input[type="text"],
249 | input[type="tel"],
250 | input[type="url"],
251 | input[type="password"],
252 | textarea {
253 | -webkit-appearance: none;
254 | -moz-appearance: none;
255 | appearance: none; }
256 | textarea {
257 | min-height: 65px;
258 | padding-top: 6px;
259 | padding-bottom: 6px; }
260 | input[type="email"]:focus,
261 | input[type="number"]:focus,
262 | input[type="search"]:focus,
263 | input[type="text"]:focus,
264 | input[type="tel"]:focus,
265 | input[type="url"]:focus,
266 | input[type="password"]:focus,
267 | textarea:focus,
268 | select:focus {
269 | border: 1px solid #33C3F0;
270 | outline: 0; }
271 | label,
272 | legend {
273 | display: block;
274 | margin-bottom: .5rem;
275 | font-weight: 600; }
276 | fieldset {
277 | padding: 0;
278 | border-width: 0; }
279 | input[type="checkbox"],
280 | input[type="radio"] {
281 | display: inline; }
282 | label > .label-body {
283 | display: inline-block;
284 | margin-left: .5rem;
285 | font-weight: normal; }
286 |
287 |
288 | /* Lists
289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
290 | ul {
291 | list-style: circle inside; }
292 | ol {
293 | list-style: decimal inside; }
294 | ol, ul {
295 | padding-left: 0;
296 | margin-top: 0; }
297 | ul ul,
298 | ul ol,
299 | ol ol,
300 | ol ul {
301 | margin: 1.5rem 0 1.5rem 3rem;
302 | font-size: 90%; }
303 | li {
304 | margin-bottom: 1rem; }
305 |
306 |
307 | /* Code
308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
309 | code {
310 | padding: .2rem .5rem;
311 | margin: 0 .2rem;
312 | font-size: 90%;
313 | white-space: nowrap;
314 | background: #F1F1F1;
315 | border: 1px solid #E1E1E1;
316 | border-radius: 4px; }
317 | pre > code {
318 | display: block;
319 | padding: 1rem 1.5rem;
320 | white-space: pre; }
321 |
322 |
323 | /* Tables
324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
325 | th,
326 | td {
327 | padding: 12px 15px;
328 | text-align: left;
329 | border-bottom: 1px solid #E1E1E1; }
330 | th:first-child,
331 | td:first-child {
332 | padding-left: 0; }
333 | th:last-child,
334 | td:last-child {
335 | padding-right: 0; }
336 |
337 |
338 | /* Spacing
339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
340 | button,
341 | .button {
342 | margin-bottom: 1rem; }
343 | input,
344 | textarea,
345 | select,
346 | fieldset {
347 | margin-bottom: 1.5rem; }
348 | pre,
349 | blockquote,
350 | dl,
351 | figure,
352 | table,
353 | p,
354 | ul,
355 | ol,
356 | form {
357 | margin-bottom: 2.5rem; }
358 |
359 |
360 | /* Utilities
361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
362 | .u-full-width {
363 | width: 100%;
364 | box-sizing: border-box; }
365 | .u-max-full-width {
366 | max-width: 100%;
367 | box-sizing: border-box; }
368 | .u-pull-right {
369 | float: right; }
370 | .u-pull-left {
371 | float: left; }
372 |
373 |
374 | /* Misc
375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
376 | hr {
377 | margin-top: 3rem;
378 | margin-bottom: 3.5rem;
379 | border-width: 0;
380 | border-top: 1px solid #E1E1E1; }
381 |
382 |
383 | /* Clearing
384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
385 |
386 | /* Self Clearing Goodness */
387 | .container:after,
388 | .row:after,
389 | .u-cf {
390 | content: "";
391 | display: table;
392 | clear: both; }
393 |
394 |
395 | /* Media Queries
396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */
397 | /*
398 | Note: The best way to structure the use of media queries is to create the queries
399 | near the relevant code. For example, if you wanted to change the styles for buttons
400 | on small devices, paste the mobile query code up in the buttons section and style it
401 | there.
402 | */
403 |
404 |
405 | /* Larger than mobile */
406 | @media (min-width: 400px) {}
407 |
408 | /* Larger than phablet (also point when grid becomes active) */
409 | @media (min-width: 550px) {}
410 |
411 | /* Larger than tablet */
412 | @media (min-width: 750px) {}
413 |
414 | /* Larger than desktop */
415 | @media (min-width: 1000px) {}
416 |
417 | /* Larger than Desktop HD */
418 | @media (min-width: 1200px) {}
419 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------