Hardware components | ||||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
| ||||||
| ||||||
|
Once we had automated turning the sockets on and off, it just seemed natural to want voice controlled lights! (who doesn't!)
With a simple sketch on our WiFi connected board, and a port forwarding setup on our router, we can connect directly to If This Then That and Google / Amazon for voice control services and more!
Getting your Arduino OnlineFirst we need to make our Arduino accessible from anywhere on the internet, using Port Forwarding on your local router.
To do this you will need to set your Arduino to a Static IP Address (or add a Reservation for its MAC on your Router).
Then we will need to decide on a port (e.g. 9090) to serve our Arduino Webserver on (80 is in use by your routers' web interface externally normally, you can use this so all requests hit your Arduino without any other changes).
Once that's decided we can add a Service which defines the port ranges, and then add it as a rule to allocate it to the IP address from the previous step.
Now that's configured you can access it via the External IP of your router (available on your Router or at https://www.whatismyip.com/) with a Port e.g. http://90.98.241.123:9090,
To make things easier to access, set up a Dynamic DNS name (e.g. https://www.noip.com/), which in many cases can be entered on the Router directly, then you can access it via a more memorable URL such as http://visualmicrohomeproject.ddns.net:9090
IFThis Then That provides a linking service between a wide variety of tools such as Google Assistant / Alexa, as well as being able to link plain Web hooks as well.
First register with IFTTT with the same Google Account as on your Assistant device.
(If you don't use the same account it will not work)
IFTTT SetupNow you're logged in > Click on Create > Click "This"
Then Search for Google > Select Google Assistant
At this point you can configure different phrase types, in this example we will use a word "On" or "Off" as our variable, in the phrase "Turn the light On/Off" which we enter as "Turn the light $"
Now we need to link this Trigger by clicking on "That"
Search for Webhooks > Select Webhooks
Here we configure our URL and Port as setup in the previous step, and we can add our "ingredient" variable from above.
For simplicity we will add it to the URL here, but if you were sending more data it would generally go in the body, often in a JSON Format, where you can use the Arduino JSON library to parse it on your Arduino.
Once the applet is configured, we can amend the code on our board as needed.
Simply amend the Wifi password and SSID if using an ESP8266/8285/32 board, it will automatically include the relevant libraries.
For other boards adjust the headers and relevant web methods as needed.
Now you should be able to say to your assistant "Hey Google, turn the light On", and it will respond with the phrase you said.
The light should also be on now as well!
You will also see in your Serial Monitor that there is a request sent in with the On Keyword appended to the URL, and your Arduino will have sent the RF message to turn the light on from the code changes.
From here the sky is the limit, as you can now have your own custom web interface on the device itself, as well as send data to and from it using IFTTT, and control your projects from your Voice Enabled Assistant whether it be a phone or physical device.
Arduino Example Software for ESP32/ESP8266
ArduinoAlso has web UI which allows for web page control of the socket as well.
Change headers if using a different board
/*
Name: ESP32_WebServerApp_Example.ino
Created: 3/14/2019 12:06:43 AM
Author: Simon
Simple WebApp on SPIFFS Served Locally via mDNS Name or IP
Example #1 - RF Lightswitches - ESP32 as WiFi Client, Simple AJAX Requests or whole page loads for now (no WS yet)
Upload and go to http://esp32.local (or inspect Serial monitor if connecting via IP)
*/
#ifdef ESP32
#include <spiffs.h>
#include <WiFi.h>
#include <ESPmDNS.h>
#define FORMAT_SPIFFS_IF_FAILED false // You may have to set this to true the first time you run this
#endif
#ifdef ESP8266
#include <FS.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#define FORMAT_SPIFFS_IF_FAILED // Leave Empty on ESP8266
#endif
#include <RCSwitch.h>
// WiFi Client Settings
const char* ssid = "**************";
const char* password = "**************";
const char* mDnsName = "esp32";
const byte hwPin_rfTx = 2;
WiFiServer server(9090);
RCSwitch mySwitch = RCSwitch();
File root;
void setup() {
Serial.begin(115200);
delay(5000);
// Setup our File System in SPIFFS
if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {
Serial.println("SPIFFS Mount Failed");
return;
}
root = SPIFFS.open("/", "r");
// Static IP Configuration
IPAddress local_IP(192, 168, 0, 55);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(192, 168, 0, 1);
IPAddress secondaryDNS(8, 8, 4, 4); // fake
WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);
// Setup the Web Connection
WiFi.begin(ssid, password);
Serial.println(F("Connecting to Wifi. "));
while (!WL_CONNECTED || WiFi.localIP().toString() == "0.0.0.0") {
delay(500); // Yield() sdasdasdasd
Serial.print(". ");
}
// Setup our Remote Controlled Wall Socket
mySwitch.enableTransmit(2);
Serial.print("Connected to ");
Serial.print(ssid);
Serial.print(" on IP address: ");
Serial.println(WiFi.localIP());
// Set up mDNS responder:
// - first argument is the domain name, in this example
// the fully-qualified domain name is "mDnsName.local"
if (!MDNS.begin(mDnsName)) {
Serial.println("Error setting up MDNS responder!");
}
else {
Serial.println("mDNS responder started");
}
// Finally startup the web server
server.begin();
Serial.println("TCP server started.");
// Bind the HTTP requests via MDNS
MDNS.addService("http", "tcp", 9090);
}
void loop(void) {
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // Someone is talking to us...
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
String req = client.readStringUntil('\r'); // Read the first line of HTTP request
// First line of HTTP request looks like "GET /path HTTP/1.1"
// Retrieve the "/path" part by finding the spaces
int addr_start = req.indexOf(' ');
int addr_end = req.indexOf(' ', addr_start + 1);
if (addr_start == -1 || addr_end == -1) {
Serial.print("Invalid request: ");
Serial.println(req);
break;
}
req = req.substring(addr_start + 1, addr_end);
Serial.print("Request: ");
Serial.println(req);
// Set Access Control Policy to allow CORS (Not actually needed here at present...)
client.println("HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: text/html\r\n\r\n");
handleFileRead(req, client);
}
// close the connection:
client.stop();
Serial.println("Client Disconnected.");
}
}
// Handle all the content types we may get asked for from our SPIFFS
String getContentType(String filename) {
if (filename.endsWith(".htm")) return "text/html";
else if (filename.endsWith(".html")) return "text/html";
else if (filename.endsWith(".css")) return "text/css";
else if (filename.endsWith(".js")) return "application/javascript";
else if (filename.endsWith(".png")) return "image/png";
else if (filename.endsWith(".gif")) return "image/gif";
else if (filename.endsWith(".jpg")) return "image/jpeg";
else if (filename.endsWith(".ico")) return "image/x-icon";
else if (filename.endsWith(".xml")) return "text/xml";
return "text/plain";
}
// Handle a File request either via our custom handlers, or simply serving a file...
bool handleFileRead(String path, WiFiClient &client) {
Serial.println("handleFileRead: " + path);
if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file
String contentType = getContentType(path); // Get the MIME type
// Simple hardcoding for now but can easily be expanded into the below
// If we used a command with a number it would map in but "turn the light 1" sounds odd..
if (path.startsWith("/on")) {
mySwitch.switchOn(1, 1);
}
else if (path.startsWith("/off")) {
mySwitch.switchOff(1, 1);
}
else
// Handle our "custom" (non-file) requests first
// We Expect the URL in format /socket/1/1/1 meaning:Socket 1, Address 1, Action ON
if (path.startsWith("/socket/")) {
int socket = path.charAt(8) - 48;
int address = path.charAt(10) - 48;
int action = path.charAt(12) - 48;
client.print("Socket ");
client.print(socket);
client.print(":");
client.print(address);
if (action == 1) {
mySwitch.switchOn(socket, address);
client.println(" ON");
return true;
}
else {
mySwitch.switchOff(socket, address);
client.println(" OFF");
return true;
}
}
else if (SPIFFS.exists(path)) { // If the file exists
File file = SPIFFS.open(path, "r"); // Open it
client.print(file.readStringUntil(EOF)); // Read Until End Of File back to client
file.close(); // Then close the file again
return true;
}
Serial.println("\tFile Not Found");
client.print("<b>Invalid Request</b>"); // Show the user some info on web page
return false; // If the file doesn't exist, return false
}
data\Index.html
HTMLFor ESP place in "sketchFolder\data" for upload via SPIFFS
Uses simple responsive framework (included in other files)
<HTML>
<HEAD>
<!-- Basic Page Needs -->
<meta charset="utf-8">
<title>ESP Web App</title>
<meta name="description" content="Starting you Off with IoT">
<meta name="author" content="vMicro">
<!-- Mobile Specific Metas -->
<meta name="viewport" content="width=device-width, initial-scale=1; maximum-scale=5.0; user-scalable=0;">
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- CSS -->
<link rel="stylesheet" href="normalize.css">
<link rel="stylesheet" href="skeleton.css">
<!-- Client Scripts -->
<script type="text/javascript" src="/ClientSideScript.js"></script>
</HEAD>
<BODY>
<div class="container">
<div class="row">
<h2>Embedded SPIFFS ESP Application (8266/8285/32)</h2>
<p>About as simple as it can be made, and all simplistic, easy to edit code.....</p>
</div>
<div class="row">
<button id="on" class="button-primary">1-1 On</button>
<button id="off">1-1 Off</button>
</div>
<div class="row">
<div>Response: <span id="infoPanel"></span></div>
</div>
</div>
</BODY>
</HTML>
ClientSideScript.js
JavaScriptFor ESP place in "sketchFolder\data" for upload via SPIFFS
// Everything in pure JS to save codespace for your preferred Libs
window.addEventListener("load", onWindowLoad);
// This all kicks in when the page lands on the client browser
function onWindowLoad() {
console.log("Page Loaded");
// Add some Listeners to our buttons to send requests back
document.getElementById("on").addEventListener("click", sendOnRequest);
document.getElementById("off").addEventListener("click", sendOffRequest);
}
function sendOnRequest() {
sendAjaxRequest(
function () {
document.getElementById("infoPanel").innerHTML = this.responseText;
}
, "GET"
, "/socket/1/1/1"
);
}
function sendOffRequest() {
sendAjaxRequest(
function () {
document.getElementById("infoPanel").innerHTML = this.responseText;
}
, "GET"
, "/socket/1/1/0"
);
}
// Generic wrapper to send requests to the server
function sendAjaxRequest(onReplyFunction, method, url) {
var xhttp = new XMLHttpRequest()
// What happens when the server replies with an answer
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
onReplyFunction.call(this);
}
};
xhttp.open(method, url, true);
xhttp.send();
}
normalize.css
CSSFor ESP place in "sketchFolder\data" for upload via SPIFFS
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
skeleton.css
CSSFor ESP place in "sketchFolder\data" for upload via SPIFFS
/*
* Skeleton V2.0.4
* Copyright 2014, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 12/29/2014
*/
/* Table of contents
- Grid
- Base Styles
- Typography
- Links
- Buttons
- Forms
- Lists
- Code
- Tables
- Spacing
- Utilities
- Clearing
- Media Queries
*/
/* Grid
*/
.container {
position: relative;
width: 100%;
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box; }
.column,
.columns {
width: 100%;
float: left;
box-sizing: border-box; }
/* For devices larger than 400px */
@media (min-width: 400px) {
.container {
width: 85%;
padding: 0; }
}
/* For devices larger than 550px */
/* HACKED To ensure it remains the same on mobile */
@media (min-width: 320px) {
.container {
width: 80%; }
.column,
.columns {
margin-left: 4%; }
.column:first-child,
.columns:first-child {
margin-left: 0; }
.one.column,
.one.columns { width: 4.66666666667%; }
.two.columns { width: 13.3333333333%; }
.three.columns { width: 22%; }
.four.columns { width: 30.6666666667%; }
.five.columns { width: 39.3333333333%; }
.six.columns { width: 48%; }
.seven.columns { width: 56.6666666667%; }
.eight.columns { width: 65.3333333333%; }
.nine.columns { width: 74.0%; }
.ten.columns { width: 82.6666666667%; }
.eleven.columns { width: 91.3333333333%; }
.twelve.columns { width: 100%; margin-left: 0; }
.one-third.column { width: 30.6666666667%; }
.two-thirds.column { width: 65.3333333333%; }
.one-half.column { width: 48%; }
/* Offsets */
.offset-by-one.column,
.offset-by-one.columns { margin-left: 8.66666666667%; }
.offset-by-two.column,
.offset-by-two.columns { margin-left: 17.3333333333%; }
.offset-by-three.column,
.offset-by-three.columns { margin-left: 26%; }
.offset-by-four.column,
.offset-by-four.columns { margin-left: 34.6666666667%; }
.offset-by-five.column,
.offset-by-five.columns { margin-left: 43.3333333333%; }
.offset-by-six.column,
.offset-by-six.columns { margin-left: 52%; }
.offset-by-seven.column,
.offset-by-seven.columns { margin-left: 60.6666666667%; }
.offset-by-eight.column,
.offset-by-eight.columns { margin-left: 69.3333333333%; }
.offset-by-nine.column,
.offset-by-nine.columns { margin-left: 78.0%; }
.offset-by-ten.column,
.offset-by-ten.columns { margin-left: 86.6666666667%; }
.offset-by-eleven.column,
.offset-by-eleven.columns { margin-left: 95.3333333333%; }
.offset-by-one-third.column,
.offset-by-one-third.columns { margin-left: 34.6666666667%; }
.offset-by-two-thirds.column,
.offset-by-two-thirds.columns { margin-left: 69.3333333333%; }
.offset-by-one-half.column,
.offset-by-one-half.columns { margin-left: 52%; }
}
/* Base Styles
*/
/* NOTE
html is set to 62.5% so that all the REM measurements throughout Skeleton
are based on 10px sizing. So basically 1.5rem = 15px :) */
html {
font-size: 62.5%; }
body {
font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */
line-height: 1.6;
font-weight: 400;
font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #222; }
/* Typography
*/
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 2rem;
font-weight: 300; }
h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;}
h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; }
h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; }
h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; }
h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; }
h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; }
/* Larger than phablet */
@media (min-width: 550px) {
h1 { font-size: 5.0rem; }
h2 { font-size: 4.2rem; }
h3 { font-size: 3.6rem; }
h4 { font-size: 3.0rem; }
h5 { font-size: 2.4rem; }
h6 { font-size: 1.5rem; }
}
p {
margin-top: 0; }
/* Links
*/
a {
color: #1EAEDB; }
a:hover {
color: #0FA0CE; }
/* Buttons
*/
.button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
display: inline-block;
height: 38px;
padding: 0 30px;
color: #555;
text-align: center;
font-size: 11px;
font-weight: 600;
line-height: 38px;
letter-spacing: .1rem;
text-transform: uppercase;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border-radius: 4px;
border: 1px solid #bbb;
cursor: pointer;
box-sizing: border-box; }
.button:hover,
button:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover,
.button:focus,
button:focus,
input[type="submit"]:focus,
input[type="reset"]:focus,
input[type="button"]:focus {
color: #333;
border-color: #888;
outline: 0; }
.button.button-primary,
button.button-primary,
input[type="submit"].button-primary,
input[type="reset"].button-primary,
input[type="button"].button-primary {
color: #FFF;
background-color: #33C3F0;
border-color: #33C3F0; }
.button.button-primary:hover,
button.button-primary:hover,
input[type="submit"].button-primary:hover,
input[type="reset"].button-primary:hover,
input[type="button"].button-primary:hover,
.button.button-primary:focus,
button.button-primary:focus,
input[type="submit"].button-primary:focus,
input[type="reset"].button-primary:focus,
input[type="button"].button-primary:focus {
color: #FFF;
background-color: #1EAEDB;
border-color: #1EAEDB; }
/* Forms
*/
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea,
select {
height: 38px;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
background-color: #fff;
border: 1px solid #D1D1D1;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box; }
/* Removes awkward default styles on some inputs for iOS */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none; }
textarea {
min-height: 65px;
padding-top: 6px;
padding-bottom: 6px; }
input[type="email"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus {
border: 1px solid #33C3F0;
outline: 0; }
label,
legend {
display: block;
margin-bottom: .5rem;
font-weight: 600; }
fieldset {
padding: 0;
border-width: 0; }
input[type="checkbox"],
input[type="radio"] {
display: inline; }
label > .label-body {
display: inline-block;
margin-left: .5rem;
font-weight: normal; }
/* Lists
*/
ul {
list-style: circle inside; }
ol {
list-style: decimal inside; }
ol, ul {
padding-left: 0;
margin-top: 0; }
ul ul,
ul ol,
ol ol,
ol ul {
margin: 1.5rem 0 1.5rem 3rem;
font-size: 90%; }
li {
margin-bottom: 1rem; }
/* Code
*/
code {
padding: .2rem .5rem;
margin: 0 .2rem;
font-size: 90%;
white-space: nowrap;
background: #F1F1F1;
border: 1px solid #E1E1E1;
border-radius: 4px; }
pre > code {
display: block;
padding: 1rem 1.5rem;
white-space: pre; }
/* Tables
*/
th,
td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #E1E1E1; }
th:first-child,
td:first-child {
padding-left: 0; }
th:last-child,
td:last-child {
padding-right: 0; }
/* Spacing
*/
button,
.button {
margin-bottom: 1rem; }
input,
textarea,
select,
fieldset {
margin-bottom: 1.5rem; }
pre,
blockquote,
dl,
figure,
table,
p,
ul,
ol,
form {
margin-bottom: 2.5rem; }
/* Utilities
*/
.u-full-width {
width: 100%;
box-sizing: border-box; }
.u-max-full-width {
max-width: 100%;
box-sizing: border-box; }
.u-pull-right {
float: right; }
.u-pull-left {
float: left; }
/* Misc
*/
hr {
margin-top: 3rem;
margin-bottom: 3.5rem;
border-width: 0;
border-top: 1px solid #E1E1E1; }
/* Clearing
*/
/* Self Clearing Goodness */
.container:after,
.row:after,
.u-cf {
content: "";
display: table;
clear: both; }
/* Media Queries
*/
/*
Note: The best way to structure the use of media queries is to create the queries
near the relevant code. For example, if you wanted to change the styles for buttons
on small devices, paste the mobile query code up in the buttons section and style it
there.
*/
/* Larger than mobile */
@media (min-width: 400px) {}
/* Larger than phablet (also point when grid becomes active) */
@media (min-width: 550px) {}
/* Larger than tablet */
@media (min-width: 750px) {}
/* Larger than desktop */
@media (min-width: 1000px) {}
/* Larger than Desktop HD */
@media (min-width: 1200px) {}
Comments