Migrate netlify-cms-www site into this repo (#860)

* Add frontmatter to docs files (prep to move)

* Move docs into position for website migration

* Migrate website from netlify-cms-www

Some modifications, including most of the changes in https://github.com/netlify/netlify-cms-www/pull/58 (previously reverted).

Also updated the readme and added hugo-bin for quicker onboarding of new docs contributors.

* Remove netlify.toml

This allows separate build commands for cms-demo and netlifycms.org.

* Remove website/netlify.toml

May re-add later, but it's not doing anything for now.

* Remove unused content file
This commit is contained in:
Jessica Parsons
2017-12-04 16:42:20 -08:00
committed by GitHub
parent 8e529ee7cc
commit 155f40e5e4
85 changed files with 9616 additions and 18 deletions

7
website/.babelrc Normal file
View File

@ -0,0 +1,7 @@
{
"presets": ["es2015"],
"plugins": [
"syntax-object-rest-spread",
"transform-object-rest-spread"
]
}

1
website/.nvmrc Normal file
View File

@ -0,0 +1 @@
node

19
website/README.md Executable file
View File

@ -0,0 +1,19 @@
# Netlify CMS Website & Docs
This directory builds netlifycms.org. If you'd like to propose changes to the site or docs, you'll find the source files in here.
## Local development
The site is built with [Hugo](https://gohugo.io/), managed as an npm dependency via [hugo-bin](https://www.npmjs.com/package/hugo-bin).
To run the site locally, you'll need to have [Node](https://nodejs.org) and [Yarn](https://yarnpkg.com/en/) installed on your computer.
From your terminal window, `cd` into the `website` directory of the repo, and run
```bash
yarn
yarn start
```
Then visit http://localhost:3000/ - BrowserSync will automatically reload CSS or
refresh the page when stylesheets or content changes.

View File

@ -0,0 +1,45 @@
// if you change these you must restart the server
module.exports = {
// colors
lightestGrey: '#E6E6E6',
lighterGrey: '#F7F8F8',
lightGrey: '#F6F6F6',
grey: '#313D3E',
darkGrey: '#2F3132',
darkerGrey: '#1C1E1E',
lightGreen: '#97bf2f',
green: '#C9FA4B',
darkGreen: '#7CA511',
// typography
thin: 100,
light: 300,
regular: 400,
semibold: 500,
bold: 700,
black: 900,
// fonts
roboto: "'Roboto', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
// padding
micro: '8px',
tiny: '16px',
small: '24px',
medium: '40px',
large: '64px',
xl: '104px',
xxl: '168px',
// border radius
borderRadius: '4px',
largeBorderRadius: '10px',
// responsive breakpoints
mobile: '480px',
tablet: '768px',
desktop: '960px',
display: '1200px'
}

94
website/gulpfile.babel.js Executable file
View File

@ -0,0 +1,94 @@
import gulp from "gulp";
import cp from "child_process";
import hugoBin from "hugo-bin"
import gutil from "gulp-util";
import postcss from "gulp-postcss";
import cssImport from "postcss-import";
import neatgrid from "postcss-neat";
import nestedcss from "postcss-nested";
import colorfunctions from "postcss-colour-functions";
import hdBackgrounds from "postcss-at2x";
import cssvars from "postcss-simple-vars-async";
import cssextend from "postcss-simple-extend";
import styleVariables from "./config/variables";
import BrowserSync from "browser-sync";
import webpack from "webpack";
import webpackConfig from "./webpack.conf";
const browserSync = BrowserSync.create();
const defaultArgs = ["-d", "../dist", "-s", "site", "-v"];
gulp.task("hugo", (cb) => buildSite(cb));
gulp.task("hugo-preview", (cb) => buildSite(cb, ["--buildDrafts", "--buildFuture"]));
gulp.task("build", ["css", "js", "fonts", "images", "hugo"]);
gulp.task("build-preview", ["css", "js", "fonts", "images", "hugo-preview"]);
gulp.task("css", () => (
gulp.src("./src/css/**/*.css")
.pipe(postcss([
cssImport({from: "./src/css/main.css"}),
neatgrid(),
nestedcss(),
colorfunctions(),
hdBackgrounds(),
cssextend(),
cssvars({variables: styleVariables})]))
.pipe(gulp.dest("./dist/css"))
.pipe(browserSync.stream())
));
gulp.task("js", (cb) => {
const myConfig = Object.assign({}, webpackConfig);
webpack(myConfig, (err, stats) => {
if (err) throw new gutil.PluginError("webpack", err);
gutil.log("[webpack]", stats.toString({
colors: true,
progress: true
}));
browserSync.reload();
cb();
});
});
gulp.task("fonts", () => (
gulp.src("./src/fonts/**/*")
.pipe(gulp.dest("./dist/fonts"))
.pipe(browserSync.stream())
));
gulp.task("images", () => (
gulp.src("./src/img/**/*")
.pipe(gulp.dest("./dist/img"))
.pipe(browserSync.stream())
));
gulp.task("server", ["hugo", "css", "js", "fonts", "images"], () => {
browserSync.init({
server: {
baseDir: "./dist"
},
notify: false
});
gulp.watch("./src/js/**/*.js", ["js"]);
gulp.watch("./src/css/**/*.css", ["css"]);
gulp.watch("./src/img/**/*", ["images"]);
gulp.watch("./src/fonts/**/*", ["fonts"]);
gulp.watch("./site/**/*", ["hugo"]);
});
function buildSite(cb, options) {
const args = options ? defaultArgs.concat(options) : defaultArgs;
return cp.spawn(hugoBin, args, {stdio: "inherit"}).on("close", (code) => {
if (code === 0) {
browserSync.reload();
cb();
} else {
browserSync.notify("Hugo build failed :(");
cb("Hugo build failed");
}
});
}

53
website/package.json Executable file
View File

@ -0,0 +1,53 @@
{
"name": "victor-hugo",
"version": "1.0.0",
"description": "Victor Hugo is a Hugo boilerplate for creating truly epic websites!",
"main": "index.js",
"scripts": {
"hugo": "gulp hugo",
"webpack": "gulp webpack",
"build": "gulp build",
"build-preview": "gulp build-preview",
"start": "gulp server",
"lint": "eslint src"
},
"author": "",
"license": "MIT",
"dependencies": {
"autoprefixer": "^6.3.7",
"babel-eslint": "^6.1.2",
"babel-loader": "^6.2.4",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"babel-plugin-transform-class-properties": "^6.10.2",
"babel-plugin-transform-object-assign": "^6.8.0",
"babel-plugin-transform-object-rest-spread": "^6.8.0",
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.11.6",
"browser-sync": "^2.13.0",
"css-loader": "^0.23.1",
"eslint": "^3.1.1",
"eslint-plugin-import": "^1.11.1",
"exports-loader": "^0.6.3",
"file-loader": "^0.9.0",
"gulp": "^3.9.1",
"gulp-babel": "^6.1.2",
"gulp-postcss": "^6.1.1",
"gulp-util": "^3.0.7",
"hugo-bin": "^0.18.0",
"imports-loader": "^0.6.5",
"postcss-at2x": "^2.0.0",
"postcss-colour-functions": "^1.5.1",
"postcss-cssnext": "^2.7.0",
"postcss-import": "^8.1.2",
"postcss-loader": "^0.9.1",
"postcss-neat": "^2.5.2",
"postcss-nested": "^1.0.0",
"postcss-simple-extend": "^1.0.0",
"postcss-simple-vars-async": "^1.2.1",
"url-loader": "^0.5.7",
"webpack": "^1.13.1",
"whatwg-fetch": "^1.0.0",
"yamljs": "^0.2.8"
},
"devDependencies": {}
}

6
website/site/config.yaml Executable file
View File

@ -0,0 +1,6 @@
baseurl: "/"
languageCode: "en-us"
title: "Netlify CMS | Open-Source Content Management System"
disable404: true
pluralizeListTitles: false
metaDataFormat: "yaml"

0
website/site/content/.keep Executable file
View File

View File

@ -0,0 +1,65 @@
---
title: Architecture
position: 90
---
# Technical Architecture
Netlify CMS is a React application, using Redux for state management with immutable data structures (immutable.js).
The core abstractions for content editing are `collections`, `entries` and `widgets`.
Each `collection` represents a collection of entries. This can either be a collection of similar entries with the same structure, or a set of entries where each has its own structure.
The structure of an entry is defined as a series of fields, each with a `name`, a `label`, and a `widget` .
The `widget` determines the UI widget that the content editor will use when editing this field of an entry, as well as how the content of the field is presented in the editing preview.
Entries are loaded and persisted through a `backend` that will typically represent a `git` repository.
## State shape / reducers
**Auth:** Keeps track of the logged state and the current user.
**Config:** Holds the environment configuration (backend type, available collections and fields).
**Collections** List of available collections, their fields and metadata information.
**Entries:** Entries for each field.
**EntryDraft:** Reused for each entry that is edited or created. It holds the entry's temporary data until it's persisted on the backend.
**Medias:** Keeps references to all media files uploaded by the user during the current session.
## Selectors
Selectors are functions defined within reducers used to compute derived data from the Redux store. The available selectors are:
**selectEntry:** Selects a single entry, given the collection and a slug.
**selectEntries:** Selects all entries for a given collection.
**getAsset:** Selects a single AssetProxy object for the given URI.
## Value Objects
**AssetProxy:** AssetProxy is a Value Object that holds information regarding an asset file (such as an image, for example), whether it's persisted online or held locally in cache.
For a file persisted online, the AssetProxy only keeps information about its URI. For local files, the AssetProxy will keep a reference to the actual File object while generating the expected final URIs and on-demand blobs for local preview.
The AssetProxy object can be used directly inside a media tag (such as `<img>`), as it will always return something that can be used by the media tag to render correctly (either the URI for the online file or a single-use blob).
## Components structure and Workflows
Components are separated into two main categories: Container components and Presentational components.
### Entry Editing
For either updating an existing entry or creating a new one, the `EntryEditor` is used and the flow is the same:
* When mounted, the `EntryPage` container component dispatches the `createDraft` action, setting the `entryDraft` state to a blank state (in case of a new entry) or to a copy of the selected entry (in case of an edit).
* The `EntryPage` will also render widgets for each field type in the given entry.
* Widgets are used for editing entry fields. There are different widgets for different field types, and they are always defined in a pair containing a `control` and a `preview` component. The control component is responsible for presenting the user with the appropriate interface for manipulating the current field value, while the preview component is responsible for displaying the value with the appropriate styling.
#### Widget components implementation
The control component receives three (3) callbacks as props: `onChange`, `onAddAsset`, and `onRemoveAsset`.
* onChange (required): Should be called when the users changes the current value. It will ultimately end up updating the EntryDraft object in the Redux Store, thus updating the preview component.
* onAddAsset & onRemoveAsset (optionals): If the field accepts file uploads for media (images, for example), these callbacks should be invoked with a `AssetProxy` value object. `onAddAsset` will get the current media stored in the Redux state tree while `onRemoveAsset` will remove it. AssetProxy objects are stored in the `Medias` object and referenced in the `EntryDraft` object on the state tree.
Both control and preview widgets receive a `getAsset` selector via props. Displaying the media (or its URI) for the user should always be done via `getAsset`, as it returns an AssetProxy that can return the correct value for both medias already persisted on the server and cached media not yet uploaded.
The actual persistence of the content and medias inserted into the control component is delegated to the backend implementation. The backend will be called with the updated values and a list of assetProxy objects for each field of the entry, and should return a promise that can resolve into the persisted entry object and the list of the persisted media URIs.

View File

@ -0,0 +1,97 @@
---
title: Authentication & Backends
position: 25
---
# Authentication & Backends
Netlify CMS stores content in your GitHub repository. (GitLab and Bitbucket coming soon!) In order for this to work, you need to authenticate with GitHub, and that requires a server. We have a few options for handling this.
## Git Gateway with Netlify Identity
[Git Gateway](https://github.com/netlify/git-gateway) is a Netlify open source project that allows you to add editors to your site CMS without giving them direct push access to your GitHub repository. [Netlify Identity](https://www.netlify.com/docs/identity/) service handles the authentication and provides a simple interface for user management. The Netlify CMS [Test Drive](/test-drive/) is a working example of this backend.
To use it in your own project, follow these steps:
1. Head over to the [Netlify Identity docs](https://www.netlify.com/docs/identity) and follow the
steps to get started.
2. Add the following lines to your `config.yml` file:
``` yaml
backend:
name: git-gateway
accept_roles: #optional - accepts all users if left out
- admin
- editor
```
3. Optionally, you can assign roles to users in your Netlify dashboard, and then limit which
roles can access the CMS by defining the `accept_roles` field in the `config.yml` example above.
Otherwise `accept_roles` can be left out, and all Netlify Identity users on your site will have access.
## Git Gateway without Netlify
You can use [Git Gateway](https://github.com/netlify/git-gateway) without Netlify by setting up your own Git Gateway server and connecting it with your own instance of [GoTrue](https://www.gotrueapi.org) (the open source microservice that powers Netlify Identity), or with any other identity service that can issue JSON Web Tokens (JWT).
To configure in Netlify CMS, use the same `backend` settings in your `config.yml` file as described in Step 2 of the [Git Gateway with Netlify Identity](#git-gateway-with-netlify-identity) instructions above.
## GitHub Backend
The GitHub backend allows CMS users to log in directly with their GitHub account. Note that the
user's GitHub account must have push access to your content repository for this to work.
Because Github [requires a
server](https://github.com/netlify/netlify-cms/issues/663#issuecomment-335023723) for
authentication, Netlify facilitates basic GitHub authentication.
To enable it:
1. Follow the authentication provider setup steps in the [Netlify
docs](https://www.netlify.com/docs/authentication-providers/#using-an-authentication-provider).
2. Add the following lines to your `config.yml` file:
``` yaml
backend:
name: github
repo: owner-name/repo-name # Path to your Github repository
```
### External OAuth Clients
If you would like to facilitate your own OAuth authentication rather than use Netlify's service, you
can use one of the community-maintained projects below. Feel free to [submit a pull request](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md) if you'd like to add yours!
| Author | Supported Git hosts | Languages | Link |
|----------------------------------------|---------------------------|-----------|------------------------------------------------------------------------------|
| [@vencax](https://github.com/vencax) | GitHub, GitHub Enterprise | Node.js | [Repo](https://github.com/vencax/netlify-cms-github-oauth-provider) |
| [@igk1972](https://github.com/igk1972) | GitHub, GitHub Enterprise | Go | [Repo](https://github.com/igk1972/netlify-cms-oauth-provider-go) |
Check each project's documentation for instructions on how to configure it.
## Bitbucket and GitLab Support
Netlify CMS is meant to be platform agnostic, so were always looking to expand the ecosystem and
find new ways to use it. Check out our active PRs in progress for
[Bitbucket](https://github.com/netlify/netlify-cms/pull/525) and
[Gitlab](https://github.com/netlify/netlify-cms/pull/517) backends.
Git Gateway could also be modified to support these Git hosts. If you're interested, you can file an
issue (or a pull request!) in the [git-gateway repo](https://github.com/netlify/git-gateway).
## Options
Both `git-gateway` and `github` backends allow some additional optional fields for certain use
cases. A full reference is below. Note that these are properties of the `backend` field, and should
be nested under that field.
| Field | Default | Description |
|----------------|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `repo` | none | **Required** for `github` backend; ignored by `git-gateway`. Follows the pattern `[org-or-username]/[repo-name]`. |
| `accept_roles` | none | `git-gateway` only. Limits CMS access to your defined array of user roles. Omitting this field gives access to all registered users. |
| `branch` | `master` | The branch where published content is stored. All CMS commits and PRs are made to this branch. |
| `api_root` | `https://api.github.com` (ignored for `git-gateway` backend) | The API endpoint. Only necessary in certain cases, like with GitHub Enterprise. |
| `site_domain` | `location.hostname` (or `cms.netlify.com` when on `localhost`) | Sets the `site_id` query param sent to the API endpoint. Non-Netlify auth setups will often need to set this for local development to work properly. |
| `base_url` | `https://api.netlify.com` | OAuth client URL for the `github` backend. **Required** when using an external OAuth server with the `github` backend. |

View File

@ -0,0 +1,14 @@
---
title: Contributing
position: 100
---
# Welcome, contributors!
We're hoping that Netlify CMS will do for the [JAMstack](https://www.jamstack.org) what WordPress did for dynamic sites back in the day. We know we can't do that without building a thriving community of contributors and users, and we'd love to have you join us.
While we work on building this page (and you can help!), here are some links with more information about getting involved:
* [Project Milestones](https://github.com/netlify/netlify-cms/milestones)
* [Code of Conduct](https://github.com/netlify/netlify-cms/blob/master/CODE_OF_CONDUCT.md)
* [Setup instructions and Contribution Guidelines](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md)

View File

@ -0,0 +1,199 @@
---
title: Custom Previews
position: 50
---
# Customizing the Preview Pane
The NetlifyCMS exposes a `window.CMS` global object that you can use to register custom widgets, previews and editor plugins. The available customization methods are:
* **registerPreviewStyle:** Register a custom stylesheet to use on the preview pane.
* **registerPreviewTemplate:** Registers a template for a collection.
Explore the [NetlifyCMS GitHub example](https://github.com/netlify/netlify-cms/blob/9ced3f16c8bcc3d1a36773b126842d023c589eaf/example/index.html#L90-L91), a working example you can review on GitHub.
### React Components inline interaction
NetlifyCMS is a collection of React components and exposes two constructs globally to allow you to create components inline: createClass and h (alias for React.createElement).
## `registerPreviewStyle`
Register a custom stylesheet to use on the preview pane.
```js
CMS.registerPreviewStyle(file);
```
**Params:**
* **file:** css file path
**Example:**
```html
// index.html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
CMS.registerPreviewStyle("/example.css");
</script>
```
```css
/* example.css */
html,
body {
color: #444;
font-size: 14px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
body {
padding: 20px;
}
```
## `registerPreviewTemplate`
Registers a template for a collection.
`CMS.registerPreviewTemplate(collection, react_component);`
**Params:**
* collection: The name of the collection which this preview component will be used for.
* react_component: A React component that renders the collection data. Four props will be passed to your component during render:
* entry: Immutable collection containing the entry data.
* widgetFor: Returns the appropriate widget preview component for a given field.
* [widgetsFor](#lists-and-objects): Returns an array of objects with widgets and associated field data. For use with list and object type entries.
* getAsset: Returns the correct filePath or in-memory preview for uploaded images.
**Example:**
```html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
var PostPreview = createClass({
render: function() {
var entry = this.props.entry;
var image = entry.getIn(['data', 'image']);
var bg = this.props.getAsset(image);
return h('div', {},
h('h1', {}, entry.getIn(['data', 'title'])),
h('img', {src: bg.toString()}),
h('div', {"className": "text"}, this.props.widgetFor('body'))
);
}
});
CMS.registerPreviewTemplate("posts", PostPreview);
</script>
```
### Lists and Objects
The API for accessing the individual fields of list- and object-type entries is similar to the API
for accessing fields in standard entries, but there are a few key differences. Access to these
nested fields is facilitated through the `widgetsFor` function, which is passed to the preview
template component during render.
**Note**: as is often the case with the NetlifyCMS API, arrays and objects are created with
Immutable.js. If some of the methods that we use are unfamiliar, such as `getIn`, check out
[their docs](https://facebook.github.io/immutable-js/docs/#/) to get a better understanding.
**List Example:**
```html
<script>
var AuthorsPreview = createClass({
// For list fields, the widgetFor function returns an array of objects
// that you can map over in your template. If our field is a list of
// authors containing two entries, with fields `name` and `description`,
// the return value of `widgetsFor` would look like this:
//
// [{
// data: { name: 'Mathias', description: 'Co-Founder'},
// widgets: { name: (<WidgetComponent>), description: (WidgetComponent>)}
// },
// {
// data: { name: 'Chris', description: 'Co-Founder'},
// widgets: { name: (<WidgetComponent>), description: (WidgetComponent>)}
// }]
//
// Templating would look something like this:
render: function() {
return h('div', {},
// This is a static header that would only be rendered once for the entire list
h('h1', {}, 'Authors'),
// Here we provide a simple mapping function that will be applied to each
// object in the array of authors
this.props.widgetsFor('authors').map(function(author, index) {
return h('div', {key: index},
h('hr', {}),
h('strong', {}, author.getIn(['data', 'name'])),
author.getIn(['widgets', 'description'])
);
})
);
}
});
CMS.registerPreviewTemplate("authors", AuthorsPreview);
</script>
```
**Object Example:**
```html
<script>
var GeneralPreview = createClass({
// Object fields are simpler than lists - instead of `widgetsFor` returning
// an array of objects, it returns a single object. Accessing the shape of
// that object is the same as the shape of objects returned for list fields:
//
// {
// data: { front_limit: 0, author: 'Chris' },
// widgets: { front_limit: (<WidgetComponent>), author: (WidgetComponent>)}
// }
render: function() {
var entry = this.props.entry;
var title = entry.getIn(['data', 'site_title']);
var posts = entry.getIn(['data', 'posts']);
return h('div', {},
h('h1', {}, title),
h('dl', {},
h('dt', {}, 'Posts on Frontpage'),
h('dd', {}, this.props.widgetsFor('posts').getIn(['widgets', 'front_limit']) || 0),
h('dt', {}, 'Default Author'),
h('dd', {}, this.props.widgetsFor('posts').getIn(['data', 'author']) || 'None'),
)
);
}
});
CMS.registerPreviewTemplate("general", GeneralPreview);
</script>
```
### Accessing Metadata
Preview Components also receive an additional prop: `fieldsMetaData`. It contains aditional information (besides the plain plain textual value of each field) that can be useful for preview purposes.
For example, the Relation widget passes the whole selected relation data in `fieldsMetaData`.
```js
export default class ArticlePreview extends React.Component {
render() {
const {entry, fieldsMetaData} = this.props;
const author = fieldsMetaData.getIn(['authors', data.author]);
return <article><h2>{ entry.getIn(['data', 'title']) }</h2>
{author &&<AuthorBio author={author.toJS()}/>}
</article>
}
}
```

View File

@ -0,0 +1,44 @@
---
title: Editorial Workflow
position: 40
---
# Editorial Workflow
## Overview
By default, all entries created or edited in the Netlify CMS are committed directly into the main repository branch.
Alternatively, you can enable an optional "Editorial Workflow" mode that allows for more control over the content publishing phases. All unpublished entries will be arranged in a board according to their status, and they can be further reviewed and edited before going live.
![Editorial workflow](https://cloud.githubusercontent.com/assets/33676/19452442/d10d9002-948f-11e6-9463-06955b6c15c8.png)
From a technical perspective, the workflow translates editor UI actions into common Git commands:
Actions in Netlify UI ... | Perform these Git actions
--- | ---
Save draft | Commits to a new branch, and opens a pull request
Edit draft | Pushes another commit to the draft branch/pull request
Approve and publish draft | Merges pull request and deletes branch
## Adding to your site
To enable the editorial workflow, add this line to your `admin/config.yml` file:
``` yaml
publish_mode: editorial_workflow
```
There are no other configuration options right now. There are always three possible statuses, and new branch names are created according to the pattern `cms/collectionName-entrySlug`.
## About metadata
Netlify CMS embraces the idea of Git-as-backend for storing metadata. The first time it runs with the editorial_workflow setup, it creates a new ref called `meta/_netlify_cms`, pointing to an empty, orphan tree.
Actual data are stored in individual `json` files committed to this tree.
## Implementation
Instead of adding logic to `CollectionPage` and `EntryPage`, the Editorial Workflow is implemented as Higher Order Components, adding UI and dispatching additional actions.
Furthermore, all editorial workflow state is managed in Redux - there's an `actions/editorialWorkflow.js` file and a `reducers/editorialWorkflow.js` file.

View File

@ -0,0 +1,13 @@
---
title: Examples
position: 110
---
# Examples
Do you have a great example? Submit a pull request to this page.
Name | Tools | Type | Example | More info |
--- | --- | --- | --- | ---
This Developing Journey | middleman | blog | [briandouglas.me](https://briandouglas.me) | [blog post](https://deploy-preview-496--www.netlify.com/blog/2017/04/18/blog-with-middleman-and-the-netlifycms/)
JAMstack Recipes | Hugo, Azure | demo | [jamstack-cms.netlify.com](http://jamstack-cms.netlify.com) | [blog post](http://conductofcode.io/post/managing-content-for-a-jamstack-site-with-netlify-cms/)

View File

@ -0,0 +1,117 @@
---
title: Extending Widgets
position: 60
---
# Extending With Widgets
The NetlifyCMS exposes an `window.CMS` global object that you can use to register custom widgets, previews, and editor plugins. The available widget extension methods are:
* **registerWidget:** lets you register a custom widget.
* **registerEditorComponent:** lets you add a block component to the Markdown editor.
### Writing React Components inline
The `registerWidget` requires you to provide a React component. If you have a build process in place for your project, it is possible to integrate with this build process.
However, although possible, it may be cumbersome or even impractical to add a React build phase. For this reason, NetlifyCMS exposes two constructs globally to allow you to create components inline: createClass and h (alias for React.createElement).
## `registerWidget`
Register a custom widget.
```js
CMS.registerWidget(name, control, \[preview\])
```
**Params:**
Param | Type | Description
--- | --- | ---
`name` | string | Widget name, allows this widget to be used via the field `widget` property in config
`control` | React.Component \| string | <ul><li>React component that renders the control, receives the following props: <ul><li>**value:** Current field value</li><li>**onChange:** Callback function to update the field value</li></ul></li><li>Name of a registered widget whose control should be used (includes built in widgets).</li></ul>
[`preview`] | React.Component, optional | Renders the widget preview, receives the following props: <ul><li>**value:** Current preview value</li><li>**field:** Immutable map of current field configuration</li><li>**metadata:** Immutable map of any available metadata for the current field</li><li>**getAsset:** Function for retrieving an asset url for image/file fields</li><li>**entry:** Immutable Map of all entry data</li><li>**fieldsMetaData:** Immutable map of metadata from all fields.</li></ul>
* **field:** The field type that this widget will be used for.
* **control:** A React component that renders the editing interface for this field. Two props will be passed:
* **value:** The current value for this field.
* **onChange:** Callback function to update the field value.
* **preview (optional):** A React component that renders the preview of how the content will look. A `value` prop will be passed to this component.
**Example:**
```html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
var CategoriesControl = createClass({
handleChange: function(e) {
this.props.onChange(e.target.value.split(',').map((e) => e.trim()));
},
render: function() {
var value = this.props.value;
return h('input', { type: 'text', value: value ? value.join(', ') : '', onChange: this.handleChange });
}
});
var CategoriesPreview = createClass({
render: function() {
return h('ul', {},
this.props.value.map(function(val, index) {
return h('li', {key: index}, val);
})
);
}
});
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview);
</script>
```
## `registerEditorComponent`
Register a block level component for the Markdown editor:
CMS.registerEditorComponent(definition)
**Params**
* **definition:** The component definition; must specify: id, label, fields, patterns, fromBlock, toBlock, toPreview
**Example:**
```html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
CMS.registerEditorComponent({
// Internal id of the component
id: "youtube",
// Visible label
label: "Youtube",
// Fields the user need to fill out when adding an instance of the component
fields: [{name: 'id', label: 'Youtube Video ID', widget: 'string'}],
// Pattern to identify a block as being an instance of this component
pattern: /youtube (\S+)\s/,
// Function to extract data elements from the regexp match
fromBlock: function(match) {
return {
id: match[1]
};
},
// Function to create a text block from an instance of this component
toBlock: function(obj) {
return 'youtube ' + obj.id;
},
// Preview output for this component. Can either be a string or a React component
// (component gives better render performance)
toPreview: function(obj) {
return (
'<img src="http://img.youtube.com/vi/' + obj.id + '/maxresdefault.jpg" alt="Youtube Video"/>'
);
}
});
</script>
```
**Result:**
![youtube-widget](/img/youtube-widget.png)

View File

@ -0,0 +1,107 @@
---
title: Introduction
position: 0
---
# Introduction
Netlify CMS is a Content Management System for static sites, allowing collaborators to create, edit, review, and publish content without writing code or dealing with version control. It brings the ease of WordPress-style editing to the simplicity and speed of static sites.
At its core, Netlify CMS is an open-source React app that acts as a wrapper for the Git workflow, using the GitHub API. This provides many advantages, including:
* **Fast, web-based UI:** with rich-text editing, real-time preview, and drag-and-drop media uploads.
* **Platform agnostic:** works with most static site generators.
* **Easy installation:** add two files to your site and hook up the backend by including in your build process or linking to our CDN.
* **Modern authentication:** using GitHub and JSON web tokens.
* **Flexible content types:** specify an unlimited number of content types with custom fields.
* **Fully extensible:** create custom-styled previews, UI widgets, and editor plugins.
# Core Concepts
## The Admin Interface
The admin interface is a single-page app with the entry point stored in a static `/admin` folder you add to the root of your site. You can include it with a simple `index.html` file that loads the necessary CSS and JS files from a CDN:
``` html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.css" />
</head>
<body>
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
</body>
</html>
```
The JS is also available via npm and can be integrated into your regular build process.
### Editorial Workflow
Netlify CMS has an optional [editorial workflow](/docs/editorial-workflow) that translates common Git commands into familiar language in a simple UI:
Actions in Netlify UI ... | Perform these Git actions
--- | ---
Save draft | Commits to a new branch, and opens a pull request
Edit draft | Pushes another commit to the draft branch/pull request
Approve and publish draft | Merges pull request and deletes branch
## Configuration
All configuration is handled in a single `config.yml` file, also stored in the `/admin` folder. A simple version might look like this:
``` yaml
backend:
name: github
repo: owner/repo # Path to your Github repository
branch: master # Branch to update (master by default)
media_folder: "img/uploads" # Folder where user uploaded files should go
collections: # A list of collections the CMS should be able to edit
- name: "post" # Used in routes, e.g., /admin/collections/:slug/edit
label: "Post" # Used in the UI, e.g., "New Post"
folder: "_posts" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
fields: # The fields each document in this collection have
- {label: "Title", name: "title", widget: "string", tagname: "h1"}
- {label: "Body", name: "body", widget: "markdown"}
- {label: "Foo", name: "foo", widget: "foo"}
- {label: "Publish Date", name: "date", widget: "datetime"}
```
### Backend
Because Netlify CMS is a wrapper for the GitHub API, the "backend" is a repo stored on GitHub. *(Using a different Git host? File a [feature request](https://github.com/netlify/netlify-cms/issues), or [help us add it](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md)!)* For authentication, you can connect to GitHub using Netlifys [Authentication Provider feature](https://www.netlify.com/docs/authentication-providers), or you can roll your own.
### Collections
All content managed by Netlify CMS is organized in Collections (groups of files), such as:
* blog posts
* portfolio samples
* product listings
* podcast episodes
You point to where the files are stored, and specify the fields that define them. The `body` field typically stores the main text of a file, while any other fields are included at the top of the document in the front matter. They can be required, optional, or hidden, and can have preset defaults.
### Widgets
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in [widgets](/docs/widgets).
## Customization
Netlify CMS exposes a `window.CMS` global object that you can use to register custom widgets, previews, and editor plugins. The available methods are:
* `registerPreviewStyle`: register a custom stylesheet to match the editor preview pane to your site style.
* `registerPreviewTemplate`: registers a template to determine how fields are displayed in the preview, customizable for each collection.
* `registerWidget`: registers a custom widget.
* `registerEditorComponent`: adds a block component to the Markdown editor.

View File

@ -0,0 +1,235 @@
---
title: Quick Start
position: 20
---
# Quick Start
Netlify CMS is adaptable to a wide variety of projects. The only inflexible requirement is that your site content must be written in markdown, JSON, YAML, or TOML files, stored in a repo on [GitHub](https://github.com/). (If you're partial to another Git hosting service, check out the PRs in progress for [GitLab](https://github.com/netlify/netlify-cms/pull/517) and [Bitbucket](https://github.com/netlify/netlify-cms/pull/525) support.)
In this guide, we're going to assume you're using a [static site generator](https://www.staticgen.com/), like Jekyll, Hugo, Hexo, or Gatsby.
## App File Structure
All Netlify CMS files are contained in a static `admin` folder, stored at the root of your published site. Where you store this folder in the source files depends on your static site generator. Here's the the static file location for a few of the most popular static site generators:
These generators ... | store static files in
--- | ---
Jekyll, GitBook | `/` (project root)
Hugo, Gatsby | `/static`
Hexo, Middleman | `/source`
Spike | `/views`
If your generator isn't listed here, you can check its documentation, or as a shortcut, look in your project for a `css` or `images` folder. The contents of folders like that are usually processed as static files, so it's likely you can store your `admin` folder next to those. (When you've found the location, feel free to add it to these docs by [filing a pull request](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md)!)
Inside the `admin` folder, you'll create two files:
```
admin
├ index.html
└ config.yml
```
The first file, `admin/index.html`, is the entry point for the Netlify CMS admin interface. This means that users can navigate to `yoursite.com/admin` to access it. On the code side, it's a basic HTML starter page that loads the necessary CSS and JavaScript files. In this example, we pull those files from a public CDN:
``` html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
<!-- Include the styles for the Netlify CMS UI, after your own styles -->
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.css" />
</head>
<body>
<!-- Include the script that builds the page and powers Netlify CMS -->
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
</body>
</html>
```
The second file, `admin/config.yml`, is the heart of your Netlify CMS installation, and a bit more complex. The next section covers the details.
## Configuration
Configuration will be different for every site, so we'll break it down into parts. All code snippets in this section will be added to your `admin/config.yml` file.
### Backend
Because we're using GitHub and Netlify for our hosting and authentication, backend configuration is fairly strightforward. You can start your `config.yml` file with these lines:
``` yaml
backend:
name: git-gateway
branch: master # Branch to update (optional; defaults to master)
```
These lines specify your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We'll get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it will default to `master`.
### Editorial Workflow
By default, saving a post in the CMS interface will push a commit directly to the publication branch specified in `backend`. However, you also have the option to enable the [Editorial Workflow](/docs/editorial-workflow), which adds an interface for drafting, reviewing, and approving posts. To do this, add the following line to your `config.yml`:
``` yaml
publish_mode: editorial_workflow
```
### Media and Public Folders
Netlify CMS allows users to upload images directly within the editor. For this to work, the CMS needs to know where to save them. If you already have an `images` folder in your project, you could use its path, possibly creating an `uploads` sub-folder, for example:
``` yaml
media_folder: "images/uploads" # Media files will be stored in the repo under images/uploads
```
If you're creating a new folder for uploaded media, you'll need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
Note that the`media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Hexo, Middleman or others that store static files in a subfolder. Here's an example that could work for a Hugo site:
``` yaml
media_folder: "static/images/uploads" # Media files will be stored in the repo under static/images/uploads
public_folder: "/images/uploads" # The src attribute for uploaded media will begin with /images/uploads
```
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files will be saved in the repo, `public_folder` indicates where they can be found in the published site. This path is used in image `src` attributes and is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
*If `public_folder` is not set, Netlify CMS will default to the same value as `media_folder`, adding an opening `/` if one is not included.*
### Collections
Collections define the structure for the different content types on your static site. Since every site is different, the `collections` settings will be very different from one site to the next.
Let's say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
``` yaml
---
layout: blog
title: "Let's Party"
date: 1999-12-31 11:59:59 -0800
thumbnail: "/images/prince.jpg"
rating: 5
---
This is the post body, where I write about our last chance to party before the Y2K bug destroys us all.
```
Given this example, our `collections` settings would look like this:
``` yaml
collections:
- name: "blog" # Used in routes, e.g., /admin/collections/blog
label: "Blog" # Used in the UI
folder: "_posts/blog" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
fields: # The fields for each document, usually in front matter
- {label: "Layout", name: "layout", widget: "hidden", default: "blog"}
- {label: "Title", name: "title", widget: "string"}
- {label: "Publish Date", name: "date", widget: "datetime"}
- {label: "Featured Image", name: "thumbnail", widget: "image"}
- {label: "Rating (scale of 1-5)", name: "rating", widget: "number"}
- {label: "Body", name: "body", widget: "markdown"}
```
Let's break that down:
<table>
<tr>
<td><code>name</code></td>
<td>Post type identifier, used in routes. Must be unique.</td>
</tr>
<tr>
<td><code>label</code></td>
<td>What the post type will be called in the admin UI.</td>
</tr>
<tr>
<td><code>folder</code></td>
<td>Where files of this type are stored, relative to the repo root.</td>
</tr>
<tr>
<td><code>create</code></td>
<td>Set to <code>true</code> to allow users to create new files in this collection.
</td>
</tr>
<tr>
<td><code>slug</code></td>
<td>Template for filenames. <code>{{year}}</code>, <code>{{month}}</code>, and <code>{{day}}</code> will pull from the post's <code>date</code> field or save date. <code>{{slug}}</code> is a url-safe version of the post's <code>title</code>. Default is simply <code>{{slug}}</code>.
</td>
</tr>
<tr>
<td><code>fields</code></td>
<td>Fields listed here are shown as fields in the content editor, then saved as front matter at the beginning of the document (except for <code>body</code>, which follows the front matter). Each field contains the following properties:
<ul>
<li><code>label</code>: Field label in the editor UI.</li>
<li><code>name</code>: Field name in the document front matter.</li>
<li><code>widget</code>: Determines UI style and value data type (details below).</li>
<li><code>default</code> (optional): Sets a default value for the field.</li>
</ul>
</td>
</tr>
</table>
As described above, the `widget` property specifies a built-in or custom UI widget for a given field. When a content editor enters a value into a widget, that value will be saved in the document front matter as the value for the `name` specified for that field. A full listing of available widgets can be found in the [Widgets doc](/docs/widgets).
Based on this example, you can go through the post types in your site and add the appropriate settings to your `config.yml` file. Each post type should be listed as a separate node under the `collections` field.
### Filter
The entries for any collection can be filtered based on the value of a single field. The example collection below would only show post entries with the value "en" in the language field.
``` yaml
collections:
- name: "posts"
label: "Post"
folder: "_posts"
filter:
field: language
value: en
fields:
- {label: "Language", name: "language"}
```
## Authentication
Now that you have your Netlify CMS files in place and configured, all that's left is to enable authentication. There are [many ways to do this](/docs/custom-authentication) (with or without deploying to Netlify), but this example uses Netlify because it's one of the quickest ways to get started.
### Setup on Netlify
Netlify offers a built-in authentication service called Identity. In order to use it, you'll need to connect your site repo with Netlify. Netlify has published a general [Step-by-Step Guide](https://www.netlify.com/blog/2016/10/27/a-step-by-step-guide-deploying-a-static-site-or-single-page-app/) for this, along with detailed guides for many popular static site generators, including [Jekyll](https://www.netlify.com/blog/2015/10/28/a-step-by-step-guide-jekyll-3.0-on-netlify/), [Hugo](https://www.netlify.com/blog/2016/09/21/a-step-by-step-guide-victor-hugo-on-netlify/), [Hexo](https://www.netlify.com/blog/2015/10/26/a-step-by-step-guide-hexo-on-netlify/), [Middleman](https://www.netlify.com/blog/2015/10/01/a-step-by-step-guide-middleman-on-netlify/), [Gatsby](https://www.netlify.com/blog/2016/02/24/a-step-by-step-guide-gatsby-on-netlify/) and more.
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have GitHub accounts or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you'll want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This will authenticate with GitHub and generate a GitHub API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
### Add the Netlify Identity Widget
With the backend set to handle authentication, now you need a frontend interface to connect to it. The open source Netlify Identity Widget is a drop-in widget made for just this purpose. To include the widget in your site, you'll need to add the following script tag in two places:
```html
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
```
You'll need to add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that's probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
When a user logs in with the Netlify Identity widget, they will be directed to the site homepage with an access token. In order to complete the login and get back to the CMS, we'll need to redirect the user back to the `/admin` path. To do this, add the following script before the closing `body` tag of your site's main index page:
```html
<script>
if (window.netlifyIdentity) {
window.netlifyIdentity.on("init", user => {
if (!user) {
window.netlifyIdentity.on("login", () => {
document.location.href = "/admin/";
});
}
});
}
</script>
```
Note: This example script requires modern JavaScript and will not work on IE11. For legacy browser support, use function expressions (`function () {}`) in place of the arrow functions (`() => {}`), or use a transpiler like [Babel](https://babeljs.io/).
## Accessing the CMS
Your site CMS is now fully configured and ready for login!
If you set your registration preference to "Invite only," you'll need to invite yourself (and anyone else you choose) as a site user. To do this, select the **Identity** tab from your site dashboard, and then select the **Invite users** button. Invited users will receive an email invitation with a confirmation link. Clicking the link will take you to your site with a login prompt.
If you left your site registration open, or for return visits after comfirming an email invitation, you can access your site's CMS at `yoursite.com/admin`.
Happy posting!

View File

@ -0,0 +1,33 @@
---
title: Test Drive
position: 10
---
# Take a test drive
Netlify CMS can run in any frontend web environment, but the quickest way to try it out is by running it on a pre-configured starter site with Netlify. Our example here is the [Kaldi coffee company template](https://github.com/netlify-templates/one-click-hugo-cms). Use the button below to build and deploy your own copy of the repository:
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/netlify-templates/one-click-hugo-cms&stack=cms)
After clicking that button, youll authenticate with GitHub and choose a repository name. Netlify will then automatically create a repository in your GitHub account with a copy of the files from the template. Next, it will build and deploy the new site on Netlify, bringing you to the site dashboard when the build is complete. Next, youll need to set up Netlify's [Identity](https://www.netlify.com/docs/identity) service to authorize users to log in to the CMS.
## Adding users
1. From the Netlify site dashboard, select the **Identity** tab, and you'll find that there are no users yet. By default, this site is set to accept users by invitation only, and even the site owner needs to be invited.
2. Select **Invite users**, enter your email address, and select Send.
3. Check your email for the invitation. It will be sent from `no-reply@netlify.com`.
![Sample email subject line: You've been invited to join radiologist-amanda-53841.netlify.com](/img/email-subject.png?raw=true)
4. Click the link to accept the invite, and youll be directed to your new site, with a prompt to create a password.
!["Complete your signup" modal on the Kaldi coffee site](/img/create-password.png?raw=true)
5. Enter a password, sign in, and youll be directed straight to the CMS!
Try adding and editing posts, or changing the content of the Products page. When you save, the changes will be pushed immediately to GitHub, triggering a build on Netlify, and updating the content on your site.
## More paths to explore
- If youd like to learn more about how it all works, check out the [Intro](/docs/intro).
- To see how to integrate Netlify CMS into an existing project, go to the [Quick Start](/docs/quick-start).
- If youd like to change how users log in to your site, read up on [Netlify Identity service](https://www.netlify.com/docs/identity).

View File

@ -0,0 +1,59 @@
---
title: Validation
position: 70
---
# Collection Field Validation
## Available validations to use on `config.yml`:
- Presence: By default all widgets are required, unless specified in the config. Example:
`- {label: "Subtitle", name: "subtitle", widget: "string", required: false}`
- Pattern: Field configuration can specify a regex pattern with the appropriate error message. Example:
`- {label: "Title", name: "title", widget: "string", pattern: ['.{10,}', "Should have more than 10 characters"] }`
## Advanced Guide (For widget authors)
The widget control can optionally implement an `isValid` method to perform custom validations, in addition to presence and pattern. The `isValid` method will be automatically called, and it can return either a boolean value, an object with an error message or a promise. Examples:
**Boolean**
No errors:
```javascript
isValid = () => {
// Do internal validation
return true;
};
```
Existing error:
```javascript
isValid = () => {
// Do internal validation
return false;
};
```
**Object with `error` (useful for returning custom error messages)**
Existing error:
```javascript
isValid = () => {
// Do internal validation
return { error: 'Your error message.' };
};
```
**Promise**
You can also return a promise from `isValid`. While the promise is pending, the widget will be marked as "in error". When the promise resolves, the error is automatically cleared.
```javascript
isValid = () => {
return this.existingPromise;
};
```
Note: Do not create a promise inside `isValid` - `isValid` is called right before trying to persist. This means that even if a previous promise was already resolved, when the user hits 'save', `isValid` will be called again. If it returns a new promise, it will be immediately marked as "in error" until the new promise resolves.

View File

@ -0,0 +1,132 @@
---
title: Widgets
position: 30
---
# Widgets
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets, including:
| Name | UI | Data Type |
| -------- | ---------------------------------- | ---------------------------------------------------|
| `string` | text input | string |
| `boolean` | toggle switch | boolean |
| `text` | textarea input | string (multiline) |
| `number` | number input | number |
| `markdown` | rich text editor | string (markdown) |
| `datetime` | date picker | string (ISO date) |
| `select` | select input (dropdown) | string |
| `image` | file picker w/ drag-and-drop | image file |
| `file` | file picker w/ drag-and-drop | file |
| `hidden` | none | string |
| `object` | group of other widgets | Immutable Map containing field values |
| `list` | repeatable group of other widgets | Immutable List of objects containing field values |
| `relation` | text input w/ suggestions dropdown | value of `valueField` in related entry (see below) |
Were always adding new widgets, and you can also [create your own](/docs/extending)!
### Select Widget
The select widget allows you to pick a string value from a drop down menu
```yaml
collections:
- name: posts
label: Post
folder: "_posts"
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
create: true
fields:
- {label: Title, name: title, widget: string, tagname: h1}
- {label: Body, name: body, widget: markdown}
- {label: Align Content, name: align, widget: select, options: ['left', 'center', 'right']}
```
### List Widget
The list widget allows you to map a user-provided string with a comma delimiter into a list. Consider the following example that also demonstrates how to set default values:
```yaml
collections:
- name: posts
label: Post
folder: "_posts"
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
create: true
fields:
- {label: Title, name: title, widget: string, tagname: h1}
- {label: Body, name: body, widget: markdown}
- {label: Categories, name: categories, widget: list}
- {label: Tags, name: tags, widget: list, default: ['term_1', 'term_2']}
```
Lists of objects are supported as well and require a nested field list.
```yaml
collections:
- name: posts
label: Post
folder: "_posts"
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
create: true
fields:
- {label: Title, name: title, widget: string, tagname: h1}
- {label: Body, name: body, widget: markdown}
- name: authors
label: Authors
widget: list
fields:
- {label: Name, name: name, widget: string}
- {label: Description, name: description, widget: markdown}
```
### Relation Widget
The relation widget allows you to reference an existing entry from within the entry you're editing. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed.
The following field configuration properties are specific to fields using the relation widget:
Property | Accepted Values | Description
--- | --- | ---
`collection` | string | name of the collection being referenced
`searchFields` | list | one or more names of fields in the referenced colleciton to search for the typed value
`valueField` | string | name a field from the referenced collection whose value will be stored for the relation
`name` | text input | string
Let's say we have a "posts" collection and an "authors" collection, and we want to select an author for each post - our config might look something like this:
```yaml
collections:
- name: authors
label: Authors
folder: "authors"
create: true
fields:
- {name: name, label: Name}
- {name: twitterHandle, label: "Twitter Handle"}
- {name: bio, label: Bio, widget: text}
- name: posts
label: Posts
folder: "posts"
create: true
fields:
- {name: title, label: Title}
- {name: body, label: Body, widget: markdown}
- name: author
label: Author
widget: relation
collection: authors
searchFields: [name, twitterHandle]
valueField: name
```
### Date Widget / DateTime Widget
The date and datetime widgets provide date or datetime pickers when the widget is active. The resulting date string can be formatted (uses Moment.js), and accepts a default value. The initial value will be the current date unless `false` or an empty string are provided as the default value.
The following field configuration properties are specific to fields using the date or datetime widget:
Property | Accepted Values | Description
--- | --- | ---
`format` | string | format string uses Moment.js [tokens](https://momentjs.com/docs/#/parsing/string-format/)
`default` | boolean, string | can be a date string, or else an empty string - defaults to current date

View File

@ -0,0 +1,29 @@
---
title: Community
layout: community
url: /community
headline: Be a part of building the CMS of the future.
subhead: We're serious about being community driven, so everyone is welcome to join our open, bi-weekly planning sessions.
primarycta: "[Register on Eventbrite →](https://www.eventbrite.com/e/netlify-cms-planning-session-bi-weekly-tickets-35794058994)"
upcomingevent:
hook: The next development planning session is on
howitworks: Every other week we have public development planning sessions. They're web based, last about an hour, and are geared toward contributors and those interested in contributing. Sessions currently take place every other Wednesday, 9am - 10am PT.
howtojoin: |
**On the web:**
1. https://bluejeans.com/278173690
**By phone:**
1. [+1.408.740.7256](tel:+14087407256) (United States)
[+1.408.317.9253](tel:+14083179253) (Alternate number)
(http://bluejeans.com/numbers)
2. Enter Meeting ID: 278173690
---

0
website/site/data/.keep Executable file
View File

View File

@ -0,0 +1,13 @@
---
styleoverrides: '/docs.css'
headline: Netlify builds, deploys, and hosts your front end.
bottomcta:
hook: Want to get started quick?
btns:
- type: primary
btntext: View our Templates
linksto: https://app.netlify.com/signup/templates
- type: secondary
btntext: Read our Tutorials
linksto: /tags/tutorial/
---

View File

@ -0,0 +1,13 @@
---
footer:
hook: |
Host faster.</br>
Manage easier.</br>
Smile bigger.
headline: "Netlify is your one-stop web dev solution."
body: "Vestibulum rutrum quam vitae fringilla tincidunt. Suspendisse nec tortor urna. Ut laoreet sodales nisi, quis iaculis nulla iaculis vitae. Donec sagittis faucibu."
cta:
secondarycta:
copy: "Try it now FREE"
link: "/intro"
---

View File

@ -0,0 +1,50 @@
---
hero:
headline: An open-source CMS for your Git workflow
subhead: |
- Content management in a single-page app
- Built in React.js with extensible components
- Integrate with any build tool
- Plug in to any static site generator
ctas: |
- [Take it for a test drive →](/docs/test-drive)
productvideo:
thumbnail: "/img/hero-graphic.jpg"
mp4: "/img/demo.mp4"
ogg: "/img/demo.ogg"
webm: "/img/demo.webm"
thanksdevs: "**Thanks to all our contributors.**Keep shooting for the stars with Netlify CMS."
collab:
hook: "Integrate the roles of developers, writers, and editors."
body: "Writers can focus on writing. Editors can approve content and publish with ease. Developers can use their favorite tools and libraries. **Netlify CMS keeps all of their contributions together, in Git.**"
graphicpath: "/img/collab.svg"
featureshook: "Designed with developers in mind - its in our DNA."
features:
- feature: "Fast, web-based UI"
description: "Built with React, featuring rich-text editing, real-time preview, and drag-and-drop media uploads."
- feature: "Platform agnostic"
description: "Works with most static site generators for sites stored in GitHub."
- feature: "Easy installation"
description: "Add two files to your site, include or link to the JS, and add your custom configuration."
- feature: "Modern authentication"
description: "Connect with Netflify's authentication service or roll your own, using GitHub and JSON web tokens."
- feature: "Flexible content types"
description: "Specify an unlimited number of content types with custom fields."
- feature: "Fully extensible"
description: "Create custom-styled previews, UI widgets, and editor plugins."
featuresgraphic: "/img/helix.svg"
inspiration: |
Netlify CMS is part of a long evolution in content management for the web. Even in the budding realm of the [JAMstack](https://www.jamstack.org) and [static site generators](https://www.staticgen.com), developers have a variety [“headless” CMS](http://www.headlesscms.org) options to choose from.
Proprietary services like Contentful and DatoCMS provide the ease and polish of a concierge provider, while simple open source projects like Prose and Google Drive CMS provide targeted functionality for limited use cases. We love that these other projects help advance the modern web, and we believe there will always be a place for them in the JAMstack ecosystem.
With Netlify CMS, we hope to carve a different niche. We aim to do for the JAMstack what WordPress did for dynamic sites back in the day. In other words, we want to build a CMS that is open-source but fully-featured and production-ready, thats as easy to customize as it is to use, and that developers and content editors can build a community around.
[Help us make it happen!](/docs/contributor-guide)
---

View File

@ -0,0 +1,16 @@
notifications:
- loud: true
message: >-
Register to join us online for our next community dev meeting, every other
Wednesday at 9am-10am PT!
published: false
title: Netlify CMS Development Planning Sessions Promo
url: >-
https://www.eventbrite.com/e/netlify-cms-planning-session-bi-weekly-tickets-35794058994
- loud: true
message: >-
We have a community on Gitter - join now to ask questions and discuss the
project with other devs!
published: true
title: Gitter shoutout
url: 'https://gitter.im/netlify/netlifycms'

View File

@ -0,0 +1,35 @@
{{ partial "header" . }}
<div class="docs page">
<div class="container">
<aside id="sidebar" class="sidebar">
<nav class="docs-nav" id="docs-nav">
{{ range sort .Data.Pages "Params.position" }}
<a href="{{ .Permalink }}" class="nav-link {{ if eq .Params.position 0 }}active{{ end }}">{{ .Title }}</a>
{{ end }}
</nav>
<div class="mobile docs-nav">
<select class="btn-primary" id="mobile-docs-nav">
<option value="" selected="selected">Select A Topic</option>
{{ range sort .Data.Pages "Params.position" }}
<option value="{{ .Permalink }}" class="nav-link {{ if eq .Params.position 0 }}active{{ end }}">{{ .Title }}</option>
{{ end }}
</select>
</div>
</aside>
<article class="docs-content" id="docs-content">
{{ range sort .Data.Pages "Params.position" }}
{{ if eq .Params.position 0 }}
<div>
<p>
{{ partial "edit-link" . }}
</p>
{{ .Content }}
</div>
{{ end }}
{{ end }}
</article>
</div>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,30 @@
{{ partial "header" . }}
{{ $activePage := .Params.position }}
<div class="docs detail page">
<div class="container">
<aside id="sidebar" class="sidebar">
<nav class="docs-nav" id="docs-nav">
{{ $subjects := where .Site.Pages "Section" "docs" }}
{{ range sort $subjects "Params.position" }}
<a href="{{ .Permalink }}" class="nav-link {{ if eq .Params.position $activePage }}active{{ end }}">{{ .Title }}</a>
{{ end }}
</nav>
<div class="mobile docs-nav">
<select class="btn-primary" id="mobile-docs-nav">
<option value="" selected="selected">Select A Topic</option>
{{ $subjects := where .Site.Pages "Section" "docs" }}
{{ range sort $subjects "Params.position" }}
<option value="{{ .Permalink }}" class="nav-link {{ if eq .Params.position $activePage }}active{{ end }}">{{ .Title }}</option>
{{ end }}
</select>
</div>
</aside>
<article class="docs-content" id="docs-content">
{{ partial "edit-link" . }}
{{ .Content }}
</article>
</div>
</div>
{{ partial "footer" . }}

69
website/site/layouts/index.html Executable file
View File

@ -0,0 +1,69 @@
{{ partial "header" . }}
<div class="landing page">
<section class="hero">
<div class="contained">
<div class="hero-copy">
<h1 class="headline">{{ .Site.Data.landing.hero.headline | markdownify }}</h1>
<h2 class="subhead">{{ .Site.Data.landing.hero.subhead | markdownify }}</h2>
<h3 class="ctas">{{ .Site.Data.landing.hero.ctas | markdownify }}</h3>
<p class="mooseheads">
<strong>As seen on...</strong>
<img src="/img/smashing-logo.svg"/>
</p>
</div>
<div class="hero-graphic">
<img src="/img/demo.gif" class="responsive"/>
<!--<p class="thanks-devs">{{ .Site.Data.landing.thanksdevs | markdownify }}</p>-->
</div>
</div>
</section>
<section class="collab">
<div class="container">
<h1>{{ .Site.Data.landing.collab.hook | markdownify }}</h1>
<div class="row">
<div class="collab-graphic">
<img src="{{ .Site.Data.landing.collab.graphicpath }}" class="responsive"/>
</div>
<div class="collab-copy">
<p>{{ .Site.Data.landing.collab.body | markdownify }}</p>
</div>
</div>
</div>
</section>
<section class="features">
<div class="container">
<h1>{{ .Site.Data.landing.featureshook | markdownify }}</h1>
{{ $featuresgraphic := .Site.Data.landing.featuresgraphic }}
<div class="features-column">
{{ range $index, $id := .Site.Data.landing.features }}
<div class="feature">
<h3>{{ .feature | markdownify }}</h3>
<p>{{ .description | markdownify }}</p>
</div>
{{ if eq $index 2 }}
</div>
<div class="features-graphic">
<img src="{{ $featuresgraphic }}" class="responsive"/>
</div>
<div class="features-column">
{{ end }}
{{ end }}
</div>
</div>
</section>
<section class="inspiration">
<div class="container">
<h1>Our Inspiration</h1>
<p>{{ .Site.Data.landing.inspiration | markdownify }}</p>
</div>
</section>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,38 @@
{{ partial "header" . }}
<div class="community page">
<section class="hero">
<div class="contained">
<div class="hero-copy">
<h1 class="headline">{{ .Params.headline | markdownify }}</h1>
<h2 class="subhead">{{ .Params.subhead | markdownify }}</h2>
<h3 class="ctas"><ul><li>{{ .Params.primarycta | markdownify }}</li></ul></h3>
</div>
<div class="calendar-cta">
<h2>{{ .Params.upcomingevent.hook }}</h2>
<div class="calendar">
<div class="month"></div>
<div class="day"></div>
</div>
<h2><strong></strong> at <strong></strong></h2>
<div class="cal-cta">{{ .Params.primarycta | markdownify }}</div>
</div>
</div>
</section>
<section class="how-it-works clearfix">
<div class="contained">
<div class="half">
<h4 class="section-label">How it works</h4>
<p>{{ .Params.howitworks | markdownify }}</p>
<h4 class="section-label">How to join</h4>
<p>{{ .Params.howtojoin | markdownify }}</p>
</div>
</div>
</section>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,10 @@
<a class="edit-this-page" href="https://github.com/netlify/netlify-cms/blob/master/website/site/content/{{ .File.Path }}">
<svg version="1.1" id="pencil" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="14px" height="14px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path d="M398.875,248.875L172.578,475.187l-22.625-22.625L376.25,226.265L398.875,248.875z M308.375,158.39L82.063,384.687
l45.266,45.25L353.625,203.64L308.375,158.39z M263.094,113.125L36.828,339.437l22.625,22.625L285.75,135.765L263.094,113.125z
M308.375,67.875L285.719,90.5L421.5,226.265l22.625-22.625L308.375,67.875z M376.25,0L331,45.25l135.75,135.766L512,135.781
L376.25,0z M32,453.5V480h26.5L32,453.5 M0,376.25L135.766,512H0V376.25L0,376.25z"/>
</svg>
Edit this page
</a>

View File

@ -0,0 +1,25 @@
<footer>
<p class="contained"><a href="https://github.com/netlify/netlify-cms/blob/master/LICENSE" class="text-link">Distributed under MIT License</a> ·
<a href="https://github.com/netlify/netlify-cms/blob/master/CODE_OF_CONDUCT.md" class="text-link">Code of Conduct</a></p>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
{{ if or (eq .Section "docs") (eq .Title "Docs") }}
<script src="/jquery.scrollTo.min.js"></script>
<script src="/jquery.localScroll.min.js"></script>
<script src="/prism.js"></script>
{{ end }}
{{ if eq .Title "Community" }}
<script src="/moment.min.js"></script>
{{ end }}
<script src="/app.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script type="text/javascript"> docsearch({
apiKey: '08d03dc80862e84c70c5a1e769b13019',
indexName: 'netlifycms',
inputSelector: '.algolia-search',
debug: false // Set debug to true if you want to inspect the dropdown
});
</script>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!doctype html>
<html>
<head>
<title>{{ .Site.Title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" href="/img/favicon/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/img/favicon/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="/img/favicon/manifest.json">
<link rel="mask-icon" href="/img/favicon/safari-pinned-tab.svg" color="#96cf05">
<meta name="apple-mobile-web-app-title" content="NetlifyCMS">
<meta name="application-name" content="NetlifyCMS">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="/css/main.css"/>
{{ if or (eq .Section "docs") (eq .Title "Docs") }}
<link rel="stylesheet" href="/css/lib/prism.css"/>
{{ end }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
</head>
<body>
{{ range .Site.Data.notifications }}
{{ range . }}
{{ if .published }}
<a href="{{ .url }}" class="notification {{ if .loud }}notification-loud{{end}}">{{ .message }}</a>
{{ end }}
{{ end }}
{{ end }}
<header id="header" {{ if or (eq .Section "docs") (eq .Title "Docs") }}class="docs"{{ end }}>
<div class="contained">
<a href="/" class="logo"><img src="/img/netlify-cms-logo.svg"/></a>
<a class="nav-link docs-link" href="/docs">Docs</a>
<a class="nav-link contributing-link" href="/docs/contributor-guide">Contributing</a>
<a class="nav-link" href="/community">Community</a>
<a class="utility-input">
<img src="/img/search.svg" />
<input type="search" placeholder="Search the docs" class="algolia-search"/>
</a>
<a href="https://github.com/netlify/netlify-cms" class="btn-secondary small github-btn "><img src="/img/github.svg"/> Fork me on GitHub</a>
</div>
</header>

0
website/site/static/.keep Executable file
View File

View File

@ -0,0 +1 @@
/docs/custom-authentication /docs/authentication-backends 301

View File

@ -0,0 +1,35 @@
backend:
name: github
repo: netlify/netlify-cms
publish_mode: editorial_workflow
media_folder: "website/site/static/img" # Folder where user uploaded files should go
public_folder: "img"
collections: # A list of collections the CMS should be able to edit
- name: "docs" # Used in routes, ie.: /admin/collections/:slug/edit
label: "Docs" # Used in the UI, ie.: "New Post"
folder: "website/site/content/docs" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
fields: # The fields each document in this collection have
- {label: "Title", name: "title", widget: "string", tagname: "h1"}
- {label: "Position", name: "position", widget: "number"}
- {label: "Body", name: "body", widget: "markdown"}
- name: notifications
label: Notifications
files:
- name: notifications
label: Notifications
file: website/site/data/notifications.yml
description: Site-top notifications - publish one at a time
fields:
- name: notifications
label: Notifications
widget: list
fields:
- {label: Title, name: title, widget: string, tagname: h1}
- {label: Published, name: published, widget: boolean}
- {label: Loud, name: loud, widget: boolean}
- {label: Message, name: message, widget: text}
- {label: URL, name: url}

View File

@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
<!-- Include the stylesheets from your site here -->
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@^0.7/dist/cms.css" />
<!-- Include a CMS specific stylesheet here -->
</head>
<body>
<script src="https://unpkg.com/netlify-cms@^0.7/dist/cms.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -0,0 +1,7 @@
/**
* Copyright (c) 2007-2016 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
* Licensed under MIT
* @author Ariel Flesler
* @version 1.4.0
*/
;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var g=location.href.replace(/#.*/,'');var h=$.localScroll=function(a){$('body').localScroll(a)};h.defaults={duration:1000,axis:'y',event:'click',stop:true,target:window};$.fn.localScroll=function(a){a=$.extend({},h.defaults,a);if(a.hash&&location.hash){if(a.target)window.scrollTo(0,0);scroll(0,location,a)}return a.lazy?this.on(a.event,'a,area',function(e){if(filter.call(this)){scroll(e,this,a)}}):this.find('a,area').filter(filter).bind(a.event,function(e){scroll(e,this,a)}).end().end();function filter(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')===g&&(!a.filter||$(this).is(a.filter))}};h.hash=function(){};function scroll(e,a,b){var c=a.hash.slice(1),elem=document.getElementById(c)||document.getElementsByName(c)[0];if(!elem)return;if(e)e.preventDefault();var d=$(b.target);if(b.lock&&d.is(':animated')||b.onBefore&&b.onBefore(e,elem,d)===false)return;if(b.stop){d.stop(true)}if(b.hash){var f=elem.id===c?'id':'name',$a=$('<a> </a>').attr(f,c).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});elem[f]='';$('body').prepend($a);location.hash=a.hash;$a.remove();elem[f]=c}d.scrollTo(elem,b).trigger('notify.serialScroll',[elem])}return h}));

View File

@ -0,0 +1,7 @@
/**
* Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
* Licensed under MIT
* @author Ariel Flesler
* @version 2.1.0
*/
;(function(l){'use strict';l(['jquery'],function($){var k=$.scrollTo=function(a,b,c){return $(window).scrollTo(a,b,c)};k.defaults={axis:'xy',duration:0,limit:true};function isWin(a){return!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!==-1}$.fn.scrollTo=function(f,g,h){if(typeof g==='object'){h=g;g=0}if(typeof h==='function'){h={onAfter:h}}if(f==='max'){f=9e9}h=$.extend({},k.defaults,h);g=g||h.duration;var j=h.queue&&h.axis.length>1;if(j){g/=2}h.offset=both(h.offset);h.over=both(h.over);return this.each(function(){if(f===null)return;var d=isWin(this),elem=d?this.contentWindow||window:this,$elem=$(elem),targ=f,attr={},toff;switch(typeof targ){case'number':case'string':if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=d?$(targ):$(targ,elem);if(!targ.length)return;case'object':if(targ.is||targ.style){toff=(targ=$(targ)).offset()}}var e=$.isFunction(h.offset)&&h.offset(elem,targ)||h.offset;$.each(h.axis.split(''),function(i,a){var b=a==='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,prev=$elem[key](),max=k.max(elem,a);if(toff){attr[key]=toff[pos]+(d?0:prev-$elem.offset()[pos]);if(h.margin){attr[key]-=parseInt(targ.css('margin'+b),10)||0;attr[key]-=parseInt(targ.css('border'+b+'Width'),10)||0}attr[key]+=e[pos]||0;if(h.over[pos]){attr[key]+=targ[a==='x'?'width':'height']()*h.over[pos]}}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)==='%'?parseFloat(c)/100*max:c}if(h.limit&&/^\d+$/.test(attr[key])){attr[key]=attr[key]<=0?0:Math.min(attr[key],max)}if(!i&&h.axis.length>1){if(prev===attr[key]){attr={}}else if(j){animate(h.onAfterFirst);attr={}}}});animate(h.onAfter);function animate(a){var b=$.extend({},h,{queue:true,duration:g,complete:a&&function(){a.call(elem,targ,h)}});$elem.animate(attr,b)}})};k.max=function(a,b){var c=b==='x'?'Width':'Height',scroll='scroll'+c;if(!isWin(a))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,doc=a.ownerDocument||a.document,html=doc.documentElement,body=doc.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return $.isFunction(a)||$.isPlainObject(a)?a:{top:a,left:a}}$.Tween.propHooks.scrollLeft=$.Tween.propHooks.scrollTop={get:function(t){return $(t.elem)[t.prop]()},set:function(t){var a=this.get(t);if(t.options.interrupt&&t._last&&t._last!==a){return $(t.elem).stop()}var b=Math.round(t.now);if(a!==b){$(t.elem)[t.prop](b);t._last=this.get(t)}}};return k})}(typeof define==='function'&&define.amd?define:function(a,b){'use strict';if(typeof module!=='undefined'&&module.exports){module.exports=b(require('jquery'))}else{b(jQuery)}}));

7
website/site/static/moment.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

150
website/src/css/imports/base.css Executable file
View File

@ -0,0 +1,150 @@
@import url(https://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,500,700,900|Roboto+Mono:400,700);
body {
color: $grey;
font-family: $roboto;
margin: 0;
text-align: center;
-webkit-font-smoothing: antialiased;
display: flex;
flex-direction: column;
min-height: 100vh;
@media screen and (min-width: $tablet) {
text-align: left;
}
}
.page {
flex-grow: 1;
}
h1 {
font-weight: $light;
font-size: $small;
line-height: $medium;
margin: 0 0 -10px 0;
@media screen and (min-width: $tablet) {
font-size: 36px;
line-height: 48px;
}
}
h2 {
font-family: $roboto;
font-size: 18px;
font-weight: $bold;
margin: 0;
&.subhead {
font-weight: $regular;
}
}
h3 {
font-family: $roboto;
font-size: $small;
font-weight: $regular;
line-height: 32px;
margin: 0;
}
p, ul {
font-size: 14px;
line-height: $small;
@media screen and (min-width: $tablet) {
font-size: 18px;
line-height: 32px;
}
}
a {
color: $grey;
text-decoration: none;
font-weight: $bold;
}
ul {
margin: $tiny 0 $tiny $small;
padding: 0;
}
.contained {
margin: 0 auto;
max-width: $display;
padding: 0 $small;
@media screen and (min-width: $tablet) {
padding: 0 $medium;
}
}
*[class^="btn-"] {
border-radius: $borderRadius;
box-sizing: border-box;
display: inline-block;
font-family: $roboto;
font-size: $tiny;
font-weight: $bold;
margin: 0;
padding: $tiny $small;
position: relative;
overflow: hidden;
&:after {
content: "";
position: absolute;
top: -40%;
left: -210%;
width: 200%;
height: 200%;
opacity: 0;
transform: rotate(30deg);
background: rgba(255,255,255,0.2);
background: linear-gradient(
to right,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 77%,
rgba(255, 255, 255, 0.6) 92%,
rgba(255, 255, 255, 0.0) 100%
);
}
&:hover:after {
opacity: 1;
left: 110%;
transition-property: left, opacity;
transition-duration: 0.6s, 0.1s;
transition-timing-function: ease;
}
&:active:after {
opacity: 0;
}
&.small {
padding: $micro $tiny;
}
}
.btn-primary {
background-image: linear-gradient(0deg, $lightGreen 14%, $green 94%);
color: $darkerGrey;
}
.btn-secondary {
border: 1px solid white;
color: white;
}
pre {
border-radius: $borderRadius;
}
.code,
code {
font-family: 'Roboto Mono', monospace !important;
font-size: 14px;
}

View File

@ -0,0 +1,54 @@
.collab,
.how-it-works {
margin: $medium $tiny;
@media screen and (min-width: $mobile) {
margin: $large auto;
}
}
.collab {
h1 {
margin-bottom: $small;
text-align: center;
@media screen and (min-width: $mobile) {
margin-bottom: $large;
}
}
.collab-graphic {
@media screen and (min-width: $mobile) {
@neat-span-columns 4;
@neat-shift 1;
}
}
p {
margin-top: $medium;
@media screen and (min-width: $mobile) {
margin-top: 0;
text-align: left;
@neat-span-columns 5;
@neat-shift 1;
}
@media screen and (min-width: $tablet) {
margin-top: $medium;
}
}
}
.how-it-works {
padding-bottom: $xl;
text-align: left;
.section-label:not(:first-child) {
margin-top: $large;
}
a {
color: $darkGreen;
}
}

View File

@ -0,0 +1,222 @@
.docs.page {
padding: 69px $tiny $xl;
text-align: left;
@media screen and (min-width: $mobile) {
padding: 157px $medium $xl;
}
.sidebar {
@media screen and (min-width: $tablet) {
@neat-span-columns 6 24;
}
}
.docs-nav {
display: none;
@media screen and (min-width: $tablet) {
display: block;
}
@media screen and (min-width: $desktop) {
position: fixed;
}
&.mobile {
display: block;
position: relative;
@media screen and (min-width: $tablet) {
display: none;
}
&:after {
content: " ";
position: absolute;
top: 7px;
right: 20px;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid $grey;
z-index: 3;
}
select {
border: none;
border-radius: $borderRadius !important;
cursor: pointer;
margin: -$medium auto $medium auto;
width: 100%;
position: relative;
padding: 14px 20px;
z-index: 2;
outline: none;
box-shadow: none;
-webkit-appearance: none;
-webkit-border-radius: 100px;
}
}
.nav-link,
.subnav-link {
display: block;
font-weight: $regular;
color: $grey;
line-height: 32px;
text-decoration: none;
text-transform: capitalize;
transition: color .2s ease;
&.active {
color: $darkGreen;
font-weight: $bold;
}
&:hover {
color: $darkGreen;
}
}
}
.nav-subsections {
margin: $tiny 0;
padding: 0 0 0 $tiny;
border-left: 2px solid $lightestGrey;
list-style-type: none;
li {
margin: 0;
padding: 0;
}
.subnav-link {
font-size: 14px;
}
}
.docs-content {
font-size: 18px;
line-height: 28px;
font-weight: $light;
.edit-this-page {
float: right;
}
#pencil {
fill: #7CA511;
}
@media screen and (min-width: $tablet) {
@neat-span-columns 17 24;
@neat-shift 1 24;
}
@media screen and (min-width: $desktop) {
@neat-shift 7 24;
}
h2:not(:first-child) {
margin-top: 86px;
}
a {
text-decoration: none;
color: $darkGreen;
}
iframe {
width: 100%;
}
img {
max-width: 100%;
height: auto;
}
table {
background: #f7f7f7;
border-radius: $borderRadius;
}
code {
background: $lightestGrey;
border-radius: 2px;
padding: 2px 6px;
white-space: nowrap;
}
pre > code {
background: initial;
padding: initial;
white-space: inherit;
}
}
h1,
h2 {
font-size: 36px;
line-height: 48px;
&.intro-headline {
padding: 0 $small;
margin-bottom: 86px;
}
}
h2 {
font-size: $small;
}
h3 {
color: $grey;
font-size: 12px;
font-weight: $semibold;
text-transform: uppercase;
letter-spacing: 1.5px;
margin-top: $medium;
&:after {
content: ' ';
width: $small;
height: 2px;
background: $darkGreen;
display: block;
margin-top: 5px;
}
&.inverse {
color: white;
}
}
table {
width: 100%;
text-align: left;
margin: 34px 0 $medium 0;
th,
td {
padding: $micro;
}
th {
font-size: 18px;
font-weight: $bold;
}
tbody tr {
&:nth-child(odd) {
background: #fdfdfd;
}
}
td {
font-size: 14px;
}
}
}

View File

@ -0,0 +1,81 @@
.features {
@neat-row;
overflow: hidden;
padding-top: $large;
position: relative;
margin: 0 auto $medium auto;
@media screen and (min-width: $mobile) {
margin: 0 auto $large auto;
padding-top: calc($xl * 1.2);
}
@media screen and (min-width: $tablet) {
margin-bottom: $medium;
}
&:before {
background: url('/img/wavy-divider.svg') no-repeat top center;
background-size: contain;
top: 0;
content: '';
height: $xl;
left: -$xl;
position: absolute;
right: -$xl;
width: calc(100% + ($xl * 2));
}
h1 {
text-align: center;
margin-bottom: calc($large * .9);
padding: 0 $small;
@media screen and (min-width: $tablet) {
padding: 0;
}
+ .features-column .feature {
@media screen and (min-width: $desktop) {
text-align: right;
}
}
}
.features-column {
@media screen and (min-width: $mobile) {
@neat-span-columns 6;
}
@media screen and (min-width: $desktop) {
@neat-span-columns 4;
}
}
.feature {
margin-bottom: $small;
padding: 0 $small;
text-align: left;
@media screen and (min-width: $desktop) {
margin-bottom: calc($large * 1.37);
padding: 0;
}
p {
font-size: 14px;
line-height: 26px;
}
}
.features-graphic {
display: none;
padding-top: $tiny;
@media screen and (min-width: $desktop) {
display: inline-block;
@neat-span-columns 4;
}
}
}

View File

@ -0,0 +1,12 @@
footer {
background: $lighterGrey;
padding: $small 0;
p {
color: $grey;
font-family: $roboto;
font-size: 12px;
opacity: .5;
text-align: center;
}
}

View File

@ -0,0 +1,262 @@
.notification {
background: #414344;
box-sizing: border-box;
color: white;
display: block;
padding: $tiny $small;
position: absolute;
text-align: center;
width: 100%;
z-index: 101;
@media screen and (min-width: $mobile) {
position: fixed;
}
em {
font-style: normal;
color: #8B8B8B;
padding: 0 8px;
}
sup,
sub {
font-size: initial;
vertical-align: initial;
}
.text-link {
text-decoration: underline;
color: $green;
}
&.notification-loud {
background-color: $green;
color: $darkerGrey;
}
+ header {
margin-top: 100px;
@media screen and (min-width: 360px) {
margin-top: 74px;
}
@media screen and (min-width: 712px) {
margin-top: 50px;
}
+ div:before,
+ .hero:before {
content: '';
display: block;
height: 100px;
width: 100%;
@media screen and (min-width: 360px) {
height: 74px;
}
@media screen and (min-width: 712px) {
height: 50px;
}
}
}
}
header {
background: transparent;
box-shadow: none;
font-family: $roboto;
position: absolute;
padding: $medium 0;
text-align: center;
transition: background .2s ease, padding .2s ease, box-shadow .2s ease;
width: 100%;
z-index: 100;
@media screen and (min-width: $mobile) {
text-align: right;
position: fixed;
}
&.scrolled {
@media screen and (min-width: $mobile) {
background: $darkGrey;
padding: $small 0;
}
}
&.docs {
background: $darkGrey;
padding: $small 0;
@media screen and (max-width: $mobile) {
position: static;
}
.nav-link {
@media screen and (min-width: 487px) and (max-width: 767px) {
margin-top: $micro;
}
}
.github-btn {
@media screen and (max-width: 767px) {
display: none;
}
}
.utility-input {
@media screen and (max-width: 767px) {
display: block;
height: $small;
margin: $tiny 0 0 0;
}
@media screen and (min-width: 487px) and (max-width: 767px) {
margin-top: 32px;
}
}
}
a {
color: white;
display: inline-block;
vertical-align: middle;
-webkit-vertical-align: middle !important;
margin-left: $micro;
@media screen and (min-width: $mobile) {
margin-left: 4px;
}
@media screen and (min-width: $tablet) {
margin-left: $micro;
}
&.nav-link:not(:nth-child(2)):before {
content: '•';
padding-right: $tiny;
color: $green;
@media screen and (min-width: $mobile) {
padding-right: $micro;
}
@media screen and (min-width: $tablet) {
padding-right: $tiny;
}
}
&:hover {
color: $green;
}
&.github-btn {
margin-top: $tiny;
margin-left: $micro;
@media screen and (min-width: 688px) {
margin-top: 0;
}
}
}
img {
margin: 0;
padding: 0;
}
.algolia-search {
margin-top: 1px;
-webkit-margin-top: 0;
-webkit-display: inline-block;
vertical-align: baseline !important;
}
input.closed {
display: none;
}
input {
padding-left: 0;
border: none;
border-radius: 0;
appearance: none;
background: none;
color: $lightGrey;
padding: 2px $micro $micro 0;
display: inline-block;
font-size: $tiny;
font-weight: $regular;
max-width: 160px;
-webkit-appearance: none;
visibility: visible;
&:focus {
outline: none;
display: inline-block;
~ .bar {
&::before,
&::after {
width: 50%;
}
}
~ .button-submit {
margin-top: -35px;
}
}
}
.utility-input {
display: none;
padding: $micro;
width: auto;
border: 1px solid white;
border-radius: 4px;
background: none;
color: white;
margin: 0 $micro 0 10px;
padding-bottom: 9px;
text-align: left;
text-decoration: none;
font-weight: 600;
font-size: 16px;
line-height: 24px;
transition: all .2s ease-in-out;
@media screen and (min-width: 968px) {
display: inline;
}
&:focus,
&:active {
outline-style: none;
}
&::-webkit-input-placeholder,
&:-moz-placeholder,
&::-moz-placeholder,
&:-ms-input-placeholder {
font-size: $tiny;
font-weight: $semibold;
text-align: left;
text-decoration: none;
line-height: $small;
}
}
.logo {
margin: 0 auto $tiny auto;
width: 100%;
@media screen and (min-width: $mobile) {
float: left;
margin: -$micro 0 -6px 0;
width: initial;
}
}
}

View File

@ -0,0 +1,231 @@
.hero {
@neat-row;
background: $darkerGrey;
background-image: linear-gradient(-17deg, $darkerGrey 17%, $darkGrey 94%);
color: white;
overflow: hidden;
padding: calc($xl * 2.25) 0 $large 0;
position: relative;
@media screen and (min-width: $mobile) {
padding-top: calc($xl * 1.5);
}
@media screen and (min-width: $tablet) {
padding-top: calc($large * 1.5);
}
&:before {
background: url('/img/bow.svg') no-repeat bottom center;
background-size: contain;
bottom: -1px;
content: '';
height: $xl;
left: -$xl;
position: absolute;
right: -$xl;
width: calc(100% + ($xl * 2));
}
@media screen and (min-width: $tablet) {
background-position: center;
padding: calc($xl * 1.5) 0 $xl 0;
}
.hero-copy {
@media screen and (min-width: $tablet) {
@neat-span-columns 6;
}
}
.headline {
margin-top: -12px;
max-width: 400px;
@media screen and (min-width: $mobile) {
text-align: left;
}
span {
display: none;
@media screen and (min-width: $tablet) {
display: initial;
}
}
}
.subhead {
display: inline-block;
margin: $micro auto;
text-align: left;
@media screen and (min-width: $mobile) {
display: block;
margin: $medium 0 $small 0;
}
}
.ctas {
margin-bottom: $small;
@media screen and (min-width: $mobile) {
text-align: left;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
a {
color: $green;
font-weight: $semibold;
}
}
.btn-secondary {
display: none;
padding-top: 14px;
max-height: 53px;
margin: 0 0 0 calc($micro / 2);
@media screen and (min-width: $tablet) {
display: inline-block;
}
}
strong {
display: block;
}
.mooseheads {
margin: $micro 0 0 0;
@media screen and (min-width: $mobile) {
text-align: left;
}
strong {
font-size: 12px;
opacity: .5;
}
}
.hero-graphic {
@neat-span-columns 6;
display: none;
@media screen and (min-width: $tablet) {
display: initial;
}
&:before {
border: 1px solid white;
border-bottom: none;
border-radius: $largeBorderRadius $largeBorderRadius 0 0;
box-sizing: border-box;
content: '•••';
display: block;
font-size: $small;
height: $small;
line-height: 22px;
padding-left: 6px;
text-align: left;
width: 100%;
}
img {
border: 1px solid white;
border-radius: 0 0 $largeBorderRadius $largeBorderRadius;
box-sizing: border-box;
overflow: hidden;
width: 100%;
height: auto;
}
}
.calendar-cta {
text-align: center;
background: $darkerGrey;
background-image: linear-gradient(-17deg, $darkerGrey 17%, $darkGrey 94%);
border-radius: $largeBorderRadius;
box-shadow: 0 $micro $small rgba(0,0,0,0.1);
padding: $medium;
box-sizing: border-box;
@media screen and (min-width: $tablet) {
max-width: 446px;
}
@media screen and (min-width: $tablet) {
@neat-span-columns 5;
@neat-shift 1;
display: inline-block;
position: fixed;
right: $medium;
}
@media screen and (min-width: 1280px) {
right: initial;
left: calc(50% - $large);
}
.calendar {
border-radius: $largeBorderRadius;
overflow: hidden;
box-shadow: 0 $micro $small rgba(0,0,0,0.5);
margin: $small auto;
max-width: 250px;
.month {
background: $green;
color: $grey;
font-weight: $black;
text-transform: uppercase;
letter-spacing: 4px;
font-size: $tiny;
padding: $tiny;
}
.day {
font-size: $xl;
font-weight: $black;
color: white;
border: 1px solid $grey;
border-top: none;
border-bottom-left-radius: $largeBorderRadius;
border-bottom-right-radius: $largeBorderRadius;
}
}
strong {
display: inline-block;
}
h2:not(:first-child) {
font-weight: $light;
}
.cal-cta {
margin-top: $micro;
a {
color: $green;
}
}
}
.thanks-devs {
line-height: 18px;
margin: $micro 0 0 0;
opacity: 0.5;
text-align: center;
@media screen and (min-width: $tablet) {
font-size: $tiny;
line-height: 21px;
}
}
}

View File

@ -0,0 +1,41 @@
.inspiration {
margin: $small $small $large $small;
text-align: center;
@media screen and (min-width: $tablet) {
margin: $xl $small;
}
h1 {
margin-bottom: $small;
@media screen and (min-width: $tablet) {
margin-bottom: 44px;
}
}
p {
box-sizing: border-box;
display: inline-block;
padding: 0;
margin: 0 auto $small auto;
max-width: 700px;
text-align: left;
width: 100%;
&:nth-child(2) {
font-size: $tiny;
line-height: $small;
font-weight: $bold;
@media screen and (min-width: $tablet) {
font-size: $small;
line-height: $medium;
}
}
}
a {
color: $darkGreen;
}
}

View File

@ -0,0 +1,259 @@
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.centered-text {
text-align: center;
}
.container {
@neat-outer-container;
}
.half,
.third,
.quarter {
padding-bottom: $small;
@neat-span-columns 12;
@media screen and (min-width: $tablet) {
@neat-span-columns 6;
padding-bottom: 0;
}
&:nth-child(even) {
@media screen and (min-width: $mobile) and (max-width: 767px) {
margin-right: 0;
}
}
}
.third {
@media screen and (min-width: $tablet) {
@neat-span-columns 4;
}
}
.quarter {
@media screen and (min-width: $tablet) {
@neat-span-columns 3;
}
}
.clearfix {
&:after {
content: ' ';
width: 100%;
display: table;
}
}
.section-label {
color: $grey;
font-size: 12px;
font-weight: $semibold;
letter-spacing: 1.5px;
text-transform: uppercase;
&:after {
background: $darkGreen;
content: ' ';
display: block;
height: 2px;
margin-top: 5px;
width: $small;
}
&.inverse {
color: white;
&.mono:after {
background: $grey;
}
}
&.extended {
display: inline-block;
line-height: 1.15;
&:after {
position: relative;
height: 1px;
left: -10000%;
width: calc(10100%);
z-index: -1;
}
}
}
img.responsive {
width: 100%;
height: auto;
}
.img-bg-hero {
color: white;
background-size: 100% auto;
background-size: cover !important;
h1,
h2 {
color: white;
font-weight: $light;
@media screen and (min-width: $tablet) {
font-weight: $thin;
}
}
}
.pagination {
text-align: center;
margin: 0;
padding: 0;
list-style-type: none;
@media screen and (min-width: $tablet) {
@neat-span-columns 8;
@neat-shift 2;
}
li {
margin: 0;
padding: 0;
display: inline;
@media screen and (min-width: $tablet) {
display: inline-block;
}
&.active a,
a[aria-label] {
display: inline-block;
}
}
a {
text-decoration: none;
font-weight: $light;
color: $grey;
width: 18px;
font-size: $tiny;
padding: $micro;
border-radius: 99px;
display: none;
@media screen and (min-width: $tablet) {
display: inline-block;
}
}
.active a {
color: white;
background: $green;
}
.disabled a {
color: $lighterGrey;
}
}
.unordered-list ul {
margin: 0 0 $small 0;
padding: 0;
font-size: 16px;
line-height: 28px;
list-style-type: none;
list-style-position: inside;
&:last-child {
margin-bottom: 0;
}
li span {
position: relative;
padding-left: $small;
display: list-item;
&:before {
content: '\2192';
color: $green;
position: absolute;
left: 0;
}
}
}
.numbered-list ol {
list-style-type: decimal-leading-zero;
margin: 32px 0 0 0;
padding: 0 0 0 78px;
li {
margin-bottom: 33px;
font-size: 34px;
font-weight: $bold;
color: $grey;
position: relative;
&:before,
&:after {
position: absolute;
font-size: 12px;
}
&:before {
content: 'N';
top: 6px;
left: -78px;
padding-bottom: 2px;
border-bottom: 1px solid $green;
padding-right: 25px;
}
&:after {
content: 'ọ';
left: -69px;
top: 4px;
}
&:last-child {
margin-bottom: 0;
}
}
h2 {
font-size: 34px;
margin-bottom: $tiny;
@media screen and (min-width: $tablet) {
margin-bottom: 12px;
}
&:before {
content: ' ';
position: absolute;
background: white;
left: -19px;
height: 10px;
width: 10px;
top: $small;
}
}
p {
margin: 0;
font-size: 18px;
font-weight: $regular;
line-height: 28px;
color: $lightGrey;
}
}

View File

@ -0,0 +1,178 @@
/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+bash+ruby+git+go+json+jsx+yaml&plugins=line-numbers */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
pre.line-numbers {
position: relative;
padding-left: 3.8em;
counter-reset: linenumber;
}
pre.line-numbers > code {
position: relative;
}
.line-numbers .line-numbers-rows {
position: absolute;
pointer-events: none;
top: 0;
font-size: 100%;
left: -3.8em;
width: 3em; /* works for line-numbers below 1000 lines */
letter-spacing: -1px;
border-right: 1px solid #999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.line-numbers-rows > span {
pointer-events: none;
display: block;
counter-increment: linenumber;
}
.line-numbers-rows > span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: 0.8em;
text-align: right;
}

10
website/src/css/main.css Executable file
View File

@ -0,0 +1,10 @@
@import "imports/base.css";
@import "imports/utilities.css";
@import "imports/header.css";
@import "imports/hero.css";
@import "imports/collab.css";
@import "imports/features.css";
@import "imports/inspiration.css";
@import "imports/docs.css";
@import "imports/footer.css";

16
website/src/img/bow.svg Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1440px" height="26px" viewBox="0 0 1440 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 42 (36781) - http://www.bohemiancoding.com/sketch -->
<title>bow</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="landing" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="cms-landing-v6" transform="translate(0.000000, -666.000000)" fill="#FFFFFF">
<g id="hero-(green)">
<g id="background">
<path d="M0,692 L1440,692 L1440,666.431373 C1440,666.431373 1080,692 720,692 C360,692 0,666.431373 0,666.431373 L0,692 Z" id="bow"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 819 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

BIN
website/src/img/demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#222222</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,18 @@
{
"name": "NetlifyCMS",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -0,0 +1,98 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,16.000000) scale(0.002286,-0.002286)"
fill="#000000" stroke="none">
<path d="M2750 6290 c-393 -390 -712 -711 -710 -714 3 -2 196 -85 430 -184
234 -99 447 -189 473 -201 49 -21 49 -21 76 -2 74 53 141 66 299 58 33 -2 33
-2 318 438 156 242 288 447 294 455 15 22 145 223 153 237 4 6 -113 131 -298
315 -168 166 -309 304 -313 305 -4 1 -329 -317 -722 -707z"/>
<path d="M4193 6162 c-23 -37 -92 -143 -152 -237 -60 -93 -200 -311 -311 -483
-111 -172 -204 -321 -207 -331 -3 -11 2 -30 11 -42 8 -13 22 -51 30 -83 9 -32
17 -60 19 -62 3 -4 76 -35 227 -99 69 -29 275 -116 459 -194 183 -78 339 -140
345 -137 70 35 110 64 111 81 1 11 5 36 8 55 4 19 9 53 12 75 3 22 8 51 10 65
3 14 7 39 9 55 3 17 14 86 25 155 12 69 23 143 27 165 3 22 7 51 10 65 5 32
33 204 40 250 3 19 10 55 14 80 6 31 5 50 -3 60 -20 24 -627 625 -635 627 -4
1 -26 -28 -49 -65z"/>
<path d="M1495 5040 c-209 -209 -379 -383 -378 -388 2 -7 418 -619 440 -647 4
-5 20 -28 35 -50 15 -22 103 -152 197 -289 l170 -248 109 -2 108 -1 88 135
c48 74 109 169 136 210 26 41 167 259 313 485 l264 409 -33 49 c-19 27 -41 73
-50 103 -15 52 -21 144 -11 161 11 17 -10 31 -108 73 -55 23 -279 118 -497
211 -218 93 -398 169 -400 169 -2 0 -174 -171 -383 -380z"/>
<path d="M5066 5373 c-7 -37 -66 -399 -72 -440 -2 -18 -11 -72 -19 -120 -9
-49 -18 -104 -21 -123 -3 -19 -7 -46 -9 -60 -17 -105 -20 -93 38 -137 l53 -39
43 18 c24 10 57 25 75 33 17 7 119 51 226 95 107 45 209 88 225 95 17 8 52 23
79 35 l50 22 -327 324 c-180 178 -329 324 -331 324 -2 0 -7 -12 -10 -27z"/>
<path d="M3491 4679 c-67 -81 -156 -122 -270 -123 l-63 -1 -234 -360 c-129
-198 -238 -367 -242 -375 -4 -8 -41 -67 -82 -130 -202 -308 -234 -359 -227
-367 5 -4 191 70 342 138 33 14 333 142 1165 496 58 25 119 51 135 58 17 7
127 55 245 105 118 50 218 94 221 98 4 4 9 26 12 49 l5 41 -77 33 c-42 18
-101 43 -131 57 -30 13 -93 40 -140 59 -47 19 -206 86 -353 149 -148 63 -269
114 -270 114 -1 0 -17 -19 -36 -41z"/>
<path d="M5755 4534 c-71 -31 -215 -92 -320 -136 -264 -112 -285 -122 -282
-131 1 -5 93 -47 202 -93 110 -46 209 -88 220 -94 11 -5 81 -34 155 -66 74
-31 149 -62 165 -69 17 -8 75 -32 130 -55 55 -23 114 -47 130 -55 204 -91 822
-345 834 -343 11 3 -59 78 -209 227 -124 122 -371 368 -551 547 -179 178 -329
324 -335 324 -5 0 -67 -26 -139 -56z"/>
<path d="M720 4279 c-454 -445 -720 -713 -712 -720 4 -3 99 -25 212 -48 243
-50 814 -169 850 -177 35 -8 61 -13 95 -20 17 -2 46 -9 65 -14 19 -5 50 -12
68 -15 18 -3 65 -12 105 -21 39 -8 81 -17 92 -19 11 -3 52 -11 90 -19 116 -25
109 -26 153 33 l41 54 -43 61 c-23 34 -130 187 -236 341 -428 620 -447 648
-459 662 -6 7 -16 24 -23 38 -6 14 -14 25 -17 25 -4 0 -15 14 -24 30 -10 17
-21 30 -25 30 -4 0 -109 -99 -232 -221z"/>
<path d="M5030 4045 c-31 -33 -94 -70 -147 -85 -49 -15 -48 -13 -68 -155 -4
-22 -8 -47 -10 -55 -2 -8 -7 -35 -10 -60 -3 -25 -10 -67 -15 -95 -5 -27 -11
-66 -14 -85 -3 -19 -12 -75 -20 -125 -21 -124 -65 -392 -72 -439 -3 -21 -7
-46 -9 -55 -19 -102 -19 -98 24 -147 26 -29 47 -68 58 -103 9 -31 21 -61 27
-67 6 -6 58 -21 116 -32 58 -11 193 -39 300 -62 107 -22 209 -43 225 -46 17
-3 46 -9 65 -14 29 -7 102 -23 215 -46 11 -2 31 -6 45 -9 14 -3 43 -8 65 -11
l40 -5 490 487 490 487 -45 19 c-25 11 -209 89 -410 174 -201 85 -374 158
-385 163 -11 5 -41 18 -67 30 -26 12 -50 21 -53 21 -2 0 -28 11 -57 24 -77 35
-744 316 -751 316 -2 0 -14 -11 -27 -25z"/>
<path d="M4465 3985 c-128 -56 -140 -61 -325 -140 -85 -36 -168 -72 -185 -79
-16 -8 -75 -33 -130 -56 -154 -65 -1196 -509 -1230 -524 -16 -8 -54 -24 -82
-36 -48 -20 -53 -25 -53 -54 0 -17 4 -36 8 -43 6 -10 143 -43 272 -67 14 -2
81 -16 150 -31 69 -14 136 -29 150 -31 60 -12 221 -45 290 -59 243 -51 408
-85 430 -89 14 -3 34 -7 45 -9 11 -3 103 -22 204 -43 l184 -37 19 24 c56 73
132 123 200 131 35 5 38 9 44 50 3 18 14 87 25 153 23 141 26 157 34 210 3 23
8 52 10 65 3 14 10 54 15 90 18 111 51 314 55 340 3 14 12 73 21 133 16 102
16 108 -1 127 -24 27 -35 25 -150 -25z"/>
<path d="M138 3305 c-8 -9 652 -660 665 -657 7 2 172 71 367 154 195 83 374
158 398 167 23 9 42 22 42 28 0 7 -3 13 -7 14 -13 1 -39 6 -266 53 -120 25
-228 48 -240 50 -66 13 -186 38 -217 46 -19 5 -48 11 -65 14 -50 9 -86 16
-180 36 -49 11 -101 22 -115 24 -14 3 -101 21 -195 40 -186 39 -179 38 -187
31z"/>
<path d="M2406 2816 c-17 -27 -25 -13 106 -201 30 -44 111 -161 180 -260 68
-99 140 -202 159 -230 19 -27 86 -124 149 -215 63 -91 274 -397 470 -680 196
-283 387 -560 425 -615 38 -55 70 -102 72 -105 2 -2 28 20 58 50 46 46 56 62
60 100 3 25 7 52 10 60 2 8 7 35 10 60 3 25 12 81 20 125 7 44 16 100 20 125
3 25 10 68 15 95 5 28 12 68 15 90 3 23 8 52 10 65 3 14 8 43 11 65 2 22 18
117 34 210 15 94 31 186 34 205 22 145 77 478 80 493 3 12 -7 23 -34 37 -47
24 -103 89 -127 146 -21 51 -13 48 -218 88 -157 32 -251 51 -350 72 -42 9
-121 26 -145 30 -14 2 -56 11 -95 20 -65 14 -136 29 -195 40 -14 3 -59 12
-100 20 -41 9 -138 29 -215 44 -77 16 -205 43 -285 59 -80 17 -148 31 -151 31
-4 0 -14 -11 -23 -24z"/>
<path d="M1420 2684 c-162 -69 -330 -140 -372 -157 -43 -18 -78 -37 -78 -42 0
-16 360 -366 370 -360 4 3 35 49 69 102 34 54 113 177 176 273 169 259 168
256 149 286 -9 13 -17 24 -18 24 0 0 -134 -57 -296 -126z"/>
<path d="M2185 2642 c-43 -16 -193 -21 -237 -8 -25 7 -29 3 -85 -83 -32 -51
-89 -140 -128 -200 -38 -59 -86 -132 -104 -162 -19 -30 -59 -90 -88 -135 -29
-45 -53 -86 -53 -91 0 -10 424 -435 940 -943 113 -111 388 -383 611 -606 224
-222 412 -405 418 -406 6 -2 89 75 185 171 l175 174 -22 32 c-12 18 -50 73
-83 122 -34 48 -116 167 -182 263 -66 96 -135 195 -152 220 -38 54 -241 348
-351 510 -44 63 -93 135 -110 160 -18 25 -176 254 -352 510 -175 256 -324 470
-331 476 -8 8 -22 6 -51 -4z"/>
<path d="M4683 2343 c-9 -13 -40 -38 -68 -54 l-52 -29 -22 -133 c-11 -72 -23
-143 -25 -157 -6 -32 -37 -228 -41 -255 -2 -11 -26 -157 -53 -325 -28 -168
-53 -323 -56 -345 -4 -22 -11 -68 -17 -103 -12 -67 -8 -83 14 -61 419 410
1287 1276 1287 1284 0 6 -73 25 -162 43 -90 19 -172 35 -183 38 -11 2 -54 11
-95 19 -41 8 -86 18 -100 21 -14 3 -36 7 -50 10 -14 3 -88 18 -165 34 -210 43
-193 42 -212 13z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="17px" height="16px" viewBox="0 0 17 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41.2 (35397) - http://www.bohemiancoding.com/sketch -->
<title>Shape Copy</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="jamstack-landing" transform="translate(-1053.000000, -48.000000)" fill="#FFFFFF">
<g id="header" transform="translate(250.000000, 40.000000)">
<path d="M811.198938,8 C806.671816,8 803,11.6725529 803,16.2027524 C803,19.8271171 805.349142,22.9014191 808.607399,23.9861654 C809.017371,24.0610108 809.167011,23.8082793 809.167011,23.5909199 C809.167011,23.3961167 809.159837,22.8793756 809.15625,22.1950012 C806.875265,22.6907241 806.39406,21.0953885 806.39406,21.0953885 C806.021497,20.1485424 805.483921,19.8958109 805.483921,19.8958109 C804.739308,19.3877847 805.540292,19.3970122 805.540292,19.3970122 C806.363312,19.4549405 806.796345,20.2428682 806.796345,20.2428682 C807.528146,21.4952477 808.715529,21.1333238 809.182898,20.9241667 C809.257205,20.394097 809.469366,20.0321731 809.703563,19.8276298 C807.882772,19.6200106 805.968713,18.9171812 805.968713,15.7731601 C805.968713,14.8775781 806.288492,14.1455282 806.812744,13.5723967 C806.727675,13.3647775 806.446331,12.5312248 806.892176,11.401879 C806.892176,11.401879 807.58093,11.1814438 809.147025,12.2431212 C809.800931,12.061134 810.502497,11.9698841 811.199962,11.9668082 C811.896403,11.9698841 812.597456,12.061134 813.2529,12.2431212 C814.818482,11.1814438 815.505699,11.401879 815.505699,11.401879 C815.953081,12.5312248 815.671225,13.3652901 815.587181,13.5723967 C816.111945,14.1460408 816.430187,14.8780907 816.430187,15.7731601 C816.430187,18.9243581 814.513053,19.61796 812.686113,19.8204528 C812.980781,20.0747223 813.24265,20.5755715 813.24265,21.3404304 C813.24265,22.4369673 813.232401,23.3207587 813.232401,23.5909199 C813.232401,23.8098172 813.379991,24.0645993 813.796626,23.9851401 C817.051807,22.8983433 819.3989,19.8260919 819.3989,16.2027524 C819.3989,11.6725529 815.727596,8 811.198938,8 L811.198938,8 Z" id="Shape-Copy"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

14
website/src/img/heart.svg Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="13px" viewBox="0 0 14 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41.2 (35397) - http://www.bohemiancoding.com/sketch -->
<title>heart</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="jamstack-landing" transform="translate(-713.000000, -3752.000000)" fill="#FB6D77">
<g id="articles" transform="translate(0.000000, 2345.000000)">
<path d="M725.879238,1413.84495 C727.525503,1412.19869 727.286611,1409.22984 725.019692,1408.06452 C724.069878,1407.57625 722.919023,1407.55603 721.944531,1407.99308 C721.192974,1408.33013 720.67604,1408.8388 720.338716,1409.42467 C720.200205,1409.66532 719.853604,1409.66829 719.715278,1409.42754 C719.400776,1408.88009 718.929673,1408.39989 718.257251,1408.06377 C717.091367,1407.48106 715.657183,1407.56475 714.597246,1408.32326 C712.751148,1409.64445 712.595381,1412.22912 714.129854,1413.7635 C715.615991,1415.49762 717.211323,1417.11317 719.263379,1418.9961 C719.696074,1419.38965 720.358291,1419.39893 720.792471,1419 C722.423057,1417.51525 724.153467,1415.82056 725.879239,1413.84486 L725.879238,1413.84495 Z" id="heart"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

103
website/src/img/helix.svg Normal file
View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="298px" height="507px" viewBox="0 0 298 507" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 42 (36781) - http://www.bohemiancoding.com/sketch -->
<title>helix</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="2.56276788%" y1="49.6873118%" x2="97.4372321%" y2="49.6873118%" id="linearGradient-1">
<stop stop-color="#B2DE3F" offset="0%"></stop>
<stop stop-color="#FFFFFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="97.4372321%" y1="49.6873118%" x2="2.56276788%" y2="49.6873118%" id="linearGradient-2">
<stop stop-color="#B2DE3F" offset="0%"></stop>
<stop stop-color="#FFFFFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50%" y1="97.124544%" x2="50%" y2="2.25007972%" id="linearGradient-3">
<stop stop-color="#B2DE3F" offset="0%"></stop>
<stop stop-color="#CAE87C" offset="100%"></stop>
</linearGradient>
</defs>
<g id="landing" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="cms-landing-v6" transform="translate(-572.000000, -1465.000000)">
<g id="triptych" transform="translate(0.000000, 1205.000000)">
<g id="helix" transform="translate(572.000000, 260.000000)">
<g transform="translate(58.000000, 0.800049)">
<g id="bars" transform="translate(6.000000, 7.000000)" fill-rule="nonzero">
<path d="M28,1.5 L30,1.5 L30,0.5 L28,0.5 L28,1.5 Z M32,1.5 L34,1.5 L34,0.5 L32,0.5 L32,1.5 Z M36,1.5 L38,1.5 L38,0.5 L36,0.5 L36,1.5 Z M40,1.5 L42,1.5 L42,0.5 L40,0.5 L40,1.5 Z M44,1.5 L46,1.5 L46,0.5 L44,0.5 L44,1.5 Z M48,1.5 L50,1.5 L50,0.5 L48,0.5 L48,1.5 Z M52,1.5 L54,1.5 L54,0.5 L52,0.5 L52,1.5 Z M56,1.5 L58,1.5 L58,0.5 L56,0.5 L56,1.5 Z M60,1.5 L62,1.5 L62,0.5 L60,0.5 L60,1.5 Z M64,1.5 L66,1.5 L66,0.5 L64,0.5 L64,1.5 Z M68,1.5 L70,1.5 L70,0.5 L68,0.5 L68,1.5 Z M72,1.5 L74,1.5 L74,0.5 L72,0.5 L72,1.5 Z M76,1.5 L78,1.5 L78,0.5 L76,0.5 L76,1.5 Z M80,1.5 L82,1.5 L82,0.5 L80,0.5 L80,1.5 Z M84,1.5 L86,1.5 L86,0.5 L84,0.5 L84,1.5 Z M88,1.5 L90,1.5 L90,0.5 L88,0.5 L88,1.5 Z M92,1.5 L94,1.5 L94,0.5 L92,0.5 L92,1.5 Z M96,1.5 L98,1.5 L98,0.5 L96,0.5 L96,1.5 Z M100,1.5 L102,1.5 L102,0.5 L100,0.5 L100,1.5 Z M104,1.5 L106,1.5 L106,0.5 L104,0.5 L104,1.5 Z M108,1.5 L110,1.5 L110,0.5 L108,0.5 L108,1.5 Z M112,1.5 L114,1.5 L114,0.5 L112,0.5 L112,1.5 Z M116,1.5 L118,1.5 L118,0.5 L116,0.5 L116,1.5 Z M120,1.5 L122,1.5 L122,0.5 L120,0.5 L120,1.5 Z M124,1.5 L126,1.5 L126,0.5 L124,0.5 L124,1.5 Z M128,1.5 L130,1.5 L130,0.5 L128,0.5 L128,1.5 Z M132,1.5 L134,1.5 L134,0.5 L132,0.5 L132,1.5 Z M136,1.5 L138,1.5 L138,0.5 L136,0.5 L136,1.5 Z" id="Path-9" fill="url(#linearGradient-1)"></path>
<path d="M0.5,36.5 L2.5,36.5 L2.5,35.5 L0.5,35.5 L0.5,36.5 Z M4.5,36.5 L6.5,36.5 L6.5,35.5 L4.5,35.5 L4.5,36.5 Z M8.5,36.5 L10.5,36.5 L10.5,35.5 L8.5,35.5 L8.5,36.5 Z M12.5,36.5 L14.5,36.5 L14.5,35.5 L12.5,35.5 L12.5,36.5 Z M16.5,36.5 L18.5,36.5 L18.5,35.5 L16.5,35.5 L16.5,36.5 Z M20.5,36.5 L22.5,36.5 L22.5,35.5 L20.5,35.5 L20.5,36.5 Z M24.5,36.5 L26.5,36.5 L26.5,35.5 L24.5,35.5 L24.5,36.5 Z M28.5,36.5 L30.5,36.5 L30.5,35.5 L28.5,35.5 L28.5,36.5 Z M32.5,36.5 L34.5,36.5 L34.5,35.5 L32.5,35.5 L32.5,36.5 Z M36.5,36.5 L38.5,36.5 L38.5,35.5 L36.5,35.5 L36.5,36.5 Z M40.5,36.5 L42.5,36.5 L42.5,35.5 L40.5,35.5 L40.5,36.5 Z M44.5,36.5 L46.5,36.5 L46.5,35.5 L44.5,35.5 L44.5,36.5 Z M48.5,36.5 L50.5,36.5 L50.5,35.5 L48.5,35.5 L48.5,36.5 Z M52.5,36.5 L54.5,36.5 L54.5,35.5 L52.5,35.5 L52.5,36.5 Z M56.5,36.5 L58.5,36.5 L58.5,35.5 L56.5,35.5 L56.5,36.5 Z M60.5,36.5 L62.5,36.5 L62.5,35.5 L60.5,35.5 L60.5,36.5 Z M64.5,36.5 L66.5,36.5 L66.5,35.5 L64.5,35.5 L64.5,36.5 Z M68.5,36.5 L70.5,36.5 L70.5,35.5 L68.5,35.5 L68.5,36.5 Z M72.5,36.5 L74.5,36.5 L74.5,35.5 L72.5,35.5 L72.5,36.5 Z M76.5,36.5 L78.5,36.5 L78.5,35.5 L76.5,35.5 L76.5,36.5 Z M80.5,36.5 L82.5,36.5 L82.5,35.5 L80.5,35.5 L80.5,36.5 Z M84.5,36.5 L86.5,36.5 L86.5,35.5 L84.5,35.5 L84.5,36.5 Z M88.5,36.5 L90.5,36.5 L90.5,35.5 L88.5,35.5 L88.5,36.5 Z M92.5,36.5 L94.5,36.5 L94.5,35.5 L92.5,35.5 L92.5,36.5 Z M96.5,36.5 L98.5,36.5 L98.5,35.5 L96.5,35.5 L96.5,36.5 Z M100.5,36.5 L102.5,36.5 L102.5,35.5 L100.5,35.5 L100.5,36.5 Z M104.5,36.5 L106.5,36.5 L106.5,35.5 L104.5,35.5 L104.5,36.5 Z M108.5,36.5 L110.5,36.5 L110.5,35.5 L108.5,35.5 L108.5,36.5 Z M112.5,36.5 L114.5,36.5 L114.5,35.5 L112.5,35.5 L112.5,36.5 Z M116.5,36.5 L118.5,36.5 L118.5,35.5 L116.5,35.5 L116.5,36.5 Z M120.5,36.5 L122.5,36.5 L122.5,35.5 L120.5,35.5 L120.5,36.5 Z M124.5,36.5 L126.5,36.5 L126.5,35.5 L124.5,35.5 L124.5,36.5 Z M128.5,36.5 L130.5,36.5 L130.5,35.5 L128.5,35.5 L128.5,36.5 Z M132.5,36.5 L134.5,36.5 L134.5,35.5 L132.5,35.5 L132.5,36.5 Z M136.5,36.5 L138.5,36.5 L138.5,35.5 L136.5,35.5 L136.5,36.5 Z M140.5,36.5 L142.5,36.5 L142.5,35.5 L140.5,35.5 L140.5,36.5 Z M144.5,36.5 L146.5,36.5 L146.5,35.5 L144.5,35.5 L144.5,36.5 Z M148.5,36.5 L150.5,36.5 L150.5,35.5 L148.5,35.5 L148.5,36.5 Z M152.5,36.5 L154.5,36.5 L154.5,35.5 L152.5,35.5 L152.5,36.5 Z M156.5,36.5 L158.5,36.5 L158.5,35.5 L156.5,35.5 L156.5,36.5 Z M160.5,36.5 L162.5,36.5 L162.5,35.5 L160.5,35.5 L160.5,36.5 Z M164.5,36.5 L166.5,36.5 L166.5,35.5 L164.5,35.5 L164.5,36.5 Z" id="Path-9-Copy" fill="url(#linearGradient-2)"></path>
<path d="M28,71.5 L30,71.5 L30,70.5 L28,70.5 L28,71.5 Z M32,71.5 L34,71.5 L34,70.5 L32,70.5 L32,71.5 Z M36,71.5 L38,71.5 L38,70.5 L36,70.5 L36,71.5 Z M40,71.5 L42,71.5 L42,70.5 L40,70.5 L40,71.5 Z M44,71.5 L46,71.5 L46,70.5 L44,70.5 L44,71.5 Z M48,71.5 L50,71.5 L50,70.5 L48,70.5 L48,71.5 Z M52,71.5 L54,71.5 L54,70.5 L52,70.5 L52,71.5 Z M56,71.5 L58,71.5 L58,70.5 L56,70.5 L56,71.5 Z M60,71.5 L62,71.5 L62,70.5 L60,70.5 L60,71.5 Z M64,71.5 L66,71.5 L66,70.5 L64,70.5 L64,71.5 Z M68,71.5 L70,71.5 L70,70.5 L68,70.5 L68,71.5 Z M72,71.5 L74,71.5 L74,70.5 L72,70.5 L72,71.5 Z M76,71.5 L78,71.5 L78,70.5 L76,70.5 L76,71.5 Z M80,71.5 L82,71.5 L82,70.5 L80,70.5 L80,71.5 Z M84,71.5 L86,71.5 L86,70.5 L84,70.5 L84,71.5 Z M88,71.5 L90,71.5 L90,70.5 L88,70.5 L88,71.5 Z M92,71.5 L94,71.5 L94,70.5 L92,70.5 L92,71.5 Z M96,71.5 L98,71.5 L98,70.5 L96,70.5 L96,71.5 Z M100,71.5 L102,71.5 L102,70.5 L100,70.5 L100,71.5 Z M104,71.5 L106,71.5 L106,70.5 L104,70.5 L104,71.5 Z M108,71.5 L110,71.5 L110,70.5 L108,70.5 L108,71.5 Z M112,71.5 L114,71.5 L114,70.5 L112,70.5 L112,71.5 Z M116,71.5 L118,71.5 L118,70.5 L116,70.5 L116,71.5 Z M120,71.5 L122,71.5 L122,70.5 L120,70.5 L120,71.5 Z M124,71.5 L126,71.5 L126,70.5 L124,70.5 L124,71.5 Z M128,71.5 L130,71.5 L130,70.5 L128,70.5 L128,71.5 Z M132,71.5 L134,71.5 L134,70.5 L132,70.5 L132,71.5 Z M136,71.5 L138,71.5 L138,70.5 L136,70.5 L136,71.5 Z" id="Path-9-Copy-2" fill="#EAECEC"></path>
<path d="M57.5,106.5 L59.5,106.5 L59.5,105.5 L57.5,105.5 L57.5,106.5 Z M61.5,106.5 L63.5,106.5 L63.5,105.5 L61.5,105.5 L61.5,106.5 Z M65.5,106.5 L67.5,106.5 L67.5,105.5 L65.5,105.5 L65.5,106.5 Z M69.5,106.5 L71.5,106.5 L71.5,105.5 L69.5,105.5 L69.5,106.5 Z M73.5,106.5 L75.5,106.5 L75.5,105.5 L73.5,105.5 L73.5,106.5 Z M77.5,106.5 L79.5,106.5 L79.5,105.5 L77.5,105.5 L77.5,106.5 Z M81.5,106.5 L83.5,106.5 L83.5,105.5 L81.5,105.5 L81.5,106.5 Z M85.5,106.5 L87.5,106.5 L87.5,105.5 L85.5,105.5 L85.5,106.5 Z M89.5,106.5 L91.5,106.5 L91.5,105.5 L89.5,105.5 L89.5,106.5 Z M93.5,106.5 L95.5,106.5 L95.5,105.5 L93.5,105.5 L93.5,106.5 Z M97.5,106.5 L99.5,106.5 L99.5,105.5 L97.5,105.5 L97.5,106.5 Z M101.5,106.5 L103.5,106.5 L103.5,105.5 L101.5,105.5 L101.5,106.5 Z M105.5,106.5 L107.5,106.5 L107.5,105.5 L105.5,105.5 L105.5,106.5 Z M109.5,106.5 L111.5,106.5 L111.5,105.5 L109.5,105.5 L109.5,106.5 Z" id="Path-9-Copy-3" fill="#EAECEC"></path>
</g>
<g id="left" transform="translate(82.000000, 0.000000)">
<ellipse id="Oval-4" fill="#EAEBEB" cx="63" cy="8" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-3" fill="url(#linearGradient-3)" cx="90" cy="43.1999512" rx="8" ry="8"></ellipse>
<circle id="Oval-4-Copy-5" fill="#EAEBEB" cx="63" cy="78" r="8"></circle>
<ellipse id="Oval-4-Copy-4" fill="#EAEBEB" cx="35" cy="113" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-6" fill="#EAEBEB" cx="8" cy="148" rx="8" ry="8"></ellipse>
</g>
<g id="right" transform="translate(35.500000, 60.500000) scale(-1, 1) translate(-35.500000, -60.500000) ">
<ellipse id="Oval-4-Copy" fill="url(#linearGradient-3)" cx="36" cy="8" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-10" fill="#EAEBEB" cx="63" cy="43.1999512" rx="8" ry="8"></ellipse>
<circle id="Oval-4-Copy-9" fill="#EAEBEB" cx="36" cy="78" r="8"></circle>
<ellipse id="Oval-4-Copy-8" fill="#EAEBEB" cx="8" cy="113" rx="8" ry="8"></ellipse>
</g>
</g>
<g id="helix-copy" transform="translate(58.000000, 175.800049)">
<g id="bars" transform="translate(6.000000, 42.000000)" fill-rule="nonzero">
<path d="M28,1.5 L30,1.5 L30,0.5 L28,0.5 L28,1.5 Z M32,1.5 L34,1.5 L34,0.5 L32,0.5 L32,1.5 Z M36,1.5 L38,1.5 L38,0.5 L36,0.5 L36,1.5 Z M40,1.5 L42,1.5 L42,0.5 L40,0.5 L40,1.5 Z M44,1.5 L46,1.5 L46,0.5 L44,0.5 L44,1.5 Z M48,1.5 L50,1.5 L50,0.5 L48,0.5 L48,1.5 Z M52,1.5 L54,1.5 L54,0.5 L52,0.5 L52,1.5 Z M56,1.5 L58,1.5 L58,0.5 L56,0.5 L56,1.5 Z M60,1.5 L62,1.5 L62,0.5 L60,0.5 L60,1.5 Z M64,1.5 L66,1.5 L66,0.5 L64,0.5 L64,1.5 Z M68,1.5 L70,1.5 L70,0.5 L68,0.5 L68,1.5 Z M72,1.5 L74,1.5 L74,0.5 L72,0.5 L72,1.5 Z M76,1.5 L78,1.5 L78,0.5 L76,0.5 L76,1.5 Z M80,1.5 L82,1.5 L82,0.5 L80,0.5 L80,1.5 Z M84,1.5 L86,1.5 L86,0.5 L84,0.5 L84,1.5 Z M88,1.5 L90,1.5 L90,0.5 L88,0.5 L88,1.5 Z M92,1.5 L94,1.5 L94,0.5 L92,0.5 L92,1.5 Z M96,1.5 L98,1.5 L98,0.5 L96,0.5 L96,1.5 Z M100,1.5 L102,1.5 L102,0.5 L100,0.5 L100,1.5 Z M104,1.5 L106,1.5 L106,0.5 L104,0.5 L104,1.5 Z M108,1.5 L110,1.5 L110,0.5 L108,0.5 L108,1.5 Z M112,1.5 L114,1.5 L114,0.5 L112,0.5 L112,1.5 Z M116,1.5 L118,1.5 L118,0.5 L116,0.5 L116,1.5 Z M120,1.5 L122,1.5 L122,0.5 L120,0.5 L120,1.5 Z M124,1.5 L126,1.5 L126,0.5 L124,0.5 L124,1.5 Z M128,1.5 L130,1.5 L130,0.5 L128,0.5 L128,1.5 Z M132,1.5 L134,1.5 L134,0.5 L132,0.5 L132,1.5 Z M136,1.5 L138,1.5 L138,0.5 L136,0.5 L136,1.5 Z" id="Path-9" fill="url(#linearGradient-2)"></path>
<path d="M0.5,36.5 L2.5,36.5 L2.5,35.5 L0.5,35.5 L0.5,36.5 Z M4.5,36.5 L6.5,36.5 L6.5,35.5 L4.5,35.5 L4.5,36.5 Z M8.5,36.5 L10.5,36.5 L10.5,35.5 L8.5,35.5 L8.5,36.5 Z M12.5,36.5 L14.5,36.5 L14.5,35.5 L12.5,35.5 L12.5,36.5 Z M16.5,36.5 L18.5,36.5 L18.5,35.5 L16.5,35.5 L16.5,36.5 Z M20.5,36.5 L22.5,36.5 L22.5,35.5 L20.5,35.5 L20.5,36.5 Z M24.5,36.5 L26.5,36.5 L26.5,35.5 L24.5,35.5 L24.5,36.5 Z M28.5,36.5 L30.5,36.5 L30.5,35.5 L28.5,35.5 L28.5,36.5 Z M32.5,36.5 L34.5,36.5 L34.5,35.5 L32.5,35.5 L32.5,36.5 Z M36.5,36.5 L38.5,36.5 L38.5,35.5 L36.5,35.5 L36.5,36.5 Z M40.5,36.5 L42.5,36.5 L42.5,35.5 L40.5,35.5 L40.5,36.5 Z M44.5,36.5 L46.5,36.5 L46.5,35.5 L44.5,35.5 L44.5,36.5 Z M48.5,36.5 L50.5,36.5 L50.5,35.5 L48.5,35.5 L48.5,36.5 Z M52.5,36.5 L54.5,36.5 L54.5,35.5 L52.5,35.5 L52.5,36.5 Z M56.5,36.5 L58.5,36.5 L58.5,35.5 L56.5,35.5 L56.5,36.5 Z M60.5,36.5 L62.5,36.5 L62.5,35.5 L60.5,35.5 L60.5,36.5 Z M64.5,36.5 L66.5,36.5 L66.5,35.5 L64.5,35.5 L64.5,36.5 Z M68.5,36.5 L70.5,36.5 L70.5,35.5 L68.5,35.5 L68.5,36.5 Z M72.5,36.5 L74.5,36.5 L74.5,35.5 L72.5,35.5 L72.5,36.5 Z M76.5,36.5 L78.5,36.5 L78.5,35.5 L76.5,35.5 L76.5,36.5 Z M80.5,36.5 L82.5,36.5 L82.5,35.5 L80.5,35.5 L80.5,36.5 Z M84.5,36.5 L86.5,36.5 L86.5,35.5 L84.5,35.5 L84.5,36.5 Z M88.5,36.5 L90.5,36.5 L90.5,35.5 L88.5,35.5 L88.5,36.5 Z M92.5,36.5 L94.5,36.5 L94.5,35.5 L92.5,35.5 L92.5,36.5 Z M96.5,36.5 L98.5,36.5 L98.5,35.5 L96.5,35.5 L96.5,36.5 Z M100.5,36.5 L102.5,36.5 L102.5,35.5 L100.5,35.5 L100.5,36.5 Z M104.5,36.5 L106.5,36.5 L106.5,35.5 L104.5,35.5 L104.5,36.5 Z M108.5,36.5 L110.5,36.5 L110.5,35.5 L108.5,35.5 L108.5,36.5 Z M112.5,36.5 L114.5,36.5 L114.5,35.5 L112.5,35.5 L112.5,36.5 Z M116.5,36.5 L118.5,36.5 L118.5,35.5 L116.5,35.5 L116.5,36.5 Z M120.5,36.5 L122.5,36.5 L122.5,35.5 L120.5,35.5 L120.5,36.5 Z M124.5,36.5 L126.5,36.5 L126.5,35.5 L124.5,35.5 L124.5,36.5 Z M128.5,36.5 L130.5,36.5 L130.5,35.5 L128.5,35.5 L128.5,36.5 Z M132.5,36.5 L134.5,36.5 L134.5,35.5 L132.5,35.5 L132.5,36.5 Z M136.5,36.5 L138.5,36.5 L138.5,35.5 L136.5,35.5 L136.5,36.5 Z M140.5,36.5 L142.5,36.5 L142.5,35.5 L140.5,35.5 L140.5,36.5 Z M144.5,36.5 L146.5,36.5 L146.5,35.5 L144.5,35.5 L144.5,36.5 Z M148.5,36.5 L150.5,36.5 L150.5,35.5 L148.5,35.5 L148.5,36.5 Z M152.5,36.5 L154.5,36.5 L154.5,35.5 L152.5,35.5 L152.5,36.5 Z M156.5,36.5 L158.5,36.5 L158.5,35.5 L156.5,35.5 L156.5,36.5 Z M160.5,36.5 L162.5,36.5 L162.5,35.5 L160.5,35.5 L160.5,36.5 Z M164.5,36.5 L166.5,36.5 L166.5,35.5 L164.5,35.5 L164.5,36.5 Z" id="Path-9-Copy" fill="url(#linearGradient-1)"></path>
<path d="M28,71.5 L30,71.5 L30,70.5 L28,70.5 L28,71.5 Z M32,71.5 L34,71.5 L34,70.5 L32,70.5 L32,71.5 Z M36,71.5 L38,71.5 L38,70.5 L36,70.5 L36,71.5 Z M40,71.5 L42,71.5 L42,70.5 L40,70.5 L40,71.5 Z M44,71.5 L46,71.5 L46,70.5 L44,70.5 L44,71.5 Z M48,71.5 L50,71.5 L50,70.5 L48,70.5 L48,71.5 Z M52,71.5 L54,71.5 L54,70.5 L52,70.5 L52,71.5 Z M56,71.5 L58,71.5 L58,70.5 L56,70.5 L56,71.5 Z M60,71.5 L62,71.5 L62,70.5 L60,70.5 L60,71.5 Z M64,71.5 L66,71.5 L66,70.5 L64,70.5 L64,71.5 Z M68,71.5 L70,71.5 L70,70.5 L68,70.5 L68,71.5 Z M72,71.5 L74,71.5 L74,70.5 L72,70.5 L72,71.5 Z M76,71.5 L78,71.5 L78,70.5 L76,70.5 L76,71.5 Z M80,71.5 L82,71.5 L82,70.5 L80,70.5 L80,71.5 Z M84,71.5 L86,71.5 L86,70.5 L84,70.5 L84,71.5 Z M88,71.5 L90,71.5 L90,70.5 L88,70.5 L88,71.5 Z M92,71.5 L94,71.5 L94,70.5 L92,70.5 L92,71.5 Z M96,71.5 L98,71.5 L98,70.5 L96,70.5 L96,71.5 Z M100,71.5 L102,71.5 L102,70.5 L100,70.5 L100,71.5 Z M104,71.5 L106,71.5 L106,70.5 L104,70.5 L104,71.5 Z M108,71.5 L110,71.5 L110,70.5 L108,70.5 L108,71.5 Z M112,71.5 L114,71.5 L114,70.5 L112,70.5 L112,71.5 Z M116,71.5 L118,71.5 L118,70.5 L116,70.5 L116,71.5 Z M120,71.5 L122,71.5 L122,70.5 L120,70.5 L120,71.5 Z M124,71.5 L126,71.5 L126,70.5 L124,70.5 L124,71.5 Z M128,71.5 L130,71.5 L130,70.5 L128,70.5 L128,71.5 Z M132,71.5 L134,71.5 L134,70.5 L132,70.5 L132,71.5 Z M136,71.5 L138,71.5 L138,70.5 L136,70.5 L136,71.5 Z" id="Path-9-Copy-2" fill="#EAECEC"></path>
<path d="M57.5,106.5 L59.5,106.5 L59.5,105.5 L57.5,105.5 L57.5,106.5 Z M61.5,106.5 L63.5,106.5 L63.5,105.5 L61.5,105.5 L61.5,106.5 Z M65.5,106.5 L67.5,106.5 L67.5,105.5 L65.5,105.5 L65.5,106.5 Z M69.5,106.5 L71.5,106.5 L71.5,105.5 L69.5,105.5 L69.5,106.5 Z M73.5,106.5 L75.5,106.5 L75.5,105.5 L73.5,105.5 L73.5,106.5 Z M77.5,106.5 L79.5,106.5 L79.5,105.5 L77.5,105.5 L77.5,106.5 Z M81.5,106.5 L83.5,106.5 L83.5,105.5 L81.5,105.5 L81.5,106.5 Z M85.5,106.5 L87.5,106.5 L87.5,105.5 L85.5,105.5 L85.5,106.5 Z M89.5,106.5 L91.5,106.5 L91.5,105.5 L89.5,105.5 L89.5,106.5 Z M93.5,106.5 L95.5,106.5 L95.5,105.5 L93.5,105.5 L93.5,106.5 Z M97.5,106.5 L99.5,106.5 L99.5,105.5 L97.5,105.5 L97.5,106.5 Z M101.5,106.5 L103.5,106.5 L103.5,105.5 L101.5,105.5 L101.5,106.5 Z M105.5,106.5 L107.5,106.5 L107.5,105.5 L105.5,105.5 L105.5,106.5 Z M109.5,106.5 L111.5,106.5 L111.5,105.5 L109.5,105.5 L109.5,106.5 Z" id="Path-9-Copy-3" fill="#EAECEC"></path>
</g>
<g id="left" transform="translate(82.000000, 35.000000)">
<ellipse id="Oval-4" fill="url(#linearGradient-3)" cx="63" cy="8" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-3" fill="#EAEBEB" cx="90" cy="43.1999512" rx="8" ry="8"></ellipse>
<circle id="Oval-4-Copy-5" fill="#EAEBEB" cx="63" cy="78" r="8"></circle>
<ellipse id="Oval-4-Copy-4" fill="#EAEBEB" cx="35" cy="113" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-6" fill="#EAEBEB" cx="8" cy="148" rx="8" ry="8"></ellipse>
</g>
<g id="right" transform="translate(35.500000, 95.500000) scale(-1, 1) translate(-35.500000, -95.500000) translate(0.000000, 35.000000)">
<ellipse id="Oval-4-Copy" fill="#EAEBEB" cx="36" cy="8" rx="8" ry="8"></ellipse>
<circle id="Oval-4-Copy-10" fill="url(#linearGradient-3)" cx="63" cy="43.1999512" r="8"></circle>
<ellipse id="Oval-4-Copy-9" fill="#EAEBEB" cx="36" cy="78" rx="8" ry="8"></ellipse>
<circle id="Oval-4-Copy-8" fill="#EAEBEB" cx="8" cy="113" r="8"></circle>
</g>
<g id="Group-8" transform="translate(55.000000, 0.000000)">
<path d="M8.5,8.5 L10.5,8.5 L10.5,7.5 L8.5,7.5 L8.5,8.5 Z M12.5,8.5 L14.5,8.5 L14.5,7.5 L12.5,7.5 L12.5,8.5 Z M16.5,8.5 L18.5,8.5 L18.5,7.5 L16.5,7.5 L16.5,8.5 Z M20.5,8.5 L22.5,8.5 L22.5,7.5 L20.5,7.5 L20.5,8.5 Z M24.5,8.5 L26.5,8.5 L26.5,7.5 L24.5,7.5 L24.5,8.5 Z M28.5,8.5 L30.5,8.5 L30.5,7.5 L28.5,7.5 L28.5,8.5 Z M32.5,8.5 L34.5,8.5 L34.5,7.5 L32.5,7.5 L32.5,8.5 Z M36.5,8.5 L38.5,8.5 L38.5,7.5 L36.5,7.5 L36.5,8.5 Z M40.5,8.5 L42.5,8.5 L42.5,7.5 L40.5,7.5 L40.5,8.5 Z M44.5,8.5 L46.5,8.5 L46.5,7.5 L44.5,7.5 L44.5,8.5 Z M48.5,8.5 L50.5,8.5 L50.5,7.5 L48.5,7.5 L48.5,8.5 Z M52.5,8.5 L54.5,8.5 L54.5,7.5 L52.5,7.5 L52.5,8.5 Z M56.5,8.5 L58.5,8.5 L58.5,7.5 L56.5,7.5 L56.5,8.5 Z M60.5,8.5 L62.5,8.5 L62.5,7.5 L60.5,7.5 L60.5,8.5 Z" id="Path-9-Copy-3" fill="#EAECEC" fill-rule="nonzero"></path>
<circle id="Oval-4-Copy-4" fill="#EAEBEB" cx="62" cy="8" r="8"></circle>
<circle id="Oval-4-Copy-8" fill="#EAEBEB" cx="8" cy="8" r="8"></circle>
</g>
</g>
<g id="helix-copy-2" transform="translate(58.000000, 385.800049)">
<g id="bars" transform="translate(6.000000, 42.000000)" fill-rule="nonzero">
<path d="M28,1.5 L30,1.5 L30,0.5 L28,0.5 L28,1.5 Z M32,1.5 L34,1.5 L34,0.5 L32,0.5 L32,1.5 Z M36,1.5 L38,1.5 L38,0.5 L36,0.5 L36,1.5 Z M40,1.5 L42,1.5 L42,0.5 L40,0.5 L40,1.5 Z M44,1.5 L46,1.5 L46,0.5 L44,0.5 L44,1.5 Z M48,1.5 L50,1.5 L50,0.5 L48,0.5 L48,1.5 Z M52,1.5 L54,1.5 L54,0.5 L52,0.5 L52,1.5 Z M56,1.5 L58,1.5 L58,0.5 L56,0.5 L56,1.5 Z M60,1.5 L62,1.5 L62,0.5 L60,0.5 L60,1.5 Z M64,1.5 L66,1.5 L66,0.5 L64,0.5 L64,1.5 Z M68,1.5 L70,1.5 L70,0.5 L68,0.5 L68,1.5 Z M72,1.5 L74,1.5 L74,0.5 L72,0.5 L72,1.5 Z M76,1.5 L78,1.5 L78,0.5 L76,0.5 L76,1.5 Z M80,1.5 L82,1.5 L82,0.5 L80,0.5 L80,1.5 Z M84,1.5 L86,1.5 L86,0.5 L84,0.5 L84,1.5 Z M88,1.5 L90,1.5 L90,0.5 L88,0.5 L88,1.5 Z M92,1.5 L94,1.5 L94,0.5 L92,0.5 L92,1.5 Z M96,1.5 L98,1.5 L98,0.5 L96,0.5 L96,1.5 Z M100,1.5 L102,1.5 L102,0.5 L100,0.5 L100,1.5 Z M104,1.5 L106,1.5 L106,0.5 L104,0.5 L104,1.5 Z M108,1.5 L110,1.5 L110,0.5 L108,0.5 L108,1.5 Z M112,1.5 L114,1.5 L114,0.5 L112,0.5 L112,1.5 Z M116,1.5 L118,1.5 L118,0.5 L116,0.5 L116,1.5 Z M120,1.5 L122,1.5 L122,0.5 L120,0.5 L120,1.5 Z M124,1.5 L126,1.5 L126,0.5 L124,0.5 L124,1.5 Z M128,1.5 L130,1.5 L130,0.5 L128,0.5 L128,1.5 Z M132,1.5 L134,1.5 L134,0.5 L132,0.5 L132,1.5 Z M136,1.5 L138,1.5 L138,0.5 L136,0.5 L136,1.5 Z" id="Path-9" fill="#EAECEC"></path>
<path d="M0.5,36.5 L2.5,36.5 L2.5,35.5 L0.5,35.5 L0.5,36.5 Z M4.5,36.5 L6.5,36.5 L6.5,35.5 L4.5,35.5 L4.5,36.5 Z M8.5,36.5 L10.5,36.5 L10.5,35.5 L8.5,35.5 L8.5,36.5 Z M12.5,36.5 L14.5,36.5 L14.5,35.5 L12.5,35.5 L12.5,36.5 Z M16.5,36.5 L18.5,36.5 L18.5,35.5 L16.5,35.5 L16.5,36.5 Z M20.5,36.5 L22.5,36.5 L22.5,35.5 L20.5,35.5 L20.5,36.5 Z M24.5,36.5 L26.5,36.5 L26.5,35.5 L24.5,35.5 L24.5,36.5 Z M28.5,36.5 L30.5,36.5 L30.5,35.5 L28.5,35.5 L28.5,36.5 Z M32.5,36.5 L34.5,36.5 L34.5,35.5 L32.5,35.5 L32.5,36.5 Z M36.5,36.5 L38.5,36.5 L38.5,35.5 L36.5,35.5 L36.5,36.5 Z M40.5,36.5 L42.5,36.5 L42.5,35.5 L40.5,35.5 L40.5,36.5 Z M44.5,36.5 L46.5,36.5 L46.5,35.5 L44.5,35.5 L44.5,36.5 Z M48.5,36.5 L50.5,36.5 L50.5,35.5 L48.5,35.5 L48.5,36.5 Z M52.5,36.5 L54.5,36.5 L54.5,35.5 L52.5,35.5 L52.5,36.5 Z M56.5,36.5 L58.5,36.5 L58.5,35.5 L56.5,35.5 L56.5,36.5 Z M60.5,36.5 L62.5,36.5 L62.5,35.5 L60.5,35.5 L60.5,36.5 Z M64.5,36.5 L66.5,36.5 L66.5,35.5 L64.5,35.5 L64.5,36.5 Z M68.5,36.5 L70.5,36.5 L70.5,35.5 L68.5,35.5 L68.5,36.5 Z M72.5,36.5 L74.5,36.5 L74.5,35.5 L72.5,35.5 L72.5,36.5 Z M76.5,36.5 L78.5,36.5 L78.5,35.5 L76.5,35.5 L76.5,36.5 Z M80.5,36.5 L82.5,36.5 L82.5,35.5 L80.5,35.5 L80.5,36.5 Z M84.5,36.5 L86.5,36.5 L86.5,35.5 L84.5,35.5 L84.5,36.5 Z M88.5,36.5 L90.5,36.5 L90.5,35.5 L88.5,35.5 L88.5,36.5 Z M92.5,36.5 L94.5,36.5 L94.5,35.5 L92.5,35.5 L92.5,36.5 Z M96.5,36.5 L98.5,36.5 L98.5,35.5 L96.5,35.5 L96.5,36.5 Z M100.5,36.5 L102.5,36.5 L102.5,35.5 L100.5,35.5 L100.5,36.5 Z M104.5,36.5 L106.5,36.5 L106.5,35.5 L104.5,35.5 L104.5,36.5 Z M108.5,36.5 L110.5,36.5 L110.5,35.5 L108.5,35.5 L108.5,36.5 Z M112.5,36.5 L114.5,36.5 L114.5,35.5 L112.5,35.5 L112.5,36.5 Z M116.5,36.5 L118.5,36.5 L118.5,35.5 L116.5,35.5 L116.5,36.5 Z M120.5,36.5 L122.5,36.5 L122.5,35.5 L120.5,35.5 L120.5,36.5 Z M124.5,36.5 L126.5,36.5 L126.5,35.5 L124.5,35.5 L124.5,36.5 Z M128.5,36.5 L130.5,36.5 L130.5,35.5 L128.5,35.5 L128.5,36.5 Z M132.5,36.5 L134.5,36.5 L134.5,35.5 L132.5,35.5 L132.5,36.5 Z M136.5,36.5 L138.5,36.5 L138.5,35.5 L136.5,35.5 L136.5,36.5 Z M140.5,36.5 L142.5,36.5 L142.5,35.5 L140.5,35.5 L140.5,36.5 Z M144.5,36.5 L146.5,36.5 L146.5,35.5 L144.5,35.5 L144.5,36.5 Z M148.5,36.5 L150.5,36.5 L150.5,35.5 L148.5,35.5 L148.5,36.5 Z M152.5,36.5 L154.5,36.5 L154.5,35.5 L152.5,35.5 L152.5,36.5 Z M156.5,36.5 L158.5,36.5 L158.5,35.5 L156.5,35.5 L156.5,36.5 Z M160.5,36.5 L162.5,36.5 L162.5,35.5 L160.5,35.5 L160.5,36.5 Z M164.5,36.5 L166.5,36.5 L166.5,35.5 L164.5,35.5 L164.5,36.5 Z" id="Path-9-Copy" fill="url(#linearGradient-2)"></path>
<path d="M28,71.5 L30,71.5 L30,70.5 L28,70.5 L28,71.5 Z M32,71.5 L34,71.5 L34,70.5 L32,70.5 L32,71.5 Z M36,71.5 L38,71.5 L38,70.5 L36,70.5 L36,71.5 Z M40,71.5 L42,71.5 L42,70.5 L40,70.5 L40,71.5 Z M44,71.5 L46,71.5 L46,70.5 L44,70.5 L44,71.5 Z M48,71.5 L50,71.5 L50,70.5 L48,70.5 L48,71.5 Z M52,71.5 L54,71.5 L54,70.5 L52,70.5 L52,71.5 Z M56,71.5 L58,71.5 L58,70.5 L56,70.5 L56,71.5 Z M60,71.5 L62,71.5 L62,70.5 L60,70.5 L60,71.5 Z M64,71.5 L66,71.5 L66,70.5 L64,70.5 L64,71.5 Z M68,71.5 L70,71.5 L70,70.5 L68,70.5 L68,71.5 Z M72,71.5 L74,71.5 L74,70.5 L72,70.5 L72,71.5 Z M76,71.5 L78,71.5 L78,70.5 L76,70.5 L76,71.5 Z M80,71.5 L82,71.5 L82,70.5 L80,70.5 L80,71.5 Z M84,71.5 L86,71.5 L86,70.5 L84,70.5 L84,71.5 Z M88,71.5 L90,71.5 L90,70.5 L88,70.5 L88,71.5 Z M92,71.5 L94,71.5 L94,70.5 L92,70.5 L92,71.5 Z M96,71.5 L98,71.5 L98,70.5 L96,70.5 L96,71.5 Z M100,71.5 L102,71.5 L102,70.5 L100,70.5 L100,71.5 Z M104,71.5 L106,71.5 L106,70.5 L104,70.5 L104,71.5 Z M108,71.5 L110,71.5 L110,70.5 L108,70.5 L108,71.5 Z M112,71.5 L114,71.5 L114,70.5 L112,70.5 L112,71.5 Z M116,71.5 L118,71.5 L118,70.5 L116,70.5 L116,71.5 Z M120,71.5 L122,71.5 L122,70.5 L120,70.5 L120,71.5 Z M124,71.5 L126,71.5 L126,70.5 L124,70.5 L124,71.5 Z M128,71.5 L130,71.5 L130,70.5 L128,70.5 L128,71.5 Z M132,71.5 L134,71.5 L134,70.5 L132,70.5 L132,71.5 Z M136,71.5 L138,71.5 L138,70.5 L136,70.5 L136,71.5 Z" id="Path-9-Copy-2" fill="#EAECEC"></path>
</g>
<g id="left" transform="translate(137.000000, 35.000000)">
<ellipse id="Oval-4" fill="#EAEBEB" cx="8" cy="8" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-3" fill="url(#linearGradient-3)" cx="35" cy="43.1999512" rx="8" ry="8"></ellipse>
<circle id="Oval-4-Copy-5" fill="#EAEBEB" cx="8" cy="78" r="8"></circle>
</g>
<g id="right" transform="translate(21.500000, 78.000000) scale(-1, 1) translate(-21.500000, -78.000000) translate(0.000000, 35.000000)" fill="#EAEBEB">
<ellipse id="Oval-4-Copy" cx="8" cy="8" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-10" cx="35" cy="43.1999512" rx="8" ry="8"></ellipse>
<circle id="Oval-4-Copy-9" cx="8" cy="78" r="8"></circle>
</g>
<g id="Group-8" transform="translate(55.000000, 0.000000)">
<path d="M8.5,8.5 L10.5,8.5 L10.5,7.5 L8.5,7.5 L8.5,8.5 Z M12.5,8.5 L14.5,8.5 L14.5,7.5 L12.5,7.5 L12.5,8.5 Z M16.5,8.5 L18.5,8.5 L18.5,7.5 L16.5,7.5 L16.5,8.5 Z M20.5,8.5 L22.5,8.5 L22.5,7.5 L20.5,7.5 L20.5,8.5 Z M24.5,8.5 L26.5,8.5 L26.5,7.5 L24.5,7.5 L24.5,8.5 Z M28.5,8.5 L30.5,8.5 L30.5,7.5 L28.5,7.5 L28.5,8.5 Z M32.5,8.5 L34.5,8.5 L34.5,7.5 L32.5,7.5 L32.5,8.5 Z M36.5,8.5 L38.5,8.5 L38.5,7.5 L36.5,7.5 L36.5,8.5 Z M40.5,8.5 L42.5,8.5 L42.5,7.5 L40.5,7.5 L40.5,8.5 Z M44.5,8.5 L46.5,8.5 L46.5,7.5 L44.5,7.5 L44.5,8.5 Z M48.5,8.5 L50.5,8.5 L50.5,7.5 L48.5,7.5 L48.5,8.5 Z M52.5,8.5 L54.5,8.5 L54.5,7.5 L52.5,7.5 L52.5,8.5 Z M56.5,8.5 L58.5,8.5 L58.5,7.5 L56.5,7.5 L56.5,8.5 Z M60.5,8.5 L62.5,8.5 L62.5,7.5 L60.5,7.5 L60.5,8.5 Z" id="Path-9-Copy-3" fill="url(#linearGradient-1)" fill-rule="nonzero"></path>
<ellipse id="Oval-4-Copy-4" fill="#EAEBEB" cx="62" cy="8" rx="8" ry="8"></ellipse>
<ellipse id="Oval-4-Copy-8" fill="url(#linearGradient-3)" cx="8" cy="8" rx="8" ry="8"></ellipse>
</g>
</g>
<polygon id="Path-3" fill="#BCBCBC" fill-rule="nonzero" opacity="0.400000006" points="65.0389438 8.50151893 1.03894383 3.50151893 0.961056166 4.49848107 64.9610562 9.49848107"></polygon>
<polygon id="Path-4" fill="#BCBCBC" fill-rule="nonzero" opacity="0.400000006" points="252.242821 30.4370786 297.242821 5.43707864 296.757179 4.56292136 251.757179 29.5629214"></polygon>
<polygon id="Path-5" fill="#BCBCBC" fill-rule="nonzero" opacity="0.400000006" points="228.132741 213.482058 297.132741 194.482058 296.867259 193.517942 227.867259 212.517942"></polygon>
<polygon id="Path-6" fill="#BCBCBC" fill-rule="nonzero" opacity="0.400000006" points="249.373705 442.332182 297.373705 388.332182 296.626295 387.667818 248.626295 441.667818"></polygon>
<polygon id="Path-7" fill="#BCBCBC" fill-rule="nonzero" opacity="0.400000006" points="89.0227038 391.500516 1.02270383 387.500516 0.97729617 388.499484 88.9772962 392.499484"></polygon>
<polygon id="Path-8" fill="#BCBCBC" fill-rule="nonzero" opacity="0.400000006" points="44.3156357 232.612219 1.3156357 197.612219 0.684364301 198.387781 43.6843643 233.387781"></polygon>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px"
y="0px" viewBox="0 0 56.966 56.966" style="enable-background:new 0 0 56.966
56.966;" xml:space="preserve" width="16px" height="12px"
>
<path
d="M55.146,51.887L41.588,37.786c3.486-4.144,5.396-9.358,5.396-14.786c0-12.682-10.318-23-23-23s-23,10.318-23,23
s10.318,23,23,23c4.761,0,9.298-1.436,13.177-4.162l13.661,14.208c0.571,0.593,1.339,0.92,2.162,0.92
c0.779,0,1.518-0.297,2.079-0.837C56.255,54.982,56.293,53.08,55.146,51.887z
M23.984,6c9.374,0,17,7.626,17,17s-7.626,17-17,17
s-17-7.626-17-17S14.61,6,23.984,6z" fill="#FFFFFF"
/>
</svg>

After

Width:  |  Height:  |  Size: 821 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1440px" height="85px" viewBox="0 0 1440 85" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 42 (36781) - http://www.bohemiancoding.com/sketch -->
<title>wavy-divider</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="50%" y1="29.9029703%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#000000" stop-opacity="0.0211447011" offset="0%"></stop>
<stop stop-color="#000000" stop-opacity="0" offset="100%"></stop>
</linearGradient>
</defs>
<g id="landing" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="cms-landing-v6" transform="translate(0.000000, -1205.000000)" fill="url(#linearGradient-1)">
<g id="triptych" transform="translate(0.000000, 1205.000000)">
<path d="M-1.35525272e-20,85 L1440,85 L1440,61 C1440,61 1440,11 1080,11 C720,11 718,47 358,47 C-2,47 -2.27373675e-13,0.647058824 -2.27373675e-13,0.647058824 L-1.35525272e-20,85 Z" id="wavy-divider"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

124
website/src/js/app.js Executable file
View File

@ -0,0 +1,124 @@
// Sticky Nav Functionality in Vanilla JS
var header = $("#header");
if ($('.landing.page, .community.page').length) {
window.onscroll = function() {
var currentWindowPos = document.documentElement.scrollTop || document.body.scrollTop;
if (currentWindowPos > 0) {
header.addClass('scrolled');
} else {
header.removeClass('scrolled');
}
};
}
// Auto Indexing Docs Nav
var indexDocsSubsections = function() {
if ($('.docs.page').length) {
if ($('#docs-content h2').length > 1) {
$('<ul class="nav-subsections" id="nav-subsections"></ul>').insertAfter($('#docs-nav .nav-link.active'));
$('#docs-content h2').each(function() {
var sectionTitle = $(this).html(),
anchor = $(this).attr("id");
$('#nav-subsections').append('<li><a class="subnav-link for-' + anchor + '" href="#' + anchor + '">' + sectionTitle + '</a></li>');
});
$.localScroll({
offset: -120,
duration:200,
hash:true
});
}
$(window).on('scroll', function(){
window.requestAnimationFrame(scrollHandler);
function scrollHandler() {
var scrollTop = $(window).scrollTop(),
windowHeight = $(window).height(),
first = false,
allSubnavLinks = $("#docs-nav .subnav-link");
$("#docs-content h2").each( function() {
var offset = $(this).offset(),
thisLink = '.for-' + $(this).attr("id");
if (scrollTop <= offset.top && ($(this).height() + offset.top) < (scrollTop + windowHeight) && first == false) {
allSubnavLinks.removeClass('active');
$(thisLink).addClass('active');
first=true;
} else {
first=false;
}
});
}
});
}
}
indexDocsSubsections();
var docsMobileMenu = function() {
if ($('.docs.page').length) {
$("#mobile-docs-nav").change(function() {
window.location = $(this).find("option:selected").val();
});
}
}
docsMobileMenu();
// search
$('#search-button').click(function() {
console.log("clicked")
$('.search-icon').toggleClass('active');
$('.algolia-search').toggleClass('closed');
});
// eventbrite info
var eventInfoLoad = function() {
if ($('.community.page').length) {
var eventRequest = new XMLHttpRequest;
var eventbriteToken = 'C5PX65CJBVIXWWLNFKLO';
var eventbriteOrganiser = '14281996019';
eventRequest.open('GET', 'https://www.eventbriteapi.com/v3/events/search/?token=' + eventbriteToken + '&organizer.id=' + eventbriteOrganiser + '&expand=venue%27', true);
eventRequest.onload = function() {
if (eventRequest.status >= 200 && eventRequest.status < 400) {
// Success!
var data = JSON.parse(eventRequest.responseText);
var upcomingDate = data.events[0].start.utc;
updateDate(upcomingDate);
} else {
var upcomingDate = "0000-00-00T00:00:00Z";
updateDate(upcomingDate);
}
};
eventRequest.onerror = function() {
alert('The event info could not be loaded at this time, please try again later.');
};
function updateDate(eventDate) {
$('.month').append(moment(eventDate).format('MMMM'));
$('.day').append(moment(eventDate).format('DD'));
$('.calendar-cta h2 strong:first-child()').append(moment(eventDate).format('dddd, MMMM Do'));
$('.calendar-cta h2 strong:last-child()').append(moment(eventDate).utcOffset(-7).format('h a'));
}
eventRequest.send();
}
}
eventInfoLoad();

37
website/webpack.conf.js Executable file
View File

@ -0,0 +1,37 @@
import webpack from "webpack";
import path from "path";
export default {
module: {
loaders: [
{
test: /\.((png)|(eot)|(woff)|(woff2)|(ttf)|(svg)|(gif))(\?v=\d+\.\d+\.\d+)?$/,
loader: "file?name=/[hash].[ext]"
},
{test: /\.json$/, loader: "json-loader"},
{
loader: "babel",
test: /\.js?$/,
exclude: /node_modules/,
query: {cacheDirectory: true}
}
]
},
plugins: [
new webpack.ProvidePlugin({
"fetch": "imports?this=>global!exports?global.fetch!whatwg-fetch"
})
],
context: path.join(__dirname, "src"),
entry: {
app: ["./js/app"]
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js"
},
externals: [/^vendor\/.+\.js$/]
};

6788
website/yarn.lock Normal file

File diff suppressed because it is too large Load Diff