diff --git a/blog/2018-09-14-say-hello-to-gridsome/index.md b/blog/2018-10-10-say-hello-to-gridsome/index.md similarity index 100% rename from blog/2018-09-14-say-hello-to-gridsome/index.md rename to blog/2018-10-10-say-hello-to-gridsome/index.md diff --git a/blog/2019-19-12-gridsome-05/index.md b/blog/2019-02-19-gridsome-05/index.md similarity index 100% rename from blog/2019-19-12-gridsome-05/index.md rename to blog/2019-02-19-gridsome-05/index.md diff --git a/blog/2019-19-12-gridsome-05/plugins.png b/blog/2019-02-19-gridsome-05/plugins.png similarity index 100% rename from blog/2019-19-12-gridsome-05/plugins.png rename to blog/2019-02-19-gridsome-05/plugins.png diff --git a/blog/2019-05-10-gridsome-06/index.md b/blog/2019-05-10-gridsome-06/index.md new file mode 100644 index 000000000..337d7a409 --- /dev/null +++ b/blog/2019-05-10-gridsome-06/index.md @@ -0,0 +1,161 @@ +--- +title: Gridsome v0.6 +slug: gridsome-v06 +author: [hjvedvik, tommyvedvik] +date: 2019-05-10 +excerpt: "Gridsome 0.6 introduces a Pages API that gives you full control of page creation. It also has an API that let you fetch internal pages into other pages and components. This is perfect for lightboxes or «Click for more» pagination etc. 0.6 also improves build times and has a smaller core JS bundle size!" +--- + +💥 *This version includes breaking changes.* + +## The new Pages API + +Until now, you have only been able to create pages by having Vue components in the `src/pages` folder or by creating templates for content types. Gridsome 0.6 comes with a new API for creating pages. It lets you create pages programmatically, which will give you much more flexibility. + +**Very basic example:** + +```js +// gridsome.server.js +module.exports = function (api) { + api.createPages(({ createPage }) => { + createPage({ + path: '/my-page', + component: './src/templates/MyPage.vue' + }) + }) +} +``` + +You can use it to create pages manually from the GraphQL data layer: + +```js +module.exports = function (api) { + api.createPages(async ({ graphql, createPage }) => { + const { data } = await graphql(`{ + allProduct { + edges { + id + path + } + } + `) + + data.allProduct.edges.forEach(({ node }) => { + createPage({ + path: `${node.path}/reviews`, + component: './src/templates/ProductReviews.vue', + context: { + id: node.id + } + }) + }) + }) +} +``` + +... or you could **bypass GraphQL** if you think feel it's a overkill in some use cases: + +```js +//gridsome.server.js +module.exports = function (api) { + api.createManagedPages(async ({ createPage }) => { + const { data } = await axios.get('https://api.example.com/posts') + + data.forEach(item => { + createPage({ + path: `/posts/${item.path}`, + component: './src/templates/Post.vue', + context: { + title: item.title, + content: item.content + } + }) + }) + }) +} +``` + +Add data by using `$context` in Vue component. + +```html + +``` + +Read more about the [Pages API](/docs/pages-api) + +## New function for fetching internal pages +A new function available in [Client API](/docs/client-api) let you fetch internal pages. This is perfect for building lightboxes or «Click for more» pagination etc. + +```js +export default { + data () { + return { + fetchedPage: null + } + }, + async mounted () { + try { + const results = await this.$fetch('/a-page-path') + this.fetchedPage = results.data + } catch (error) { + console.log(error) + } + } +} +``` + +Learn more about [fetching internal pages](/docs/client-side-data) + + +## Faster build times and smaller core JS bundle size + +Gridsome has been importing `page-query` data with webpack dynamic imports. Which means that webpack had to compile every JSON file into a JavaScript chunk. Having lots of pages would increase build times unnecessary. From now on, page data will be stored as raw JSON files without interference from webpack. And each file is prefetched and loaded on demand for each page. The overall JavaScript size is reduced by about 30% in most cases. + +**Example when building a site with 8300 pages:** + +| | Before | After | +|-|-------:|-----:| +| Run webpack | 50s | **10s** | +| Execute GraphQL | 25s | 25s | +| Render HTML | 15s | 15s | +| `app.js` size | 475kb | **170kb** | +| `page-query.js` size | 510kb | *removed* | +| Size of all JS chunks | 1000kb (excl. data chunks) | **570kb** (total) | + + +## New website design and starters library +We redesigned https://gridsome.org to be more lightweight & clean. This is a website you will spend a lot of time at if you're developing Gridsome projects. We wanted it to require as little CPU power as possible. The old website killed CPUs with SVG path animations. + +We also added a [Starter library](/starters) to help anyone get quickly up and running with Gridsome. + +![Starters](./starters.png) + +## Breaking changes + +- `NodeInterface` is renamed to `Node`. +- `title`, `path`, `date`, `content`, `excerpt`, and `slug` are now custom fields. +- Custom fields are no longer camel cased automatically. + - Some plugins depends on the original field names to work properly. +- Routes for pages in `src/pages` no longer get automatic names + - This was changed because automatic route names would most likely conflict with custom pages. + - Links like `` needs to be updated to ``. Or add a custom `name` to the Page. + +## Other features and fixes + +- Add custom node fields as top-level options. [Read more](/docs/data-store-api#collectionaddnodeoptions) +- Limit argument for GraphQL. [Read more](/docs/querying-data#limit) +- Advanced GraphQL sort argument. [Read more](/docs/querying-data#advancedsorting) +- Configure webpack without chaining. [Read more](/docs/config#configurewebpack) +- Render the current state in markup to prevent initial request. + +You will find all the changes in the [changelog](https://github.com/gridsome/gridsome/blob/master/gridsome/CHANGELOG.md) + + +## 3000 GitHub Stars 🌟🎉 + +Thanks to everyone who have contributed! Gridsome is going fast on its mission to be the best and easiest way to build modern websites and PWAs for any data source. diff --git a/blog/2019-05-10-gridsome-06/starters.png b/blog/2019-05-10-gridsome-06/starters.png new file mode 100644 index 000000000..c523fa64d Binary files /dev/null and b/blog/2019-05-10-gridsome-06/starters.png differ diff --git a/blog/authors/authors.yaml b/blog/authors/authors.yaml deleted file mode 100644 index a012640a8..000000000 --- a/blog/authors/authors.yaml +++ /dev/null @@ -1,11 +0,0 @@ -- id: tommyvedvik - name: Tommy Vedvik - bio: Co-founder of @gridsome. UX & Design. - avatar: ./tommyvedvik.jpg - twitter: "@tommyvedvik" - -- id: hjvedvik - name: Hans-Jørgen Vedvik - bio: Co-founder of @gridsome. Developer lead. - avatar: ./hjvedvik.png - twitter: "@hjvedvik" \ No newline at end of file diff --git a/contributors/contributors.yaml b/contributors/contributors.yaml new file mode 100644 index 000000000..1e3c16373 --- /dev/null +++ b/contributors/contributors.yaml @@ -0,0 +1,29 @@ +- id: tommyvedvik + name: Tommy Vedvik + bio: "Co-creator of @gridsome. UX & Design." + avatar: ./images/tommyvedvik.jpg + twitter: "@tommyvedvik" + +- id: hjvedvik + name: Hans-Jørgen Vedvik + bio: "Co-creator of @gridsome. Developer lead." + avatar: ./images/hjvedvik.png + twitter: "@hjvedvik" + +- id: gridsome + name: Gridsome + bio: "Official Gridsome Projects" + avatar: ../static/logos/logo-circle-light.svg + twitter: "@gridsome" + +- id: cosmicjs + name: Cosmic JS + bio: Modern Content Management Solution + avatar: ./images/cosmicjs.svg + twitter: "@cosmicjs" + +- id: cossssmin + name: + bio: "Developer. Maker of (mostly) email things. #emailgeeks #JAMStack" + avatar: ./images/cosmin.png + twitter: "@cossssmin" \ No newline at end of file diff --git a/contributors/images/cosmicjs.svg b/contributors/images/cosmicjs.svg new file mode 100644 index 000000000..b8488dd05 --- /dev/null +++ b/contributors/images/cosmicjs.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/contributors/images/cosmin.png b/contributors/images/cosmin.png new file mode 100644 index 000000000..4b60c9605 Binary files /dev/null and b/contributors/images/cosmin.png differ diff --git a/blog/authors/hjvedvik.png b/contributors/images/hjvedvik.png similarity index 100% rename from blog/authors/hjvedvik.png rename to contributors/images/hjvedvik.png diff --git a/blog/authors/tommyvedvik.jpg b/contributors/images/tommyvedvik.jpg similarity index 100% rename from blog/authors/tommyvedvik.jpg rename to contributors/images/tommyvedvik.jpg diff --git a/docs/README.md b/docs/README.md index 943111c29..af9d220bb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,32 +1,28 @@ # Getting started -> Gridsome is a Vue.js-powered, modern site generator for building the fastest possible websites for any Headless CMS, APIs or Markdown-files. Gridsome makes it easy and fun for developers to create fast, beautiful websites without needing to become a performance expert. +> Gridsome is a **Vue.js-powered modern website generator** that makes it easy and fun for developers to create beautiful [JAMstack](/docs/jamstack) websites & PWAs that are [fast by default](/docs/fast-by-default). ### Why Gridsome? - **Local development with hot-reloading** - See code changes in real-time. -- **Data source plugins** - Use it for any popular Headless CMSs, APIs or Markdown-files. +- **Data source plugins** - Connect to any popular Headless CMSs, APIs or Markdown-files. - **File-based page routing** - Quickly create and manage routes with files. - **Centralized data managment** - Pull data into a local, unified GraphQL data layer. - **Vue.js for frontend** - A lightweight and approachable front-end framework. - **Auto-optimized code** - Get code-splitting and asset optimization out-of-the-box. - **Static files generation** - Deploy securely to any CDN or static web host. -[Learn more about how Gridsome works](/docs/how-it-works) +## Prerequisites +You should have basic knowledge about HTML, CSS, [Vue.js](https://vuejs.org) and how to use the [Terminal](https://www.linode.com/docs/tools-reference/tools/using-the-terminal/). Knowing how [GraphQL](https://www.graphql.com/) works is a plus, but not required. Gridsome is a great way to learn it. -### Prerequisites -You should have basic knowledge about HTML, CSS, [Vue.js](https://vuejs.org) and how to use the [Terminal](https://www.linode.com/docs/tools-reference/tools/using-the-terminal/). Knowing how [Vue Single File components](https://vuejs.org/v2/guide/single-file-components.html) & [GraphQL](https://www.graphql.com/) works is a plus, but not required. Gridsome is a great way to learn both. - -Gridsome requires **Node.js** and recommends **Yarn**. [How to setup](/docs/prerequisites) +Gridsome requires [Node.js](https://nodejs.org/) (v8.3+) and recommends [Yarn](https://yarnpkg.com). +## How to install ### 1. Install Gridsome CLI tool -Using yarn: -`yarn global add @gridsome/cli` - -Using npm: -`npm install --global @gridsome/cli` +- Using **YARN:** `yarn global add @gridsome/cli` +- Using **NPM:** `npm install --global @gridsome/cli` ### 2. Create a Gridsome project @@ -41,6 +37,20 @@ Using npm: 2. Use `gridsome build` to generate static files in a `/dist` folder + +#### Learn more + - [How it works](/docs/how-it-works) - [How Pages work](/docs/pages) - [How to deploy](/docs/deployment) + + +## Alternatives + +- **[VuePress.](https://vuepress.vuejs.org/)** Another static site generator for Vue.js. It uses local markdown files for content and is perfect for documentation sites. It is possible to build anything in VuePress and Markdown (Like a blog f.ex). + +- **[Nuxt.](https://nuxtjs.org/)** A Universal Vue.js Framework for server-side rendered (SSR) apps and websites. It also has a static site generator feature, but the main focus is SSR. + +- **[Gatsby.js](https://www.gatsbyjs.org/)** Gridsome is highly inspired by Gatsby.js (React.js based), which collects data sources and generates a static site from it. Gridsome is an alternative for Gatsby.js. + + diff --git a/docs/assets-css.md b/docs/assets-css.md index b6a9f2f0c..4ccfc31db 100644 --- a/docs/assets-css.md +++ b/docs/assets-css.md @@ -24,7 +24,7 @@ You can also use SASS in **Vue Components** with the `lang="scss"` attribute: ```html ``` @@ -126,15 +132,17 @@ Gridsome [Critical CSS plugin](/plugins/@gridsome/plugin-critical) extracts CSS ### Tailwind In order to add Tailwind to your Gridsome project, You need to install the [gridsome-plugin-tailwindcss plugin](https://www.npmjs.com/package/gridsome-plugin-tailwindcss), To install it run `npm install -D gridsome-plugin-tailwindcss` add the following to your `gridsome.config.js`. where the config option will be the configuration file for tailwind, If you don't supply that option, `./tailwind.js` will be used by default. -``` -plugins: [ - { - use: 'gridsome-plugin-tailwindcss', - options: { - config: './some/file/js' +```js +module.exports = { + plugins: [ + { + use: 'gridsome-plugin-tailwindcss', + options: { + config: './some/file/js' + } } - } -] + ] +} ``` ### Bulma @@ -145,6 +153,7 @@ plugins: [ ...plugin coming ### BootstrapVue + [BootstrapVue](https://bootstrap-vue.js.org/) provides one of the most comprehensive implementations of Bootstrap V4 components and grid system available for Vue.js 2.5+, complete with extensive and automated WAI-ARIA accessibility markup. To instal use: @@ -161,17 +170,19 @@ Then, register BootstrapVue plugin in your `main.js` file: ```js import BootstrapVue from 'bootstrap-vue' + import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' // Then add it to export function -export default function (Vue, { router, head, isClient }) { +export default function (Vue) { Vue.use(BootstrapVue) } ``` ### Vuetify + [Vuetify](https://vuetifyjs.com/en/) is a semantic component framework for Vue. It aims to provide clean, semantic and reusable components that make building your application a breeze. Based on Google's material design, it can be a quick way to get an app up and running quickly with pre-built components available to use and customize. To install use: @@ -192,7 +203,7 @@ import Vuetify from 'vuetify' import 'vuetify/dist/vuetify.min.css' import DefaultLayout from '~/layouts/Default.vue' -export default function (Vue, { router, head, isClient }) { +export default function (Vue, { head }) { head.link.push({ rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons' @@ -204,6 +215,7 @@ export default function (Vue, { router, head, isClient }) { Vue.component('Layout', DefaultLayout) } ``` + Finally, there is one last thing you will need in order to build your application with vuetify. You will need to whitelist Vuetify in webpack in order to build. @@ -237,5 +249,6 @@ module.exports = function (api) { }) } ``` + Then you should be able to build now! You will find the files in your dist/ folder. diff --git a/docs/client-api.md b/docs/client-api.md index dc9869686..72f6ad7a9 100644 --- a/docs/client-api.md +++ b/docs/client-api.md @@ -1,27 +1,8 @@ # Client API -Create a `gridsome.client.js` at root in your project or plugin that exports a function. The function will receive the Vue instance, plugin options and a context. The context has references to options for the Vue app, the VueRouter instance and VueMeta options. The file is loaded on the server and in the browser as default. +The client API lets you install Vue plugins, register components and directives and modify the options passed to the Vue instance. You can also add router hooks and HTML metas. Start by exporting a default function in a `src/main.js` file in your project to use the Client API. -```js -/** - * @param Vue Vue instance - * @param options Plugin options - * @param context.appOptions Options for the Vue instance - * @param context.router The router instance - * @param context.head VueMeta info objet - * @param context.isClient - * @param context.isServer - */ -export default function (Vue, options, context) { - // ... -} -``` - -## Using in `src/main.js` - -Export a default function in `src/main.js` to use the Client API. The only difference here is that it will only have access to the Vue instance and the context. The function will be called after all plugins. - -#### Example usage +> The function exported by `src/main.js` will be executed after all plugins. ```js import Bootstrap from 'bootstrap-vue' @@ -29,13 +10,30 @@ import Bootstrap from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' -export default function (Vue) { +export default function (Vue, context) { Vue.use(Bootstrap) } ``` -## context.appOptions -**appOption** is passed to the main **Vue Instance** like `new Vue(appOptions)`. +## Usage in plugins + +Create a `gridsome.client.js` at root in the plugin directory that exports a function. The function will recieve the plugin options as second argument and the context as the third. + +```js +export default function (Vue, options, context) { + // ... +} +``` + +## The client context + +The context has references to options for the Vue app, the VueRouter instance and VueMeta options. The file is loaded on the server and in the browser as default. + +### appOptions + +- Type: `Object` + +Options passed to the main **Vue Instance** like `new Vue(appOptions)`. Here is an example where we add **Vuex** store to the Vue instance. ```js @@ -57,9 +55,11 @@ export default function (Vue, { appOptions }) { } ``` -## context.router -**context.router** lets you access the [Vue Router instance](https://router.vuejs.org/api/#router-instance-methods). -This example lets you do stuff before next page load. +### router + +- Type: `VueRouter` + +Interact with the router. ```js export default function (Vue, { router }) { @@ -69,3 +69,87 @@ export default function (Vue, { router }) { }) } ``` + +Read more about the [Vue router](https://router.vuejs.org/api/#router-instance-methods) methods. + +### head + +- Type: `Object` + +Allows you to manage your websites's metadata. + +```js +export default function (Vue, { head }) { + head.script.push({ + src: 'https://www.example.com/my-script.js' + }) +} +``` + +Read more about [populating <head>](/docs/head) + +## Component Injections + +These methods are injected into every component. + +### $fetch(path) + +Fetch `page-query` results and [page context](http://localhost:8080/docs/pages-api#the-page-context) from internal pages + +```js +export default { + data () { + return { + otherPageData: null + otherPageContext: null + } + }, + async mounted () { + try { + const results = await this.$fetch('/other-page') + + this.otherPageData = results.data + this.otherPageContext = results.context + } catch (error) { + console.log(error) + } + } +} +``` + +### $url(path) + +Generates URL including `pathPrefix`. Useful for creating internal links without `g-link`. + +```html + +``` + +```html + +``` + +## Process Injections + +These properties are injected into the client process. + +### isClient + +- Type: `boolean` + +```js +if (process.isClient) { + // browser only code +} +``` + +### isServer + +- Type: `boolean` + +```js +if (process.isServer) { + // server only code +} +``` + diff --git a/docs/client-side-data.md b/docs/client-side-data.md new file mode 100644 index 000000000..45fb0e68c --- /dev/null +++ b/docs/client-side-data.md @@ -0,0 +1,48 @@ +# Client-side data + +Client-side data is data added after page load. This can be data coming from other internal pages, a REST API or a GraphQL API. It's important to ony load your data in the `mounted` hook to prevent it from beeing included in the generated markup. + +## Fetch from internal pages + +Fetch `page-query` results and [page context](http://localhost:8080/docs/pages-api#the-page-context) from other internal pages. The following example fetches data from `/other-page` and stores the results. + +```js +export default { + data () { + return { + otherPage: null + } + }, + async mounted () { + try { + const results = await this.$fetch('/other-page') + this.otherPage = results.data + } catch (error) { + console.log(error) + } + } +} +``` + +The `fetch` method can also be imported from `gridsome`. + +```js +import { fetch } from 'gridsome' + +export default { + async mounted () { + const results = await fetch('/other-page') + console.log(results.data) + } +} +``` + +Read more about the [$fetch() method](/docs/client-api#fetchpath). + +## Fetch from REST API + +....Contributions are welcome! + +## Fetch from GraphQL API + +....Contributions are welcome! diff --git a/docs/components.md b/docs/components.md index 867b4c761..436b35d82 100644 --- a/docs/components.md +++ b/docs/components.md @@ -70,12 +70,12 @@ to fetch data from data sources. The results will be stored in a ```html -query Example { - example: examplePage (path: "/docs/example") { +query Post { + post (id: "1") { content } } diff --git a/docs/config.md b/docs/config.md index 4e29474f5..64c00bdb2 100644 --- a/docs/config.md +++ b/docs/config.md @@ -29,8 +29,9 @@ The description is used as description on your frontpage. - Default `''` ## pathPrefix + - Type `string` -- Default `'/'` +- Default `''` Gridsome assumes your project is served from the root of your domain. Change this option to `'/my-app'` if your project will be hosted in a @@ -49,12 +50,29 @@ from metaInfo you set in your pages. - Type `Array` - Default `[]` -[Read more about using plugins](/docs/install-plugins) +Activate plugins by adding them to the `plugins` array. + +```js +module.exports = { + plugins: [ + { + use: '@gridsome/source-filesystem', + options: { + path: 'blog/**/*.md', + route: '/blog/:year/:month/:day/:slug', + typeName: 'Post' + } + } + ] +} +``` + +[Read more about how to use plugins](/docs/plugins) ## icon - Type `string | Object` -- Default `'src/favicon.png'` +- Default `'./src/favicon.png'` Gridsome will use any image located at `src/favicon.png` as favicon and touchicon by default, but you can define another path or sizes etc. The icon @@ -63,18 +81,18 @@ should be a square and minimum 16 pixels. The favicon will be resized to 16, 32, default. ```js -{ - icon: 'src/my-icon.png' +module.exports = { + icon: './src/my-icon.png' } ``` Use a different image for touch icons: ```js -{ +module.exports = { icon: { - favicon: 'src/my-favicon.png', - touchicon: 'src/my-touchicon.png' + favicon: './src/my-favicon.png', + touchicon: './src/my-touchicon.png' } } ``` @@ -82,14 +100,14 @@ Use a different image for touch icons: Define custom sizes and disable effects on iOS < 7 devices: ```js -{ +module.exports = { icon: { favicon: { - src: 'src/my-favicon.png', + src: './src/my-favicon.png', sizes: [16, 32, 96] }, touchicon: { - src: 'src/my-touchicon.png', + src: './src/my-touchicon.png', sizes: [76, 152, 120, 167], precomposed: true } @@ -97,6 +115,32 @@ Define custom sizes and disable effects on iOS < 7 devices: } ``` +## configureWebpack + +- Type `Object | Function` + +The option will be merged with the internal config if it is an object. + +```js +module.exports = { + configureWebpack: { + // merged with the internal config + } +} +``` + +If the option is a function, it will get the internal config as its first argument. You can either modify the argument or return a new config object that will override the internal webpack config. + +```js +const merge = require('webpack-merge') + +module.exports = { + configureWebpack (config) { + return merge({ /* custom config */ }, config) + } +} +``` + ## chainWebpack - Type `Function` @@ -106,10 +150,10 @@ A function that will receive an instance of ChainableConfig powered by ## runtimeCompiler -- Type `Boolean` +- Type `boolean` - Default `false` -Include the Vue template compiler compiler at runtime +Include the Vue template compiler compiler at runtime. ## configureServer @@ -119,15 +163,21 @@ Configure the development server. [Read more about configuring the development server](/docs/server-api#apiconfigureserverfn) +## css.split + +- Type `boolean` *Default: `false`* + +Split CSS into multiple chunks. Splitting is disabled by default. Splitting CSS can result in weird behaviors. + ## css.loaderOptions - Type `Object` -- Default `{ sass: { indentedSyntax: true }, stylus: { preferPathResolver: 'webpack' } }` +- Default `{}` Pass options to CSS-related loaders. For example: ```js -{ +module.exports = { css: { loaderOptions: { scss: { diff --git a/docs/data-dynamic.md b/docs/data-dynamic.md deleted file mode 100644 index 428e91b27..000000000 --- a/docs/data-dynamic.md +++ /dev/null @@ -1,18 +0,0 @@ -# Use dynamic data -**Gridsome sites are static and serverless when deployed.** To get real-time updates from a database (dynamic data) you'll need to fetch data from an external API after page load. - -Best practice for this is to have a placeholder where the content will be added (A spinner or ) and then update that content when content is fetched. You can also just use static content as placeholder and then update it with dynamic data when it's fetched. - -We recommend to use the [axios](https://github.com/axios/axios) package to add dynamic data to Gridsome. Install **axios** with `npm install axios --save` to get started. - - -## Query from external Rest API -....Contributions are welcome! - -## Query from external GraphQL API -....Contributions are welcome! - -## Dynamic content and eCommerce stores -A good example of using dynamic content with Gridsome is eCommerce stores. While most of the site is static you probably want prices and quantity to be dynamic. - -This means you can generate 99% of your eCommerce site at build time and then pull inn dynamic data when users are visiting for example a product page or starts filtering products. \ No newline at end of file diff --git a/docs/data-store-api.md b/docs/data-store-api.md index 0c4ad5e25..f098fba3c 100644 --- a/docs/data-store-api.md +++ b/docs/data-store-api.md @@ -14,19 +14,15 @@ module.exports = function (api) { ## Add a content type collection -### `store.addContentType(options)` +### store.addContentType(options) -Add a new content type to store. - -A Vue component in the `src/templates` folder with a filename matching the `typeName` option will be used as a template for all nodes with this type. [Read more about templates](/docs/templates). - -##### Arguments - -- options `object | string` *Options or just the GraphQL schema type name.* +- options `object | string` *Options or just a GraphQL schema type name.* - typeName `string` *Required GraphQL schema type and template name.* - route `string` *Optional dynamic route.* [Read more about Routing](/docs/routing) -##### Usage +Add a new content type to store. + +A Vue component in the `src/templates` folder with a filename matching the `typeName` option will be used as a template for all nodes with this type. [Read more about templates](/docs/templates). ```js api.loadSource(store => { @@ -37,61 +33,43 @@ api.loadSource(store => { }) ``` -### `store.getContentType(typeName)` - -Get a content type previously created. - -##### Arguments +### store.getContentType(typeName) - typeName `string` *The GraphQL schema type name.* -## Add nodes to collections +Get a content type previously created. -### `collection.addNode(options)` +## Add nodes to collections -##### Arguments +### collection.addNode(options) - options `Object` *Required.* - - title `string` *Required.* - id `string` *A unique id for this content type.* - - slug `string` *Custom slug. Fallbacks to a slugified `title`.* - - path `string` *Optional path to use when not having a dynamic route.* - - date `string` *The date. Fallbacks to current date.* - - content `string` *Optional content.* - - excerpt `string` *Optional excerpt.* - - fields `object` *Custom fields.* - -##### Usage + - ...fields `object` *Custom fields.* ```js api.loadSource(store => { const posts = store.addContentType({ typeName: 'BlogPost', - route: '/blog/:year/:slug' + route: '/blog/:year/:title' }) posts.addNode({ title: 'My first blog post', date: '2018-11-02', - fields: { - myField: 'My value' - } + customField: 'My value' }) }) ``` ## Referencing other nodes -### `store.createReference(typeName, id)` - -A helper function for creating references to other nodes. - -##### Arguments +### store.createReference(typeName, id) - typeName `string | object` *The node typeName to reference or the node instance.* - id `string | array` *The node id to reference (or ids if multiple nodes).* -##### Usage +A helper function for creating references to other nodes. This example creates two content types: `Author` and `Post`. The `author1` and `author2` fields on `Post` will both have a reference to the same author. @@ -107,36 +85,36 @@ api.loadSource(store => { posts.addNode({ title: 'The post', - fields: { - author1: store.createReference('Author', '1') - author2: store.createReference(author) - } + author1: store.createReference('Author', '1'), + author2: store.createReference(author) }) }) ``` -The field will contain the referred node fields: +The field will contain the referenced node fields in the GraphQL schema: ```graphql -query BlogPost ($id: String!) { - blogPost (id: $id) { +query BlogPost($id: String!) { + blogPost(id: $id) { title - author1 { title } - author2 { title } + author1 { + id + title + } + author2 { + id + title + } } } ``` -### `collection.addReference(fieldName, typeName)` - -Make a root field for all nodes in collection referencing to another node. - -##### Arguments +### collection.addReference(fieldName, typeName) - fieldName `string` *The field name.* - typeName `string` *GraphQL schema type to reference.* -##### Usage +Make a root field for all nodes in collection referencing to another node. ```js api.loadSource(store => { @@ -146,25 +124,19 @@ api.loadSource(store => { posts.addNode({ title: 'The post', - fields: { - author: '1' // Will reference to an author with id '1' - } + author: '1' // Will become a reference to an author with id '1' }) }) ``` ## Custom GraphQL fields -### `collection.addSchemaField(fieldName, fn)` - -Extend the GraphQL schema with a custom field for a node type. - -##### Arguments +### collection.addSchemaField(fieldName, fn) - fieldName `string` *The field name to create on node.* - fn `Function` *A function which returns an object with a GraphQL field and resolver.* -##### Usage +Extend the GraphQL schema with a custom field for a node type. ```js api.loadSource(store => { @@ -178,20 +150,18 @@ api.loadSource(store => { resolve (node, args) { const value = node.fields.myField - if (args.upperCase) { - return value.toUpperCase() - } - - return value + return args.upperCase + ? value.toUpperCase() + : value } })) }) ``` ```graphql -query Post ($id: String!) { - blogPost (id: $id) { - myField (upperCase: true) +query Post($id: String!) { + blogPost(id: $id) { + myField(upperCase: true) } } ``` @@ -212,9 +182,7 @@ api.loadSource(store => { contentType.addNode({ title: 'Lorem ipsum dolor sit amet.', - fields: { - customField: '...' - } + customField: '...' }) }) ``` @@ -246,14 +214,15 @@ module.exports = function (api) { const { data } = await axios.get('https://api.example.com/posts') const contentType = store.addContentType({ - typeName: 'BlogPosts' - route: '/blog/:year/:slug' // optional + typeName: 'BlogPosts', + route: '/blog/:year/:slug' }) for (const item of data) { contentType.addNode({ id: item.id, title: item.title, + slug: item.slug, date: item.date, content: item.content }) diff --git a/docs/data.md b/docs/data.md new file mode 100644 index 000000000..ebf3b9768 --- /dev/null +++ b/docs/data.md @@ -0,0 +1,89 @@ +# Data + +## Data store and GraphQL + +Data from source plugins are stored in an internal data store. You can use [GraphQL](https://graphql.org/) to access that data in your pages, templates or any other Vue component in your project. GraphQL is a declarative query language especially useful for retrieving only the data you ask for. Which again will result in smaller bundles. + +#### Usage in a pages or templates + +Start by adding a top-level `` block in your Vue component. Then write a [query](https://graphql.github.io/learn/queries/) inside it. The results will be available as a `$page` object in your templates. + +Read more about [fetching data](/docs/fetching-data) or the [Data Store API](/docs/data-store-api) + +```html + + + +query { + post(id: "1") { + title + } +} + +``` + +Read more about querying data in [pages](/docs/querying-data#query-data-in-pages) or [templates](/docs/querying-data#query-data-in-templates) + +#### Usage in any component + +`` only works for pages and templates. But you can use a `` block to retrieve data in any other component. The results will be available as a `$static` object in your templates. + +```html + + + +query { + post(id: "1") { + title + } +} + +``` + +Read more about querying data in [components](/docs/querying-data#query-data-in-components) + +## Pages and page context + +Every page that is created programmatically can have its own context with any data. The page context will be available as a `$context` object in your templates. + +```html + +``` + +Read more about the [Pages API](/docs/pages-api) + +## Local JSON and YAML files + +Gridome includes webpack loaders for importing [JSON](https://www.json.org/) and [YAML](https://yaml.org/) files into your components. + +```html + + + +``` diff --git a/docs/deployment.md b/docs/deployment.md index eaf173962..8a4194c9f 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,10 +1,10 @@ # Deployment -Gridsome is a modern website generator that generates secure & static files. To deploy a Gridsome site you need a **static site host**. +To deploy a Gridsome site you need a **static web host**. ## Git-based deploying -The best practice when working with Gridsome sites is to have your project hosted on a Git-service like GitHub and connect a deploy service that builds your site from a selected repository. [Netlify](//www.netlify.com/) is a great solution for this right now. They also have free plan for static website hosting. It's also possible to set up auto re-deploys if the repository or content changes. +The best practice when working with Gridsome sites is to have your project hosted on a Git-service like GitHub and connect a deploy service that builds your site from a selected repository. These services are great for Git-based deploying: diff --git a/docs/directory-structure.md b/docs/directory-structure.md index a7eacf6ae..2e1a3f33a 100644 --- a/docs/directory-structure.md +++ b/docs/directory-structure.md @@ -43,7 +43,7 @@ This file is optional and is used to hook into various parts of the Gridsome ser Import global styles and scripts here. The file also has an export function that has access to the **Client API**. This file is the place to install Vue plugins, register components and directives, etc. -[Read more about using the Client API in main.js](/docs/client-api#using-the-client-api-in-srcmainjs) +[Read more about using the Client API in main.js](/docs/client-api) #### Layouts directory diff --git a/docs/fast-by-default.md b/docs/fast-by-default.md new file mode 100644 index 000000000..142e77956 --- /dev/null +++ b/docs/fast-by-default.md @@ -0,0 +1,58 @@ +# Fast by default +> Gridsome builds ultra performance into every page automatically. You get code splitting, asset optimization, progressive images, and link prefetching out of the box. With Gridsome you gets almost perfect page speed scores by default. + +### What makes Gridsome sites fast? +1. [Pre-rendered HTML](#pre-rendered-html). Nothing beats static content in speed. +2. [Follows the PRPL-pattern](#the-prpl-pattern) for instant page loads. +3. [Smart link prefetching](#smart-link-prefetching) that uses Intersection Observer to load next pages. +4. [Progressive Images](#progressive-images) with automatic image compression and lazy loading. +5. [Vue.js SPA](#progressive-images) for fast browsing with no page refresh. + +## Pre-rendered HTML + +Gridsome pre-renders HTML at build time (Generates static files). Gridsome sites can be hosted anywhere, even on a CDN. There is no need for a Node.js server. + +A static site gives you many benefits: + +- ⚡️ **Better Performance.** Why wait for pages to build on the fly when you can generate them at deploy time? When it comes to minimizing the time to first byte, nothing beats pre-built files served over a CDN. + +- ⚡️ **Higher Security.** With server-side processes abstracted into microservice APIs, surface areas for attacks are reduced. You can also leverage the domain expertise of specialist third-party services. + +- ⚡️ **Cheaper, Easier Scaling.** When your deployment amounts to a stack of files that can be served anywhere, scaling is a matter of serving those files in more places. CDNs are perfect for this, and often include scaling in all of their plans. + + +## The PRPL pattern + +PRPL is a pattern for structuring and serving Progressive Web Apps (PWAs), with an emphasis on the performance of app delivery and launch. It stands for: + +- **Push** critical resources for the initial URL route. +- **Render** initial route. +- **Pre-cache** remaining routes. +- **Lazy-load** and create remaining routes on demand. + +Learn more about [PRPL pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/) + + + +## Smart link prefetching +Gridsome prefetches internal links in the background so browsing around goes insanely fast. It uses the built-in `` component and **Intersection Observer** to prefetch when the link is in view. + +Gridsome builds two files of every page. A static HTML and a small JavaScript file. When the website hydrates into a Vue.js-SPA, the link prefetching only loads the JavaScript to render the next page. This results in a faster and smoother browsing experience. + +[Learn more about **g-link** here](/docs/linking). + +## Progressive Images +Gridsome has a built-in `` component with automatic progressive image support. In **development** it lets you do real-time image processing, like resizing and cropping. + +In production, the `` is served as an ultra-compressed image before the image is lazy-loaded when in view by using **Intersection Observer**. + + +[Learn more about **g-image** here](/docs/images) + + +## Vue.js SPA +The `gridsome build` command generates **SEO-friendly HTML files** that can be hosted anywhere. These HTML files are optimized to load as fast as possible. After the HTML is loaded Vue.js takes over the HTML and **hydrates into a fully Vue-powered SPA.** + +**When SPA kicks in it only loads small code-splitted JS chunks for next pages.** + +[Learn more about Vue.js and Client Side hydration](https://ssr.vuejs.org/guide/hydration.html) diff --git a/docs/fetching-data.md b/docs/fetching-data.md index 88c7a8f66..e259107c4 100644 --- a/docs/fetching-data.md +++ b/docs/fetching-data.md @@ -7,22 +7,18 @@ Fetch content from local files or external APIs and store the data in a local da ## Use data source plugins Gridsome data source plugins are added in `gridsome.config.js`. You can find available data source plugins in the [Plugins directory](/plugins). - Here is an example of the [file-system](/plugins/@gridsome/source-filesystem) source added to config: + ```js module.exports = { plugins: [ { use: '@gridsome/source-filesystem', options: { - index: ['README'], path: 'docs/**/*.md', - typeName: 'DocPage', + typeName: 'DocPage' } - }, - { - // another data source - }, + } ] } ``` @@ -31,13 +27,10 @@ module.exports = { Every data source has different options, so take a look at their documentation to learn more. - ## Add data from APIs Gridsome adds data to the GraphQL data layer with the **Data store API** and the `api.loadSource` function. To use the API you need a `gridsome.server.js` file in the root folder of your Gridsome project. - - Learn more about the [Data store API here](/docs/data-store-api) A typical `gridsome.server.js` will look something like this: diff --git a/docs/filtering-data.md b/docs/filtering-data.md index 9a4b22f73..9cc65697b 100644 --- a/docs/filtering-data.md +++ b/docs/filtering-data.md @@ -1,6 +1,6 @@ # Filtering data -Each content type collection in the GraphQL schema has a `filter` argument which can be used to filter the results. You can filter by `id`, `title`, `slug`, `path` or any custom field. Each field type supports different operators. +Each content type collection in the GraphQL schema has a `filter` argument which can be used to filter the results. You can filter by any custom field. Each field type supports different operators. The syntax for `filter` is based on the [mongodb](https://docs.mongodb.com/manual/reference/operator/query/) query syntax. @@ -17,7 +17,7 @@ The syntax for `filter` is based on the [mongodb](https://docs.mongodb.com/manua ```graphql query { - allPost (filter: { id: { in: ["1", "2"] }}) { + allPost(filter: { id: { in: ["1", "2"] }}) { edges { node { title @@ -42,7 +42,7 @@ This example will query nodes where `id` is **1** or **2**. ```graphql query { - allPost (filter: { date: { gte: "2017" }}) { + allPost(filter: { date: { gte: "2017" }}) { edges { node { title @@ -65,7 +65,7 @@ This example will query only nodes where `date` is greater than or equal to **20 ```graphql query { - allPost (filter: { featured: { eq: true }}) { + allPost(filter: { featured: { eq: true }}) { edges { node { title @@ -94,7 +94,7 @@ This example will query only nodes where `featured` is **true**. ```graphql query { - allProduct (filter: { price: { between: [49, 99] }}) { + allProduct(filter: { price: { between: [49, 99] }}) { edges { node { title @@ -118,7 +118,7 @@ This example will query only nodes with `price` value between **49** and **99**. ```graphql query { - allPost (filter: { keywords: { contains: ["gridsome"] }}) { + allPost(filter: { keywords: { contains: ["gridsome"] }}) { edges { node { title @@ -128,4 +128,5 @@ query { } } ``` + This example will query only nodes which has the **gridsome** `keyword`. diff --git a/docs/gridsome-cli.md b/docs/gridsome-cli.md index b0535d322..0121d2840 100644 --- a/docs/gridsome-cli.md +++ b/docs/gridsome-cli.md @@ -5,10 +5,10 @@ globally with `npm install --global @gridsome/cli`. ## create -Usage `gridsome create {name} {starter}` +Usage `create [starter]` -- **name** - directory name to create the project in -- **starter** - optional starter kit name +- **name** - Directory name to create the project in. +- **starter** - Optional starter kit name. | Official starter kits | | | --------------------- | --------------------------------------- | diff --git a/docs/how-it-works.md b/docs/how-it-works.md index 32cd06e70..7b74c17ad 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -4,6 +4,7 @@ Gridsome is a modern website development framework for creating fast and secure Source plugins fetch content from local files or external APIs and store the data in a local database. A unified GraphQL data layer lets you extract only the data you need from the database and use it in your Vue.js components. The data is generated and stored as static JSON at build time. + ![How it works](./images/how-it-works.png) There are two ways to run Gridsome: @@ -26,11 +27,6 @@ The `gridsome develop` command starts a **local development server** with hot-re 4. **Generate code** - Generates runtime code like routes, plugins, etc. 5. **Bootstrap finish** - Starts the development server and shows the URLs in your console. -### GraphQL for data managment -**The GraphQL data layer is a tool available in development mode**. This is where all the data fetched into a Gridsome project is stored. Think of it as a local, temporary database that helps you work faster and better with your data. Add data from any **data sources** with [Source plugins](/plugins) or with the [Data store API](/docs/data-store-api). Data can be queried in any page or component. - -[Learn more about Querying data here](/docs/querying-data). - ## Gridsome build @@ -54,43 +50,13 @@ The `gridsome build` command prepares a project for **production**. This means i > Services like **Netlify** and **Zeit Now** let you run `gridsome build` automatically from a **Git-repository** and host the generated files on a CDN for you. These services also have hooks that enable you to re-build the site after a Git-commit. Learn more about Git-based [deployment here](/docs/deployment). -### Build time server-side rendering -Gridsome runs server-side rendering at build time. This means you don't need any server like Node.js to run Gridsome sites. They are pre-rendered & static and can be deployed to any static web host or FTP. +## GraphQL for data +**The GraphQL data layer is a tool available in development mode**. This is where all the data fetched into a Gridsome project is stored. Think of it as a local, temporary database that helps you work faster and better with your data. Add data from any **data sources** with [Source plugins](/plugins) or with the [Data store API](/docs/data-store-api). Data can be queried in any page or component. +[Learn more about Querying data here](/docs/querying-data). ## Vue.js for frontend - Gridsome uses [Vue.js](https://vuejs.org/) as front-end framework. Vue is an approachable, simple & fun framework for building fast interfaces. Vue is famous for its intuitive design and shallow learning curve. This means it's easy to train staff in, even non-frontend devs and designers. Since developers will be up-and-running with Vue quickly, training costs will be kept to a minimum. - -### Client-side Hydration -The `gridsome build` command generates **SEO-friendly HTML files** that can be hosted anywhere. These HTML files are optimized to load as fast as possible. After the HTML is loaded Vue.js takes over the HTML and **hydrates** into a fully Vue-powered SPA. - -> Hydration refers to the client-side process during which Vue takes over the static HTML sent by the server and turns it into a dynamic DOM that can react to client-side data changes. - -[Learn more about Vue.js and Client Side hydration](https://ssr.vuejs.org/guide/hydration.html) - - -### Automatic link prefetching -Gridsome prefetches internal links in the background so browsing around goes insanely fast. It uses the built-in `` component and **Intersection Observer** to prefetch when the link is in view. - -Gridsome builds two files of every page. A static HTML and a small JavaScript file. When the website hydrates into a Vue.js-SPA, the link prefetching only loads the JavaScript to render the next page. This results in a faster and smoother browsing experience. - -[Learn more about **g-link** here](/docs/linking). - -### Progressive Image support -Gridsome has a built-in `` component with built-in progressive image support. In **development** it lets you do real-time image processing, like resizing and cropping. - -In production, the `` is served as an ultra-compressed image before the image is lazy-loaded when in view by using **Intersection Observer**. - -[Learn more about **g-image** here](/docs/images) - - -## Alternatives - -- **[VuePress.](https://vuepress.vuejs.org/)** Another static site generator for Vue.js. It uses local markdown files for content and is perfect for documentation sites. It is possible to build anything in VuePress and Markdown (Like a blog f.ex). - -- **[Nuxt.](https://nuxtjs.org/)** A Universal Vue.js Framework for server-side rendered (SSR) apps and websites. It also has a static site generator feature, but the main focus is SSR. - -- **[Gatsby.js](https://www.gatsbyjs.org/)** Gridsome is highly inspired by Gatsby.js (React.js based), which collects data sources and generates a static site from it. Gridsome is an alternative for Gatsby.js. +[Learn more about Vue.js](https://vuejs.org/) diff --git a/docs/how-to-contribute.md b/docs/how-to-contribute.md index f8a183e2a..d82eab09c 100644 --- a/docs/how-to-contribute.md +++ b/docs/how-to-contribute.md @@ -39,12 +39,18 @@ Creating guest blog posts for Gridsome.org users is a great way to contribute to To add a new blog post to the gridsome.org blog: +**Prepare repository:** + - Clone [the Gridsome.org repo](https://github.com/gridsome/gridsome.org). - Run `yarn` to install all of the website's dependencies. - Run `gridsome develop` to preview the blog at `http://localhost:8000/blog`. -- The content for the blog lives in the `/blog` folder. Make additions or modifications here. -- Add your avatar image to `/blog/authors`. -- Add your name and info to `/blog/authors/authors.yaml`. +- The content for the blog lives in the `/blog` folder. + +**Create contributor profile:** +- Add your avatar image to `/contributors/images`. +- Add your name and info to `/contributors/contributors.yaml`. + +**Setup blog post:** - Add a new folder following the pattern `/blog/yyyy-mm-dd-title` (for example, 2018-09-14-say-hello-to-gridsome). Within this newly created folder add an `index.md` file. - Add `title`, `date`, `author`, and `tags` to the frontmatter of your `index.md`. If you are cross posting your post you can add `canonicalLink` for SEO benefits. - If your blog post contains images add them to your blog post folder and reference them in your post's `index.md`. @@ -54,13 +60,26 @@ To add a new blog post to the gridsome.org blog: - We recommend using a prefix of `docs`, like `docs/your-change`. -## Submit a Starter * +## Submit a Starter + +- Clone [the Gridsome.org repo](https://github.com/gridsome/gridsome.org). +- Run `yarn` to install all of the website's dependencies. + +**Create contributor profile:** +- Add your avatar image to `/contributors/images`. +- Add your name and info to `/contributors/contributors.yaml`. + +**Add starter:** +- Add starter screenshot to `/starters/screnshots` (840x840px / 1680x1680 for retina). +- Add starter details to end of this file `/starters/starters.yaml`. -*Comming soon...* +Gridsome Starers uses the Github project README file for content. -## Submit to Learn * +- Run `gridsome develop` to preview starter at `http://localhost:8000/starters`. +- Commit and push to your fork +- Create a pull request from your branch + - We recommend using a prefix of `starter`, like `starter/your-starter-id`. -*Comming soon...* ## Submit to Showcase * -*Comming soon...* \ No newline at end of file +*Coming soon...* \ No newline at end of file diff --git a/docs/images.md b/docs/images.md index c4519a4e4..499c52fb2 100644 --- a/docs/images.md +++ b/docs/images.md @@ -1,17 +1,33 @@ # Images -> Gridsome has a built-in `` component that outputs an optimized progressive image. It also resizes and crops in real-time when developing. There is no fake resizing with CSS. +Gridsome has a built-in `` component that outputs an optimized **progressive image**. It also resizes and crops in real-time when developing. -`` compresses and lazy-loads images automatically. The images will be resized down to 480, 1024, 1920 and 2560 pixels by default. A blurred version is displayed while the image is being loaded and a fallback `img` tag for browsers without JavaScript enabled is also generated. +A typical image component will look like this: + +```html + +``` +💡 `~` is an alias to **/src/** folder. + +📣 **Only local, relative image paths will be compressed by Gridsome.** + + + +### How it works + +- **A IMG element with a source srcset is used.** This means that using several media queries, you load the smallest image that matches your device (e.g. mobile devices get smaller images, desktop devices get larger images, etc.). The images will be resized down to 480, 1024, 1920 and 2560 pixels by default. + +- **A base64 blurred image loaded by default.** This makes: 1) Larger images outside the viewport are not requested until they’re needed, and 2) The blurred image is in a container with the same dimensions as the real image—therefore, no jumping when the image loads. + +- **An Intersection Observer** that swaps the base image for the larger image, when the image is in the viewport. (Lazy loading). -> Only local, relative image paths will be compressed by Gridsome. ## Usage in Vue templates A `` component is available in all your Vue templates and can be used to compress local images. The `src` attribute and options like `width`, `height` and `quality` must be static values because they are compiled into an object which contains URLs and other information that will be rendered into an `img` tag. ```html - + ``` ## Usage via GraphQL @@ -25,8 +41,8 @@ Local image paths from sources can also be compressed. Options like `width`, `he -query BlogPost ($path: String!) { - post: blogPost (path: $path) { +query BlogPost ($id: String!) { + post: blogPost (id: $id) { image (width: 720, height: 200, quality: 90) } } diff --git a/docs/install-plugins.md b/docs/install-plugins.md deleted file mode 100644 index 364de24dd..000000000 --- a/docs/install-plugins.md +++ /dev/null @@ -1,37 +0,0 @@ -# How to install plugins - -Gridsome plugins are npm packages. You install them with `npm` or `yarn` as dev dependencies like this: - -- With npm: `npm install @gridsome/source-wordpress -D` -- With Yarn: `yarn add @gridsome/source-wordpress -D` - -Then in your site’s `gridsome.config.js` you add an object to `plugins` with `use`, and `options` properies, where `use` will be `@gridsome/source-wordpress`: - -```js -module.exports = { - plugins: [ - { - use: '@gridsome/source-wordpress', - options: { - baseUrl: 'https://www.example.com', - typeName: 'WordPress' - } - } - ] -} -``` - -## Using local plugins - -Create a folder with an `index.js` file somewhere in your project. The `use` property can have an absolute path to the folder or a relative path starting with `~/`, where `~` is an alias for your project root folder. - -```js -module.exports = { - plugins: [ - { - use: '~/path/to/folder', - options: {} - } - ] -} -``` diff --git a/docs/jamstack.md b/docs/jamstack.md new file mode 100644 index 000000000..8f595a9b9 --- /dev/null +++ b/docs/jamstack.md @@ -0,0 +1,54 @@ +# What is the JAMstack? + +> JAMstack is a new way of building websites and apps that delivers better performance, higher security, lower cost of scaling, and a better developer experience. It's a Modern web development architecture based on client-side JavaScript, reusable APIs, and prebuilt Markup. + + +## Benefits + +### ⚡️ Better Performance + +Why wait for pages to build on the fly when you can generate them at deploy time? When it comes to minimizing the time to first byte, nothing beats pre-built files served over a CDN. + + +### ⚡️ Higher Security + +With server-side processes abstracted into microservice APIs, surface areas for attacks are reduced. You can also leverage the domain expertise of specialist third-party services. + + +### ⚡️ Cheaper, Easier Scaling + +When your deployment amounts to a stack of files that can be served anywhere, scaling is a matter of serving those files in more places. CDNs are perfect for this, and often include scaling in all of their plans. + + +### ⚡️ Better Developer Experience + +Loose coupling and separation of controls allow for more targeted development and debugging, and the expanding selection of CMS options for site generators remove the need to maintain a separate stack for content and marketing. + + +## Best practices +When building JAMstack projects, you can really get the most out of the stack if you stick to a few best practices. + + +### Entire Project on a CDN + +Because JAMstack projects don’t rely on server-side code, they can be distributed instead of living on a single server. Serving directly from a CDN unlocks speeds and performance that can’t be beat. The more of your app you can push to the edge, the better the user experience. + + +### Everything Lives in Git + +With a JAMstack project, anyone should be able to do a git clone, install any needed dependencies with a standard procedure (like npm install), and be ready to run the full project locally. **No databases to clone, no complex installs.** This reduces contributor friction, and also simplifies staging and testing workflows. + +### Modern Build Tools + +Take advantage of the world of modern build tools. It can be a jungle to get oriented in and it’s a fast moving space, but you’ll want to be able to use tomorrow’s web standards today without waiting for tomorrow’s browsers. And that currently means Babel, PostCSS, Webpack, and friends. + +### Automated Builds + +Because JAMstack markup is prebuilt, content changes won’t go live until you run another build. Automating this process will save you lots of frustration. You can do this yourself with webhooks, or use a publishing platform that includes the service automatically. + + + +## Learn more + +- Read more: [https://jamstack.org/](https://jamstack.org/) +- Videos: [https://jamstack.org/resources](https://jamstack.org/resources/) \ No newline at end of file diff --git a/docs/layouts.md b/docs/layouts.md index b76cceca6..6bc9bcd9d 100644 --- a/docs/layouts.md +++ b/docs/layouts.md @@ -32,12 +32,13 @@ When you have created a layout you need to import it to your pages and templates ``` @@ -107,13 +108,12 @@ This will pass a Prop to a layout with `sidebar = true`. In the **Layout compone ``` - ## Multiple content slots To add multiple slots to a layout you need to name them. In this example we have added a sidebar slot that will only show if the page has sidebar content. diff --git a/docs/linking.md b/docs/linking.md index 821382a9c..ee92a0fa4 100644 --- a/docs/linking.md +++ b/docs/linking.md @@ -1,7 +1,9 @@ # Linking -The `g-link` component is available globally in all your templates. It's a wrapper for [router-link](https://router.vuejs.org/api/#router-link-props) from Vue Router. +The `` component is available globally in all your Pages, Templates & Components. It's a wrapper for [router-link](https://router.vuejs.org/api/#router-link-props) from Vue Router. -`g-link` uses Intersection Observer to prefetch linked pages when link is in view. This makes browsing around in Gridsome very fast because the clicked page is already downloaded. + +### Smart link prefetching +`` uses Intersection Observer to prefetch linked pages when link is in view. **This makes browsing around in a Gridsome site very fast because the clicked page is already downloaded.** ## How to use @@ -15,16 +17,12 @@ To link to external links you need to use the normal `` ta ### Link to #anchor links To link to #anchor links you need to use the normal `` tag. - ## Options ```html About us - -About us - Read more ``` diff --git a/docs/pages-api.md b/docs/pages-api.md new file mode 100644 index 000000000..7f06e2c54 --- /dev/null +++ b/docs/pages-api.md @@ -0,0 +1,186 @@ +# Pages API + +The Pages API lets you create custom pages. This API is called after the GraphQL schema has been generated so you can query nodes and create pages from them or any other data. + +Start by using the `api.createPages()` hook in `gridsome.server.js`: + +```js +//gridsome.server.js +module.exports = function (api) { + api.createPages(({ createPage, graphql }) => { + // Create pages here + }) +} +``` + +## Create a page + +Use the `createPages` hook if you want to create pages. Pages created in this hook will be re-created and garbage collected occasionally. Use the `createManagedPages` below to have more controll over when pages are updated or deleted manually. + +### createPage(options) + +- options `object` + - **path** `string` *Required.* + - **component** `string` *Required.* + - context `object` *Optional context for the page and `page-query`.* + - queryVariables `object` *Optional context only for `page-query`.* + +```js +module.exports = function (api) { + api.createPages(({ createPage }) => { + createPage({ + path: '/my-page', + component: './src/templates/MyPage.vue' + }) + }) +} +``` + +## Create managed pages + +Pages created in the `createPages` hook will be re-created and garbage collected occasionally. That's why that hook is only able to create pages. You can use a `createManagedPages` hook to create, update and remove pages yourself. + +```js +module.exports = function (api) { + api.createManagedPages(({ createPage }) => { + createPage({ + path: '/my-page', + component: './src/templates/MyPage.vue' + }) + }) +} +``` + +### createPage(options) + +- options `object` + - **path** `string` *Required.* + - **component** `string` *Required.* + - context `object` *Optional context for the page and `page-query`.* + - queryVariables `object` *Optional context only for `page-query`.* + +Create a new page. + +### removePage(page) + +Removed a page created by `createPage`. + +### removePageByPath(path) + +Removes a page mathing the provided path. + +### removePagesByComponent(path) + +Removes all pages mathing the provided component path. + +### findAndRemovePages(query) + +Removes all pages mathing the provided query. + +### findPages(query) + +Returns all pages mathing the provided query. + +### findPage(query) + +Returns first pages mathing the provided query. + +## The page context + +Each page can have a context which will be available as variables for `page-query`. The context will also be available in the page component as `$context`. If you only want the context to be available for `page-query`, use the `queryVariables` option instead of `context`. + +##### Example usage + +```js +//gridsome.server.js +module.exports = function (api) { + api.createPages(({ createPage }) => { + createPage({ + path: '/my-page', + component: './src/templates/MyPage.vue', + context: { + customValue: '...' + } + }) + }) +} +``` + +Use the context in the page component or as variables in `page-query`. + +```html + + + +query MyPage($customValue: String) { + ... +} + +``` + +## Example usage + +### Create pages from GraphQL + +````js +//gridsome.server.js +module.exports = function (api) { + api.createPages(async ({ graphql, createPage }) => { + const { data } = await graphql(`{ + allProduct { + edges { + id + path + } + } + `) + + data.allProduct.edges.forEach(({ node }) => { + createPage({ + path: `${node.path}/reviews`, + component: './src/templates/ProductReviews.vue', + context: { + id: node.id + } + }) + }) + }) +} +```` + +### Create pages from external APIs + +We use `createManagedPages` in this example because we doesn't need the pages to be re-created on changes. The template also uses the context for rendering data instead of GraphQL results. + +```js +//gridsome.server.js +module.exports = function (api) { + api.createManagedPages(async ({ createPage }) => { + const { data } = await axios.get('https://api.example.com/posts') + + data.forEach(item => { + createPage({ + path: item.path, + component: './src/templates/Post.vue', + context: { + title: item.title, + content: item.content + } + }) + }) + }) +} +``` + +```html + +``` diff --git a/docs/pages.md b/docs/pages.md index f5019f04b..054c3d571 100644 --- a/docs/pages.md +++ b/docs/pages.md @@ -4,7 +4,7 @@ Pages are used for **normal pages** and for **listing & paginate GraphQL collect - If url will be `/about` use a **page** - If url will be `/blog` use a **page** -- If url will be `/blog/:slug` use a **[Template](/docs/templates)** +- If url will be `/blog/:slug` use a **[template](/docs/templates)** ## Creating pages @@ -33,6 +33,7 @@ A simple `Page.vue` file might look like this: -query Blog ($page: Int) { - allBlogPost (perPage: 10, page: $page) @paginate { +query Blog($page: Int) { + allBlogPost(perPage: 10, page: $page) @paginate { pageInfo { totalPages currentPage @@ -97,13 +97,12 @@ query Blog ($page: Int) { ``` -#### Options - |Property |Default| | |-----------------|-------|-| |info |*required* |Page info from GraphQL result with *totalPages* |showLinks |true |Show navigation links |showNavigation |true |Show previous and next links +|range |5|How many links to show |linkClass ||Add custom classes to the links |firstLabel |« |prevLabel |‹ diff --git a/docs/plugins.md b/docs/plugins.md index 14f691f3f..66ac890ec 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -1,26 +1,52 @@ # Plugins -Gridsome plugins are **npm packages** added to Gridsome projects. Plugin options are added to `gridsome.config.js`. -Here is an example of a data source plugin added to Gridsome: + +## Installing plugins + +Gridsome plugins are **npm packages**. Install a plugin with [npm](https://www.npmjs.com/get-npm) or [Yarn](https://yarnpkg.com). Then activate it by adding it to the `plugins` array in `gridsome.config.js`. A plugin entry in the `plugins` array can either be a string or an object. Use an object with `use` and `options` properties if the plugin requires options. The `use` property is the name of the plugin. + +Example commands for installing plugins: + +- npm: `npm install @gridsome/source-filesystem` +- Yarn: `yarn add @gridsome/source-filesystem` + +Here is an example which uses a source plugin with options: ```js module.exports = { - siteName: 'My Project', plugins: [ { use: '@gridsome/source-filesystem', options: { - index: ['README'], - path: 'docs/**/*.md', - typeName: 'DocPage', - remark: { - plugins: [ - '@gridsome/remark-prismjs' - ] - } + path: 'blog/**/*.md', + route: '/blog/:year/:month/:day/:slug', + typeName: 'Post' } - }, + } + ] +} +``` + +The following example uses a plugin with its default options: + +```js +module.exports = { + plugins: [ + '@gridsome/plugin-critical' ] } ``` -This data source plugin has been installed with the command line `yarn add @gridsome/source-filesystem`. +## Using local plugins + +Create a folder with an `index.js` file somewhere in your project. The `use` property can have an absolute path to the folder or a relative path starting with `~/`, where `~` is an alias for your project root folder. + +```js +module.exports = { + plugins: [ + { + use: '~/path/to/folder', + options: {} + } + ] +} +``` diff --git a/docs/querying-data.md b/docs/querying-data.md index 33bc1ba99..abfbf8ca8 100644 --- a/docs/querying-data.md +++ b/docs/querying-data.md @@ -40,22 +40,36 @@ query Posts { Every content type has a collection and a single entry in the GraphQL schema. You will notice that some of the root fields in your schema are prefixed with `all`. They are the collections for each of your content types and you can use them in your pages to create lists of single entries. -#### Arguments - | Argument | Default | Description | |----------|---------|-------------| | **sortBy** | `"date"` | Sort by a node field. | **order** | `DESC` | Sort order (`DESC` or `ASC`). -| **perPage** | `25` | How many nodes to get. +| **sort** | | Sort by multiple node fields. | **skip** | `0` | How many nodes to skip. -| **page** | `1` | Which page to get. +| **limit** | | How many nodes to get. +| **page** | | Which page to get. +| **perPage** | | How many nodes to show per page. Omitted if no `page` argument is provided. | **filter** | `{}` | [Read more](/docs/filtering-data). -#### Example query +#### Find nodes sorted by title + +```graphql +query Posts { + allPost(sortBy: "title", order: DESC) { + edges { + node { + title + } + } + } +} +``` + +#### Sort a collection by multiple fields ```graphql query Posts { - allPost (sortBy: "title", order: DESC, skip: 2) { + allPost(sort: [{ by: "featured" }, { by: "date" }]) { edges { node { title @@ -74,14 +88,12 @@ The other fields that do not start with `all` are your single entries. They are | Argument | Default | Description | |----------|---------|-------------| | **id** | `null` | Get node by `id`. -| **path** | `null` | Get node by `path`. -| **nullable** | `false` | Will return an error if not nullable. #### Example query ```graphql query Post { - post (id: "1") { + post(id: "1") { title } } @@ -125,13 +137,12 @@ query Blog { ``` - ## Query data in Templates Templates are used for page layout for the *single* endpoint of a data source like for example a WordPress blog post. If you have a node type called `WordPressPost`, then you can create a file in `src/templates/WordPressPost.vue`. -The `page-query` in templates also has a set of variables that can be used in the query. Available variables are `$id`, `$title`, `$slug`, `$path`, `$date` and any custom fields from the current `node`. Access field values in deep objects or arrays by separating properties or indexes with double underscores (`__`). +The `page-query` in templates also has a set of variables that can be used in the query. Any custom fields from the current `node` are available as variables. Access field values in deep objects or arrays by separating properties or indexes with double underscores (`__`). - `$id` resolves to `node.id` - `$value` resolves to `node.fields.value` @@ -153,12 +164,12 @@ The `page-query` in templates also has a set of variables that can be used in th -query Post ($id: String!, $group: String!) { - post (id: $id) { +query Post($id: String!, $group: String!) { + post(id: $id) { title content } - related: allPost (filter: { group: { eq: $group }}) { + related: allPost(filter: { group: { eq: $group }}) { edges { node { id @@ -173,18 +184,16 @@ query Post ($id: String!, $group: String!) { ## Query data in Components -Every **Component** can have a `` block with a GraphQL query -to fetch data from data sources. The results will be stored in a -`$static` property inside the component. A 'static-query' is named static as it can not accept any variables. +Every **Component** can have a `` block with a GraphQL query to fetch data from data sources. The results will be stored in a `$static` property inside the component. A `` is named static as it can not accept any variables. ```html -query Example { - example: examplePage (path: "/docs/example") { +query Post { + post(id: "1") { content } } diff --git a/docs/routing.md b/docs/routing.md index 83be6e436..a4b04c5f7 100644 --- a/docs/routing.md +++ b/docs/routing.md @@ -17,9 +17,13 @@ Examples: - `/src/pages/features/Awesome.vue` will be **/features/awesome** -## Routing for data source plugins +Learn more about [Pages](/docs/pages) -Data sources adds routing automatically with settings. These could be different for each plugin so check the plugin documentation how to use route. + + +## Routing for source plugins + +**Data source plugins** adds routing automatically with settings. These could be different for each plugin so check the plugin documentation how to use route. ```js module.exports = { @@ -36,17 +40,9 @@ module.exports = { } ``` -## Route params -Available route params are `:id`, `:title`, `:slug` and any custom fields from the current `node`. -The `node.date` field has a set of shorthand helpers; `:year`, `:month` and `:day`. Access field values in deep objects or arrays by separating properties or indexes with double underscores (`__`). Field values are slugified by default, but the original value will be available as **{fieldname}_raw**. - -- `:id` resolves to `node.id` -- `:value` resolves to `node.fields.value` -- `:value_raw` resolves to `node.fields.value` (Value without slugify) -- `:object__value` resolves to `node.fields.object.value` -- `:array__3__id` resolves to `node.fields.array[3].id` +> 💡 To create a template for the data source route you'll need to create a **[typeName].vue** file in **src/templates**. This will automatically be the template for this route. Learn more about [Templates] (/docs/templates) -## Routing for custom data sources +## Routing for custom data When you add a custom data source you need to use the `route` option inside `addContentType()` **OR** use `path` option inside `addNode()`. `route` will be used for all posts and `path` will be set per post. It's only possible to use one of them. If both are used `route` will be prioritized. Learn more about [fetching custom data here](/docs/fetching-data) @@ -74,3 +70,14 @@ module.exports = function (api) { } ``` +> 💡 To create a template for the data source route you'll need to create a **[typeName].vue** file in **src/templates**. This will automatically be the template for this route. Learn more about [Templates] (/docs/templates) + + +## Route params +Any custom field from the current `node` will be possible to use as route params. The `node.date` field has a set of shorthand helpers; `:year`, `:month` and `:day`. Access field values in deep objects or arrays by separating properties or indexes with double underscores (`__`). Field values are slugified by default, but the original value will be available as **{fieldname}_raw**. + +- `:id` resolves to `node.id` +- `:value` resolves to `node.value` *(slugified value)* +- `:value_raw` resolves to `node.value` *(original value)* +- `:object__value` resolves to `node.object.value` +- `:array__3__id` resolves to `node.array[3].id` diff --git a/docs/server-api.md b/docs/server-api.md index 7daa0518b..a24a0d53b 100644 --- a/docs/server-api.md +++ b/docs/server-api.md @@ -38,8 +38,6 @@ module.exports = MyPlugin Load data from local files or external APIs and create content types and nodes of it. The data will then be available in your GraphQL queries. -Usage: - ```js module.exports = function (api) { api.loadSource(store => { @@ -48,14 +46,58 @@ module.exports = function (api) { } ``` -[Read more about the Data Store API](/docs/data-store-api) +## api.createPages(fn) + +Create pages programmatically from nodes or other data. The handler for this hook will be re-executed when nodes are changed in store. Pages that are not re-created will be garbage collected. + +```js +module.exports = function (api) { + api.createPages(pages => { + // Create pages + }) +} +``` + +[Read more about the Pages API](/docs/pages-api) + +## api.createManagedPages(fn) + +Create, update and remove pages programmatically from nodes or other data. Unlike `createPages`, this hook will only run once and pages will not be garbage collected. + +```js +module.exports = function (api) { + api.createManagedPages(pages => { + // Create, update or remove pages + }) +} +``` + +[Read more about the Pages API](/docs/pages-api#create-managed-pages) + +## api.configureWebpack(fn) + +Configure the internal webpack config. + +The object will be merged with the internal config if it is an object. + +```js +api.configureWebpack({ + // add config here +}) +``` + +If the option is a function, it will get the internal config as its first argument. You can either modify the argument or return a new config object that will override the internal webpack config. + +```js +api.configureWebpack(config => { + return merge({ /* custom config */ }, config) +}) +``` ## api.chainWebpack(fn) A function that will receive an instance of ChainableConfig powered by [webpack-chain](https://github.com/neutrinojs/webpack-chain). -#### Usage - ```js api.chainWebpack(config => { // modify config here @@ -66,8 +108,6 @@ api.chainWebpack(config => { Gridsome runs an [Express](http://expressjs.com) server during development. Use this hook to add custom endpoints or configure the server. -#### Usage - ```js api.configureServer(app => { app.get('/my-endpoint', (req, res) => { @@ -82,30 +122,24 @@ Read more about the [Express Application API](https://expressjs.com/en/api.html# Create a custom GraphQL schema which will be merged with the Gridsome schema. -#### Usage - ```js -api.createSchema(graphql => { - return new graphql.GraphQLSchema({ +api.createSchema(({ addSchema, graphql }) => { + addSchema(new graphql.GraphQLSchema({ query: new graphql.GraphQLObjectType({ name: 'CustomRootQuery', fields: { // ... } - }) + })) }) }) ``` ## api.setClientOptions(options) -Set custom options for the client. Will use options from the plugin entry if not used. - -#### Arguments - - options `any` Any value which can be serialized by `JSON.stringify`. -#### Usage +Set custom options for the client. Will use options from the plugin entry if not used. ```js module.exports = function (api, options) { @@ -115,7 +149,6 @@ module.exports = function (api, options) { } ``` - ## api.beforeBuild(fn) ## api.afterBuild(fn) diff --git a/docs/taxonomies.md b/docs/taxonomies.md index fe287903c..83686997b 100644 --- a/docs/taxonomies.md +++ b/docs/taxonomies.md @@ -13,6 +13,7 @@ api.loadSource(store => { const posts = store.addContentType('Post') const tags = store.addContentType('Tag') + // makes all ids in the `tags` field reference a `Tag` posts.addReference('tags', 'Tag') tags.addNode({ @@ -23,9 +24,7 @@ api.loadSource(store => { posts.addNode({ id: '1', title: 'A post', - fields: { - tags: ['1'] - } + tags: ['1'] }) } ``` @@ -47,13 +46,13 @@ Now, we create a `Tag.vue` file in `src/templates` to have a template for our ta -query Tag ($id: String!) { - tag (id: $id) { +query Tag($id: String!) { + tag(id: $id) { title belongsTo { edges { node { - ...on Post { + ... on Post { id title path @@ -73,10 +72,10 @@ That's it! The tag page above will show a list of posts with links to them. Place the `@paginate` directive after the `belongsTo` field to activate pagination. The query will have a `$page` variable available to pass into the `belongsTo` `page` argument. ```graphql -query Tag ($id: String!, $page: Int) { - tag (id: $id) { +query Tag($id: String!, $page: Int) { + tag(id: $id) { title - belongsTo (page: $page) @paginate { + belongsTo(page: $page) @paginate { totalCount pageInfo { totalPages @@ -84,7 +83,7 @@ query Tag ($id: String!, $page: Int) { } edges { node { - ...on Post { + ... on Post { id title path @@ -96,14 +95,15 @@ query Tag ($id: String!, $page: Int) { } ``` - ## Arguments for `belongsTo` | Argument | Default | Description | |----------|---------|-------------| | **sortBy** | `"date"` | Sort by a node field. | **order** | `DESC` | Sort order (`DESC` or `ASC`). -| **perPage** | `25` | How many nodes to get. +| **sort** | | Sort by multiple node fields. | **skip** | `0` | How many nodes to skip. -| **page** | `1` | Which page to get. -| **filter** | `{}` | Filter nodes by `id`, `path` or `typeName`. +| **limit** | | How many nodes to get. +| **page** | | Which page to get. +| **perPage** | | How many nodes to show per page. Omitted if no `page` argument is provided. +| **filter** | `{}` | Filter nodes by `id` or `typeName`. diff --git a/docs/templates.md b/docs/templates.md index 017b13c72..351df18a4 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -12,7 +12,7 @@ The example shows a **Blog.vue** in **/pages** where Blog posts will be listed a ## Creating templates Templates must have a `` block which fetches the source node -for the current page. You can use the `$path` variable to get the node. +for the current page. You can use the `$id` variable to get the node. ```html @@ -24,8 +24,8 @@ for the current page. You can use the `$path` variable to get the node. -query Post ($path: String!) { - post: wordPressPost (path: $path) { +query Post ($id: String!) { + post: wordPressPost (id: $id) { title content } @@ -34,6 +34,7 @@ query Post ($path: String!) { diff --git a/examples/g-image.md b/examples/g-image.md deleted file mode 100644 index 0c39189ae..000000000 --- a/examples/g-image.md +++ /dev/null @@ -1,7 +0,0 @@ -```html - -``` - -```html - -``` diff --git a/examples/graphql-for-data.md b/examples/graphql-for-data.md new file mode 100644 index 000000000..ebec61f1f --- /dev/null +++ b/examples/graphql-for-data.md @@ -0,0 +1,33 @@ +--- +title: Build with Vue.js & GraphQL +filepath: src/pages/Blog.vue +filetype: vue +order: 1 +--- +```html + + + + +query Posts { + allPost { + edges { + node { + id + title + path + } + } + } +} + + +``` diff --git a/examples/graphql.md b/examples/graphql.md deleted file mode 100644 index 237a6c797..000000000 --- a/examples/graphql.md +++ /dev/null @@ -1,25 +0,0 @@ -```html - - - -query Blog { - posts: allWordPressPost (limit: 5) { - edges { - node { - id - title - } - } - } -} - -``` diff --git a/examples/markdown-for-content.md b/examples/markdown-for-content.md new file mode 100644 index 000000000..9c28dd35e --- /dev/null +++ b/examples/markdown-for-content.md @@ -0,0 +1,31 @@ +--- +title: Use Markdown for data +filepath: gridsome.config.js +filetype: js +order: 4 +--- +```js +module.exports = { + siteName: 'My Markdown Blog', + siteDescription: 'Gridsome 💚 Markdown', + plugins: [ + { + // Example usage of Markdown for content + use: '@gridsome/source-filesystem', + options: { + path: 'posts/**/*.md', // What files to use + typeName: 'Post', // GraphQL type name + route: '/posts/:year/:title' // Create routes + refs: { + // Create relation between a "tags" field. + tags: { + typeName: 'Tag', + route: '/tags/:title', + create: true + } + } + } + }, + ] +} +``` \ No newline at end of file diff --git a/examples/pagination-support.md b/examples/pagination-support.md new file mode 100644 index 000000000..fb22af9f4 --- /dev/null +++ b/examples/pagination-support.md @@ -0,0 +1,47 @@ +--- +title: Built-in Pagination support +filepath: src/pages/Blog.vue +filetype: vue +order: 5 +--- +```html + + + +query Posts ($page: Int) { + allPost (perPage: 10, page: $page) @paginate { + pageInfo { + totalPages + currentPage + } + edges { + node { + id + title + } + } + } +} + + + +``` diff --git a/examples/progressive-images.md b/examples/progressive-images.md new file mode 100644 index 000000000..ea8a289c2 --- /dev/null +++ b/examples/progressive-images.md @@ -0,0 +1,21 @@ +--- +title: Progressive Images +filepath: src/components/AnyVueComponent.vue +filetype: vue +order: 10 +--- +```html + +``` diff --git a/examples/taxonomy-pages.md b/examples/taxonomy-pages.md new file mode 100644 index 000000000..17c66b41c --- /dev/null +++ b/examples/taxonomy-pages.md @@ -0,0 +1,43 @@ +--- +title: Taxonomy pages +filepath: src/templates/Tag.vue +filetype: vue +order: 5 +--- +```html + + + +query Tag ($id: String!) { + tag (id: $id) { + title + belongsTo { + edges { + node { + ...on Post { + id + title + path + } + } + } + } + } +} + + +``` diff --git a/examples/template-system.md b/examples/template-system.md new file mode 100644 index 000000000..cd478ec1a --- /dev/null +++ b/examples/template-system.md @@ -0,0 +1,37 @@ +--- +title: Smart Templates +filepath: src/templates/Post.vue +filetype: vue +order: 4 +--- +```html + + + +query Post ($id: String!) { + post (id: $id) { + title + content + } +} + + + + + +``` diff --git a/examples/templates.md b/examples/templates.md deleted file mode 100644 index 66a10897f..000000000 --- a/examples/templates.md +++ /dev/null @@ -1,21 +0,0 @@ -```html - - - - - -query WordPressPost ($path: String!) { - post: wordPressPost (path: $path) { - title - content - } -} - -``` diff --git a/gridsome.config.js b/gridsome.config.js index 58618d8b5..4980e5ad9 100644 --- a/gridsome.config.js +++ b/gridsome.config.js @@ -4,7 +4,7 @@ module.exports = { siteName: 'Gridsome', siteUrl: `https://www.gridsome.org`, titleTemplate: '%s - Gridsome', - siteDescription: 'Gridsome is a Vue-powered static site generator for building CDN-ready websites and apps for any headless CMS, local files or APIs', + siteDescription: 'Gridsome is a Vue.js-powered modern website generator that makes it easy and fun for developers to create beautiful JAMstack websites & PWAs that are fast by default.', chainWebpack(config, { isServer }) { config.module.rules.delete('svg') @@ -63,25 +63,6 @@ module.exports = { } } }, - { - use: '@gridsome/source-filesystem', - options: { - index: ['README'], - path: 'learn/**/*.md', - typeName: 'LearnPage', - remark: { - autolinkHeadings: { - content: { - type: 'text', - value: '#' - } - }, - plugins: [ - '@gridsome/remark-prismjs' - ] - } - } - }, { use: '@gridsome/source-filesystem', options: { @@ -101,7 +82,7 @@ module.exports = { path: './blog/*/index.md', route: '/blog/:year/:month/:day/:slug', refs: { - author: 'Author' + author: 'Contributor' }, remark: { plugins: [ @@ -110,5 +91,38 @@ module.exports = { } } } + ], + // Create routes from GraphQL nodes + nodeRoutes: { + WordPressPost: '/:year/:month/:slug', + WordPressTag: '/tag/:slug', + CustomType: { + route: '/other/:custom/:type', + component: './src/templates/MyTemplate.vue', // default templates/typeName.vue + nextFieldName: 'nextPost', + prevFieldName: 'prevPost', + sortBy: 'date' + }, + Author: [ + { + route: '/author/:name', + component: './src/templates/Author.vue' + }, + { + route: '/author/:name/starters', + component: './src/templates/AuthorStarters.vue' + } + ] + }, + // Create dynamic routes + clientRoutes: [ + { + path: '/foo/', + component: './src/templates/MyTemplate.vue', + }, + { + path: '/bar/:id', + component: './src/templates/MyTemplate.vue', + } ] } diff --git a/gridsome.server.js b/gridsome.server.js index 541a05043..158cf7c07 100644 --- a/gridsome.server.js +++ b/gridsome.server.js @@ -24,14 +24,13 @@ module.exports = function (api) { }) .addNode({ id: '1' }) - - // authors - const authorsPath = path.join(__dirname, 'blog/authors/authors.yaml') + // contributors + const authorsPath = path.join(__dirname, 'contributors/contributors.yaml') const authorsRaw = await fs.readFile(authorsPath, 'utf8') const authorsJson = yaml.safeLoad(authorsRaw) const authors = store.addContentType({ - typeName: 'Author', - route: '/author/:id' + typeName: 'Contributor', + route: '/contributor/:id' }) authorsJson.forEach(({ id, name: title, ...fields }) => { @@ -43,7 +42,53 @@ module.exports = function (api) { origin: authorsPath } }) - }) + }) + + // Starters + const startersPath = path.join(__dirname, 'starters/starters.yaml') + const startersRaw = await fs.readFile(startersPath, 'utf8') + const startersJson = yaml.safeLoad(startersRaw) + const starters = store.addContentType({ + typeName: 'Starter', + route: '/starters/:title' + }) + + // Connect author field to Contributors & Platforms + starters.addReference('author','Contributor') + starters.addReference('platforms','Platform') + + startersJson.forEach(({ id, name: title, ...fields }) => { + starters.addNode({ + id, + title, + fields, + internal: { + origin: startersPath + } + }) + }) + + // Platforms + const platformsPath = path.join(__dirname, 'platforms/platforms.yaml') + const platformsRaw = await fs.readFile(platformsPath, 'utf8') + const platformsJson = yaml.safeLoad(platformsRaw) + const platforms = store.addContentType({ + typeName: 'Platform', + route: '/starters/platform/:id' + }) + + // Connect author field to Contributors + platformsJson.forEach(({ id, name: title, ...fields }) => { + platforms.addNode({ + id, + title, + fields, + internal: { + origin: platformsPath + } + }) + }) + }) api.afterBuild(async ({ config }) => { diff --git a/package.json b/package.json index 385035d85..3454c3e87 100644 --- a/package.json +++ b/package.json @@ -11,21 +11,21 @@ "@gridsome/plugin-google-analytics": "^0.1.0", "algoliasearch": "^3.32.0", "babel-runtime": "^6.26.0", + "clipboard-copy": "^3.0.0", "docsearch.js": "^2.6.2", - "gridsome": "0.5.4", + "gridsome": "^0.6.0", "marked": "^0.6.1", - "typeface-league-spartan": "0.0.44", - "typeface-nunito": "0.0.54", "typography": "^0.16.18", "vue-instantsearch": "^2.0.0", + "vue-popover": "^1.6.2", "vue-scrollto": "^2.13.0", "vue-typer": "^1.2.0" }, "devDependencies": { - "@gridsome/plugin-critical": "^0.1.1", + "@gridsome/plugin-critical": "^0.1.2", "@gridsome/remark-prismjs": "^0.0.4", - "@gridsome/source-filesystem": "^0.3.0", - "@gridsome/transformer-remark": "^0.2.1", + "@gridsome/source-filesystem": "^0.4.0", + "@gridsome/transformer-remark": "^0.3.0", "execa": "^1.0.0", "fs-extra": "^7.0.1", "js-yaml": "^3.12.1", diff --git a/platforms/logos/Page 1.svg b/platforms/logos/Page 1.svg new file mode 100755 index 000000000..bb1ff6d07 --- /dev/null +++ b/platforms/logos/Page 1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/platforms/logos/airtable.svg b/platforms/logos/airtable.svg new file mode 100755 index 000000000..381a69690 --- /dev/null +++ b/platforms/logos/airtable.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/platforms/logos/contentful.svg b/platforms/logos/contentful.svg new file mode 100755 index 000000000..8659828f0 --- /dev/null +++ b/platforms/logos/contentful.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/platforms/logos/cosmicjs.svg b/platforms/logos/cosmicjs.svg new file mode 100755 index 000000000..fa20089fa --- /dev/null +++ b/platforms/logos/cosmicjs.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/logos/craftcms.svg b/platforms/logos/craftcms.svg new file mode 100644 index 000000000..798bea1ff --- /dev/null +++ b/platforms/logos/craftcms.svg @@ -0,0 +1,4 @@ + + + + diff --git a/platforms/logos/dato.svg b/platforms/logos/dato.svg new file mode 100755 index 000000000..268ae6167 --- /dev/null +++ b/platforms/logos/dato.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/platforms/logos/drupal.svg b/platforms/logos/drupal.svg new file mode 100755 index 000000000..f350fe157 --- /dev/null +++ b/platforms/logos/drupal.svg @@ -0,0 +1,3 @@ + + + diff --git a/platforms/logos/ghost.svg b/platforms/logos/ghost.svg new file mode 100755 index 000000000..824573b66 --- /dev/null +++ b/platforms/logos/ghost.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/platforms/logos/graphcms.svg b/platforms/logos/graphcms.svg new file mode 100755 index 000000000..6c6aaa21c --- /dev/null +++ b/platforms/logos/graphcms.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/platforms/logos/markdown.svg b/platforms/logos/markdown.svg new file mode 100755 index 000000000..0ab9aab3f --- /dev/null +++ b/platforms/logos/markdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/platforms/logos/prismic.svg b/platforms/logos/prismic.svg new file mode 100755 index 000000000..73dbbff54 --- /dev/null +++ b/platforms/logos/prismic.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/platforms/logos/sanity.svg b/platforms/logos/sanity.svg new file mode 100755 index 000000000..7ee99398a --- /dev/null +++ b/platforms/logos/sanity.svg @@ -0,0 +1,4 @@ + + + + diff --git a/platforms/logos/shopify.svg b/platforms/logos/shopify.svg new file mode 100644 index 000000000..1e7286d2b --- /dev/null +++ b/platforms/logos/shopify.svg @@ -0,0 +1,4 @@ + + + + diff --git a/platforms/logos/storyblok.svg b/platforms/logos/storyblok.svg new file mode 100755 index 000000000..803dafca5 --- /dev/null +++ b/platforms/logos/storyblok.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/platforms/logos/strapi.svg b/platforms/logos/strapi.svg new file mode 100755 index 000000000..073cd592e --- /dev/null +++ b/platforms/logos/strapi.svg @@ -0,0 +1,3 @@ + + + diff --git a/platforms/logos/wordpress.svg b/platforms/logos/wordpress.svg new file mode 100755 index 000000000..ec7005bd1 --- /dev/null +++ b/platforms/logos/wordpress.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/platforms/platforms.yaml b/platforms/platforms.yaml new file mode 100644 index 000000000..8eafa6eb7 --- /dev/null +++ b/platforms/platforms.yaml @@ -0,0 +1,55 @@ +- id: markdown + title: Markdown + logo: ./logos/markdown.svg + +- id: wordpress + title: WordPress + logo: ./logos/wordpress.svg + +- id: contentful + title: Contentful + logo: ./logos/contentful.svg + +- id: drupal + title: Drupal + logo: ./logos/drupal.svg + +- id: sanity + title: Sanity + logo: ./logos/sanity.svg + +- id: dato-cms + title: Dato CMS + logo: ./logos/dato.svg + +- id: primic + title: Prismic + logo: ./logos/prismic.svg + +- id: storyblok + title: Storyblok + logo: ./logos/storyblok.svg + +- id: cosmicjs + title: Cosmic JS + logo: ./logos/cosmicjs.svg + +- id: ghost + title: Ghost + logo: ./logos/ghost.svg + +- id: shopify + title: Shopify + logo: ./logos/shopify.svg + +- id: airtable + title: Airtable + logo: ./logos/airtable.svg + +- id: craft-cms + title: Craft CMS + logo: ./logos/craftcms.svg + +- id: strapi + title: Strapi + logo: ./logos/strapi.svg \ No newline at end of file diff --git a/src/assets/fonts/Jost-600-Semi.otf b/src/assets/fonts/Jost-600-Semi.otf new file mode 100755 index 000000000..07aaadeac Binary files /dev/null and b/src/assets/fonts/Jost-600-Semi.otf differ diff --git a/src/assets/fonts/Jost-600-SemiItalic.otf b/src/assets/fonts/Jost-600-SemiItalic.otf new file mode 100755 index 000000000..9d2236d4b Binary files /dev/null and b/src/assets/fonts/Jost-600-SemiItalic.otf differ diff --git a/src/assets/fonts/Jost-700-Bold.otf b/src/assets/fonts/Jost-700-Bold.otf new file mode 100755 index 000000000..9cb55355b Binary files /dev/null and b/src/assets/fonts/Jost-700-Bold.otf differ diff --git a/src/assets/fonts/Jost-700-BoldItalic.otf b/src/assets/fonts/Jost-700-BoldItalic.otf new file mode 100755 index 000000000..b1b0f503a Binary files /dev/null and b/src/assets/fonts/Jost-700-BoldItalic.otf differ diff --git a/src/assets/images/cms-logos.png b/src/assets/images/cms-logos.png deleted file mode 100644 index 36c766603..000000000 Binary files a/src/assets/images/cms-logos.png and /dev/null differ diff --git a/src/assets/images/connect-bg.png b/src/assets/images/connect-bg.png new file mode 100644 index 000000000..c1b385920 Binary files /dev/null and b/src/assets/images/connect-bg.png differ diff --git a/src/assets/images/connect-dots.svg b/src/assets/images/connect-dots.svg new file mode 100644 index 000000000..ff9a92b69 --- /dev/null +++ b/src/assets/images/connect-dots.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/connect-logos-2.png b/src/assets/images/connect-logos-2.png new file mode 100644 index 000000000..9b0de6ea2 Binary files /dev/null and b/src/assets/images/connect-logos-2.png differ diff --git a/src/assets/images/connect-logos-3.png b/src/assets/images/connect-logos-3.png new file mode 100644 index 000000000..beb085170 Binary files /dev/null and b/src/assets/images/connect-logos-3.png differ diff --git a/src/assets/images/connect-logos.png b/src/assets/images/connect-logos.png new file mode 100644 index 000000000..b8c062435 Binary files /dev/null and b/src/assets/images/connect-logos.png differ diff --git a/src/assets/images/deploy-logos.png b/src/assets/images/deploy-logos.png deleted file mode 100644 index f8e67c77e..000000000 Binary files a/src/assets/images/deploy-logos.png and /dev/null differ diff --git a/src/assets/images/graphql-browser.png b/src/assets/images/graphql-browser.png deleted file mode 100644 index 8b5d9550b..000000000 Binary files a/src/assets/images/graphql-browser.png and /dev/null differ diff --git a/src/assets/images/graphql-logo.svg b/src/assets/images/graphql-logo.svg index 657306775..17623f6ec 100644 --- a/src/assets/images/graphql-logo.svg +++ b/src/assets/images/graphql-logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/images/gridsome-logo.svg b/src/assets/images/gridsome-logo.svg index 6507cc749..8a4356a3a 100644 --- a/src/assets/images/gridsome-logo.svg +++ b/src/assets/images/gridsome-logo.svg @@ -1,31 +1,25 @@ - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/src/assets/images/gridsome-small-white.svg b/src/assets/images/gridsome-small-white.svg new file mode 100644 index 000000000..d845c945c --- /dev/null +++ b/src/assets/images/gridsome-small-white.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/images/icon-blog.svg b/src/assets/images/icon-blog.svg index ed7fdfdd5..ca66175e7 100644 --- a/src/assets/images/icon-blog.svg +++ b/src/assets/images/icon-blog.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + + + + + diff --git a/src/assets/images/icon-clipboard.svg b/src/assets/images/icon-clipboard.svg new file mode 100644 index 000000000..ccee454d8 --- /dev/null +++ b/src/assets/images/icon-clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/icon-docs.svg b/src/assets/images/icon-docs.svg old mode 100644 new mode 100755 index 4197ddd40..6a23f4717 --- a/src/assets/images/icon-docs.svg +++ b/src/assets/images/icon-docs.svg @@ -1 +1,11 @@ - \ No newline at end of file + + + + + + + + + + + diff --git a/src/assets/images/icon-platform.svg b/src/assets/images/icon-platform.svg new file mode 100755 index 000000000..8b958b8ed --- /dev/null +++ b/src/assets/images/icon-platform.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon-plugins.svg b/src/assets/images/icon-plugins.svg old mode 100644 new mode 100755 index 8fdafa934..157a78572 --- a/src/assets/images/icon-plugins.svg +++ b/src/assets/images/icon-plugins.svg @@ -1 +1,12 @@ - \ No newline at end of file + + + + + + + + + + + + diff --git a/src/assets/images/icon-starters.svg b/src/assets/images/icon-starters.svg new file mode 100755 index 000000000..951c258ed --- /dev/null +++ b/src/assets/images/icon-starters.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/logo-algolia.svg b/src/assets/images/logo-algolia.svg new file mode 100644 index 000000000..39b00e4ea --- /dev/null +++ b/src/assets/images/logo-algolia.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/logo-codesandbox.svg b/src/assets/images/logo-codesandbox.svg new file mode 100644 index 000000000..e5a002042 --- /dev/null +++ b/src/assets/images/logo-codesandbox.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/logo-netlify-small.svg b/src/assets/images/logo-netlify-small.svg new file mode 100644 index 000000000..cd1d32e73 --- /dev/null +++ b/src/assets/images/logo-netlify-small.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/logo-netlify.svg b/src/assets/images/logo-netlify.svg new file mode 100644 index 000000000..096d51265 --- /dev/null +++ b/src/assets/images/logo-netlify.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/perfmatters.svg b/src/assets/images/perfmatters.svg new file mode 100644 index 000000000..df6c10b85 --- /dev/null +++ b/src/assets/images/perfmatters.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/plugins.png b/src/assets/images/plugins.png deleted file mode 100644 index 3f470757f..000000000 Binary files a/src/assets/images/plugins.png and /dev/null differ diff --git a/src/assets/images/spark.png b/src/assets/images/spark.png new file mode 100644 index 000000000..e95acf490 Binary files /dev/null and b/src/assets/images/spark.png differ diff --git a/src/assets/images/static.png b/src/assets/images/static.png deleted file mode 100644 index 77ef55b96..000000000 Binary files a/src/assets/images/static.png and /dev/null differ diff --git a/src/assets/images/vue-browser.png b/src/assets/images/vue-browser.png deleted file mode 100644 index aa192edbe..000000000 Binary files a/src/assets/images/vue-browser.png and /dev/null differ diff --git a/src/assets/images/vue-logo.svg b/src/assets/images/vue-logo.svg index 95ac148f7..72218d884 100644 --- a/src/assets/images/vue-logo.svg +++ b/src/assets/images/vue-logo.svg @@ -1,28 +1,4 @@ -image/svg+xml \ No newline at end of file + + + + diff --git a/src/assets/style/animations.scss b/src/assets/style/animations.scss index baeba2128..e362afb33 100644 --- a/src/assets/style/animations.scss +++ b/src/assets/style/animations.scss @@ -10,55 +10,63 @@ } } - @keyframes pulse { 0% { - box-shadow: 0 0 0 0 var(--primary-link-color); - } - 70% { - box-shadow: 0 0 0 10px transparent; - + transform: scale(1); } 100% { - box-shadow: 0 0 0 0 transparent; - + transform: scale(2); + opacity: 0; } } -@keyframes pulseSvg { +@keyframes bounce { 0% { - filter: drop-shadow( 0px 0px 10px transparent ); + transform: scale(1); } - 70% { - filter: drop-shadow( 0px 0px 10px var(--primary-link-color) ); + 50% { + transform: scale(1.05); } 100% { - filter: drop-shadow( 0px 0px 10px transparent ); - + transform: scale(1); } } - -@keyframes pulseAlt { - 0% { - box-shadow: 0 0 0 0 #E535AB; +@keyframes stroke { + to{ + stroke-dashoffset:-30; + transform: translateZ(0); } - 70% { - box-shadow: 0 0 0 10px transparent; +} + +@keyframes spin { + from { + transform: rotate(360deg); } - 100% { - box-shadow: 0 0 0 0 transparent; + to { + transform: rotate(0deg); } } - -@keyframes stroke { - to{stroke-dashoffset:-20;} +@keyframes moveInOutX { + from { + transform: translateX(-10px); + } + to { + transform: translateX(5px); + } } -@keyframes stroke-invert { - to{stroke-dashoffset:20;} +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-5px); + } + to { + opacity: 1; + transform: translateY(0px); + } } diff --git a/src/assets/style/base.scss b/src/assets/style/base.scss index 3ac9e7f99..06b92087b 100644 --- a/src/assets/style/base.scss +++ b/src/assets/style/base.scss @@ -1,5 +1,5 @@ html, body { - background-color: #fff; + background-color: var(--light-bg); touch-action: manipulation; width: 100%; } diff --git a/src/assets/style/button.scss b/src/assets/style/button.scss index c93c52198..0282305f0 100644 --- a/src/assets/style/button.scss +++ b/src/assets/style/button.scss @@ -1,11 +1,30 @@ .button { display: inline-flex; - align-items:center; - padding: .34rem 1rem; - font-weight: bold; + z-index: 1; + align-items: center; + padding: .34rem 1.2rem; + font-weight: 500; letter-spacing: .5px; position: relative; + border:0; + text-decoration: none; + overflow: hidden; border-radius: 5px; + color: var(--primary-color); + transition: transform .2s, background .3s, box-shadow .3s, color .3s; + + > span { + z-index: 2; + } + + > svg + span { + margin-left: .25rem; + } + + &:hover { + box-shadow: var(--glow); + transform: translateY(-1px); + } &:after { content: " "; @@ -16,8 +35,13 @@ position: absolute; border: 1px solid currentColor; border-radius: 5px; - transition: opacity .3s; + transition: opacity .3s, box-shadow .3s; opacity: .6; + box-shadow: inset -1px -2px 0px 0px rgba(0,0,0,.05); + } + + &:active:after { + box-shadow: none; } &:hover:after { @@ -26,16 +50,29 @@ &.primary { color:#FFF; - background-color: var(--primary-link-color); - box-shadow: 1px 1px 10px 0 var(--success-bg); - + background-color: var(--primary-color-dark); &:after { border-color: rgba(0,0,0,.1); } - &:hover { - color:#FFF; + + &:before { + content: ''; + z-index: -1; + position: absolute; + bottom: 100%; + right: 100%; + width: 1em; + height: 1em; + border-radius: 50%; background-color: var(--primary-color); - box-shadow: 1px 1px 20px 0 var(--success-bg); + transform-origin: center; + transform: translate(50%, 50%) scale(0); + transition: transform .2s ease-in-out; + } + + &:focus:before, + &:hover:before { + transform: translate(50%, 50%) scale(20); } } @@ -43,13 +80,30 @@ font-size: .85rem; padding: .35rem .7rem; } + &--xsmall { + font-size: .7rem; + padding: .3rem .7rem; + } + + &--blank { + background-color: transparent; + border-color: transparent; + padding-left:0; + padding-right:0; + box-shadow: none!important; + + &:not(:hover) { + color: rgba(0,0,0,.5); + } + &:after { + display: none; + } + } &--large { font-size: 1.2rem; - padding: .4rem 1.2rem; + padding: .5rem 1.5rem; } - + .button { - margin-left: var(--space); - } + } diff --git a/src/assets/style/code.scss b/src/assets/style/code.scss index 56b3b5046..786642c63 100644 --- a/src/assets/style/code.scss +++ b/src/assets/style/code.scss @@ -1,59 +1,171 @@ -#app pre, -#app code { - font-family: Space Mono,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace; - background-color: var(--code-color); - border-radius: 3px; - font-feature-settings: "liga" 0; +h1, h2, h3, h4 { + code { + font-size: 0.85em; + } } -#app pre { - border: 1px solid rgba(0,0,0,.05); - background-color: var(--code-color-block); +#app { + pre, + code { + font-family: Space Mono,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace; + font-feature-settings: "liga" 0; + border-radius: 3px; + line-height: 1.5; + direction: ltr; + text-align: left; + white-space: pre-wrap; + word-spacing: normal; + word-break: normal; + tab-size: 2; + hyphens: none; + color: #d8d8d8; + } + + pre::-moz-selection, pre ::-moz-selection, + code::-moz-selection, code ::-moz-selection { + text-shadow: none; + } + + pre::selection, pre ::selection, + code::selection, code ::selection { + text-shadow: none; + } + + // Code blocks + pre { + padding: 1.0em; + margin: .5em 0; + overflow: auto; + margin-bottom: var(--space); + background-color: var(--code-bg); + } + + // Inline code + :not(pre) > code { + border-radius: .3em; + color: #405a74; + padding: 0.25rem 0.5rem; + font-size: .8em; + background-color: var(--inline-code-bg); + } } +// HTML code +.language-html { + font-variant-ligatures: none; +} -#app pre code { - background-color: transparent; +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #585858; } +.token.punctuation { + color: #d8d8d8; +} -#app pre { - // fix for accessbility +.token.namespace { + opacity: .7; +} + +.token.operator {} + +.token.boolean, +.token.number { + color: #e2777a; +} + +.token.string { + color: #7ec699; +} + +.token.selector { + color: #ba8baf; +} + +.language-css { + color: #7ec699; .token.punctuation { - color: #5f5f5f; + color: #f8f8f8; } - .token.attr-name, - .token.builtin, - .token.char, - .token.inserted, - .token.selector, - .token.string { - color: #007900; + .token.property { + color: #d8d8d8; } } -// code blocks -#app pre { - padding: 1em 0 1em 1em; - margin-bottom: 2.0em; - overflow-x: auto; +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #86c1b9; } -// HTML code blocks -#app pre.language-html { - font-variant-ligatures: none; +.token.control, +.token.directive, +.token.unit { + color: #7ec699; } -// inline code -#app code { - padding: 0.2em 0.5em; - overflow-wrap: break-word; - word-wrap: break-word; +.token.keyword { + color: #cc99cd; } -h1, h2, h3, h4 { - code { - font-size: 0.85em; +.token.function { + color: #dd9a6d; +} + +.token.tag { + color: #e2777a; + .token.punctuation { + color: rgba(#e2777a, 0.75); } } +.token.attr-name { + color: lighten(#e2777a, 7); +} +.token.attr-value { + color: #7ec699; + .token.punctuation { + color: #f8f8f8; + } +} + +.token.statement, +.token.regex, +.token.atrule { + color: #86c1b9; +} + +.token.placeholder, +.token.variable { + color: #7cafc2; +} + +.token.deleted { + text-decoration: line-through; +} + +.token.inserted { + border-bottom: 1px dotted #f8f8f8; + text-decoration: none; +} + +.token.italic { + font-style: italic; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.important { + color: #ab4642; +} + +.token.entity { + cursor: help; +} diff --git a/src/assets/style/docs.scss b/src/assets/style/docs.scss index 9f5ee7dc2..54323369c 100644 --- a/src/assets/style/docs.scss +++ b/src/assets/style/docs.scss @@ -3,7 +3,7 @@ max-width: 100%; ul li { - margin-bottom: .2rem; + margin-bottom: .5rem; } p > img { @@ -30,7 +30,7 @@ text-align: center; width: 0.8em; opacity: 0.0; - color: var(--primary-link-color); + color: var(--primary-color-dark); box-shadow: none; background: none; @@ -88,7 +88,7 @@ padding-top: 1rem; &:not(:hover) { - color: var(--primary-link-color); + color: var(--primary-color-dark); } svg { @@ -110,6 +110,6 @@ &__link { padding: 0; - color: var(--primary-link-color); + color: var(--primary-color-dark); } } diff --git a/src/assets/style/dots-bg.scss b/src/assets/style/dots-bg.scss index 15224573c..8169b73ef 100644 --- a/src/assets/style/dots-bg.scss +++ b/src/assets/style/dots-bg.scss @@ -1,7 +1,7 @@ .dots-bg { background-position: 0 100%; background-repeat: repeat-x; - background-size: 16px; + background-size: 14px; background-image: url('data:image/svg+xml;utf8, '); &--animated { diff --git a/src/assets/style/forms.scss b/src/assets/style/forms.scss index 8944a227f..e7b81326c 100644 --- a/src/assets/style/forms.scss +++ b/src/assets/style/forms.scss @@ -2,15 +2,17 @@ input { width: 100%; padding: .34rem .8rem; border-radius: 5px; - border: 1px solid var(--border-color); - + border: 1px solid var(--border-color-darker); + transition: background .3s, box-shadow .3s; + &:focus { outline: 0; - border-color: rgba(0,0,0,.3); + border-color: var(--border-color-darker); + background-color: #FFF; } &[type="search"] { padding-right: 1rem; - background: 93% 48% no-repeat #FFF url('data:image/svg+xml;utf8,'); + background: 93% 48% no-repeat #FFF url('data:image/svg+xml;utf8,'); } } diff --git a/src/assets/style/grid.scss b/src/assets/style/grid.scss index e734b354e..489db302a 100644 --- a/src/assets/style/grid.scss +++ b/src/assets/style/grid.scss @@ -2,12 +2,18 @@ display: flex; align-items: center; flex-wrap: wrap; - + > *:last-child { margin-left: 0; margin-right: 0; } + > li { + list-style: none; + padding: 0; + margin-left: 0; + } + &--center { justify-content: center; } @@ -28,6 +34,10 @@ } } +ul.flex { + margin: 0; +} + .gap { &-15 { > * { @@ -69,27 +79,55 @@ .grid-cols{ display: grid; - grid-template-columns: repeat(3, 1fr); - grid-gap: calc( var(--space) * 3); + grid-template-columns: repeat( auto-fit, minmax(26%, 1fr) ); + + grid-gap: var(--space); + + &--collapse { + grid-gap: 0; + + > div:first-child { + border-right:0; + border-top-right-radius: 0; + border-bottom-right-radius:0; + } + > div:last-child { + border-left-width:0; + border-top-left-radius: 0; + border-bottom-left-radius:0; + } + } &--gap-small { - grid-gap: calc( var(--space) ); + grid-gap: var(--space); + } + + &--gap-large { + grid-gap: var(--space-x2); } &--2 { - grid-template-columns: repeat(2, 1fr); + grid-template-columns: repeat( auto-fit, minmax(36%, 1fr) ); } &--4 { - grid-template-columns: repeat(4, 1fr); + grid-template-columns: repeat( auto-fit, minmax(20%, 1fr) ); + } + + &--5 { + grid-template-columns: repeat( auto-fit, minmax(10%, 1fr) ); } } -@media screen and (max-width: 750px) { +@media screen and (max-width: 850px) { .grid-cols{ grid-template-columns: repeat(2, 1fr); } + + .grid-cols--full-md{ + grid-template-columns: repeat(1, 1fr); + } } @media screen and (max-width: 550px) { diff --git a/src/assets/style/layouts.scss b/src/assets/style/layouts.scss index e3797e154..286e1f175 100644 --- a/src/assets/style/layouts.scss +++ b/src/assets/style/layouts.scss @@ -1,6 +1,7 @@ .container { - max-width: 1280px; + max-width: 1300px; width: 100%; + position: relative; &, &-md, @@ -13,7 +14,7 @@ width: 100%; } - &-md { max-width: 720px } + &-md { max-width: 760px } &-sm { max-width: 630px } &-mini { max-width: 460px } @@ -27,19 +28,21 @@ padding: var(--space-x2); background-color: #FFF; } +} - // nested - .container { - padding-left:0; - padding-right:0; - } +@media screen and (max-width: 850px) { + .container .container { + padding-left:0; + padding-right:0; + } } + .container-main { flex-direction: column; flex-wrap: nowrap; - @media screen and (min-width: 750px) { + @media screen and (min-width: 850px) { flex-direction: row; } } diff --git a/src/assets/style/links.scss b/src/assets/style/links.scss index 451b5c19e..13b04e020 100644 --- a/src/assets/style/links.scss +++ b/src/assets/style/links.scss @@ -1,20 +1,18 @@ a { - color: rgba(0,0,0,1); - text-decoration: none; - transition: color .3s, opacity .3s, background .3s; - display: inline-block; + transition: color .3s, opacity .3s, background .3s, text-decoration .5s; position: relative; - font-weight: 500; - border-radius: 99px; + text-decoration: underline; + color: var(--primary-color-dark); + text-decoration-color: var(--border-color); - .post &:not(.button) { - background: linear-gradient(180deg, transparent 60%, var(--success-bg) 60%, var(--success-bg) 87%, transparent 0,); + &:hover { + color: #000; + text-decoration-color: #000; } - - .post &:not(.button):hover { - background: linear-gradient(180deg, transparent 0%, var(--success-bg) 60%, var(--success-bg) 87%, transparent 0,); + + svg + & { + margin-left: .5rem; } - } a:hover:not(.active) { diff --git a/src/assets/style/nav.scss b/src/assets/style/nav.scss index 3e52c695f..a414b81ea 100644 --- a/src/assets/style/nav.scss +++ b/src/assets/style/nav.scss @@ -1,33 +1,56 @@ nav a { letter-spacing: .5px; - font-size: .85rem; - font-weight: 500; + font-weight: 400; display: flex; align-items:center; justify-content: center; flex-wrap: wrap; - padding: 5px 5px; + padding: 15px 5px; + text-decoration: none; + position: relative; + font-size: .95rem; + + &:after { + content: ""; + position: absolute; + top: calc(100%); + height: 1px; + left: 50%; + width: 0px; + transform: translateX(-50%); + background-color: transparent; + opacity: .5; + transition: all .6s; + } svg { - margin: 3px 6px; + margin: 2px 6px; } - &.active { + + + + &:not(.active):not(:hover):not(.button) { color: currentColor; - - svg { - opacity: 1; - } - } + opacity: .85; + } &:hover { color: var(--primary-color); + + &:after { + background-color: var(--primary-color); + } } - &:not(.active):not(:hover):not(.button) { - color: currentColor; - opacity: .7; - } + &.active { + color: var(--primary-color-dark); + + &:after { + background-color: var(--primary-color-dark); + width: 100%; + } + } } @@ -47,7 +70,7 @@ nav { } .dropdown { - background-color: var(--primary-bg); + background-color: #FFF; pointer-events: none; visibility: hidden; z-index: 10; @@ -55,9 +78,9 @@ nav { position: absolute; top:100%; right:0; - width: 180px; + width: 200px; border-radius: 3px; - padding: 10px 20px 20px; + box-shadow: 1px 1px 5px rgba(0,0,0,.3); transform: translateY(-5px); opacity: 0; @@ -76,34 +99,12 @@ nav { a { justify-content: flex-start; - } - } -} - -nav.tabs { - - overflow-x: auto; - flex-wrap: nowrap; - -webkit-overflow-scrolling: touch; + font-size: .9rem; + padding: 8px 15px; - a { - background-color: var(--primary-bg); - border-radius: 5px; - padding-left: 10px; - padding-right: 10px; - white-space: nowrap; - } - - a.active { - color:#FFF; - background-color: var(--primary-color); - background: var(--primary-color); - box-shadow: var(--shadow); - } - - @media screen and (max-width: 750px) { - justify-content: flex-start; + &:hover { + background-color: var(--primary-bg); + } + } } -} - - +} \ No newline at end of file diff --git a/src/assets/style/plugins.scss b/src/assets/style/plugins.scss index ad9b70b13..3f1128ea2 100644 --- a/src/assets/style/plugins.scss +++ b/src/assets/style/plugins.scss @@ -1,10 +1,9 @@ .plugins { &__sidebar { - padding: 0 10px 0 0; - @media screen and (min-width: 750px) { + @media screen and (min-width: 850px) { min-width: 225px; - width: 375px; + width: 340px; } } @@ -12,9 +11,9 @@ position: sticky; top: 0; margin: 0; - padding-top: calc(var(--space) * 2); + padding-top: var(--space); padding-bottom: calc(var(--space) / 2); - background: rgba(255,255,255,.95); + background: var(--light-bg-transparent); z-index: 1; &-box { @@ -47,7 +46,7 @@ } &__count { - margin-left: calc(var(--space) / 2); + padding-left: 5px; } &__more.button { @@ -67,23 +66,17 @@ margin: 0; } } - - &__total { - font-size: .8rem; - opacity: .6; - padding: 5px; - } } .plugin { - padding: calc(var(--space) / 2); + padding: calc(var(--space) / 2.5) 4px; position: relative; border-top: 1px solid var(--border-color); margin-bottom: 0; &:hover & { &__name { - color: var(--primary-link-color); + color: var(--primary-color-dark); } } @@ -94,13 +87,15 @@ } &__name { - font-weight: bolder; + font-weight: 500; display: block; } &__description { opacity: .8; - font-size: .9rem; + font-size: .85rem; + line-height: 1.3; + display: inline-block; } &__link { @@ -121,15 +116,9 @@ .plugins-intro { text-align: center; - &__text { - margin: -20% auto 0; - max-width: 550px; - } - - &__image { - margin: 0 auto; + .connect { max-width: 500px; - width: 100%; + margin:0 auto; } } diff --git a/src/assets/style/sidebar.scss b/src/assets/style/sidebar.scss index b16296902..18d782254 100644 --- a/src/assets/style/sidebar.scss +++ b/src/assets/style/sidebar.scss @@ -1,26 +1,31 @@ - .sidebar { order: 2; z-index: 10; overflow: auto; position: relative; -webkit-overflow-scrolling: touch; + padding: 0 20px 60px 5px; - @media screen and (min-width: 750px) { + @media screen and (min-width: 850px) { position: sticky; border-right: 1px solid var(--border-color); top: var(--header-height); - height: calc(100vh + (var(--header-height) * -1)); - padding: 2% 0 50px .5rem; + height: calc(100vh + (var(--header-height) * -1) - 5px); width: 260px; order: 0; } + h3 { - font-size: .75rem; + font-size: .8rem; text-transform: uppercase; letter-spacing: 1px; - margin: 30px 0 15px; + margin: 15px 0 15px; + padding-top: 20px; + border-top: 1px solid var(--border-color); + &:first-of-type { + border:0; + } } p { @@ -33,21 +38,23 @@ opacity: 1; font-weight: 400; display: flex; + text-decoration: none; + &.active--exact { + color: var(--primary-color-dark); + } + &:hover { - opacity: .6; + color: var(--primary-color); } } .menu-link { margin: 0; padding: .2rem 0; - font-size: .95rem; &.active--exact { - color: var(--primary-link-color); - opacity: 1; - font-weight: 600; + color: var(--primary-color-dark); position: relative; padding-left: .8rem; @@ -60,7 +67,7 @@ left: -1px; top: 50%; margin-top: -5px; - background-color: var(--primary-link-color); + background-color: var(--primary-color-dark); animation: scaleIn .7s forwards; } } @@ -80,7 +87,7 @@ } } - @media screen and (max-width: 750px) { + @media screen and (max-width: 850px) { & { width: 100%; position: relative; diff --git a/src/assets/style/typography.scss b/src/assets/style/typography.scss index b94fa204f..22a4f8c7a 100644 --- a/src/assets/style/typography.scss +++ b/src/assets/style/typography.scss @@ -1,21 +1,66 @@ +* { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + @font-face { font-family: Jost; src: url(../fonts/Jost-400-Book.otf); - font-weight: normal; + font-weight: 400; font-display: swap; } +@font-face { + font-family: Jost; + src: url(../fonts/Jost-400-BookItalic.otf); + font-weight: 400; + font-display: swap; + font-style: italic; +} + @font-face { font-family: Jost; src: url(../fonts/Jost-500-Medium.otf); - font-weight: bold; + font-weight: 500; + font-display: swap; +} + +@font-face { + font-family: Jost; + src: url(../fonts/Jost-500-MediumItalic.otf); + font-weight: 500; + font-display: swap; + font-style: italic; +} + +@font-face { + font-family: Jost; + src: url(../fonts/Jost-600-Semi.otf); + font-weight: 600; + font-display: swap; +} + +@font-face { + font-family: Jost; + src: url(../fonts/Jost-600-SemiItalic.otf); + font-style: italic; + font-weight: 600; font-display: swap; } +@font-face { + font-family: Jost; + src: url(../fonts/Jost-700-Bold.otf); + font-weight: 700; + font-display: swap; +} + + + /* Smaller text for mobile */ -@media screen and (max-width: 750px) { +@media screen and (max-width: 550px) { html { - font-size: 99%; + font-size: 100%!important; } } @@ -27,17 +72,26 @@ font-size: 0.8em; } -h1, -h2, -.lead { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; +#app strong { + font-weight: 500; +} + + + +#app h5 { + text-transform: uppercase; + font-weight: 500; + letter-spacing: .5px; +} + +#app .primary-color { + color: var(--primary-color); } p { letter-spacing: -.15px; &:last-child { - margin: 0; + margin-bottom: 0; } } @@ -50,3 +104,11 @@ li { text-transform: uppercase; } + +h1, h2, h3 { + small { + font-weight: normal; + opacity: .8; + margin: 0 1rem; + } +} diff --git a/src/assets/style/utils.scss b/src/assets/style/utils.scss index 741c53b0d..d2861d33f 100644 --- a/src/assets/style/utils.scss +++ b/src/assets/style/utils.scss @@ -9,11 +9,16 @@ } .mb { + margin-bottom: var(--space)!important; +} + +.mb-x2 { margin-bottom: var(--space-x2)!important; } + .pb { - padding-bottom: var(--space-x2)!important; + padding-bottom: var(--space)!important; } .ml { @@ -25,7 +30,7 @@ display: none; } -@media screen and (max-width: 750px) { +@media screen and (max-width: 850px) { .hide-for-small { display: none!important; } diff --git a/src/assets/style/vars.scss b/src/assets/style/vars.scss index 168a58f9e..34d967067 100644 --- a/src/assets/style/vars.scss +++ b/src/assets/style/vars.scss @@ -1,13 +1,31 @@ :root { - --primary-color: #5ed3a2; - --header-height: 62px; - --primary-bg: #f3f7f9; - --secondary-bg: #0d2538; - --primary-link-color: #3bba95; - --border-color: #e1eaef; + --primary-color: #00A672; + --primary-color-dark: #00835c; + --header-height: 58px; + --light-bg: #FFF; + --light-bg-transparent: rgba(255,255,255,.95); + --primary-bg: #f7fbfb; + --primary-bg-transparent: rgba(247,251,251,.95); + --dark-bg: #0d2538; + --code-bg: #0b1d2b; + --inline-code-bg: rgba(27,31,35,.05); + --border-color: #e2ecec; + --border-color-darker: #cfe6e6; + --mark-color: #fff8ec; + --border-radius: 5px; --success-bg: #5bb19640; - --space: 20px; - --space-x2: 40px; - --code-color: #f2f4f5; - --code-color-block: #f2f4f5; + --space: 25px; + --space-x2: 50px; + --code-color: #fdf9f3; + --glow: 2px 2px 10px 0 var(--success-bg); } + +@media screen and (max-width: 550px) { + :root { + --space: 20px; + --space-x2: 20px; + } +} + + + diff --git a/src/components/Card.vue b/src/components/Card.vue index 1f3c2695a..089a5dd80 100644 --- a/src/components/Card.vue +++ b/src/components/Card.vue @@ -1,13 +1,24 @@
+
+ + Read more + +
+ {{ title }} +
+
+ +
+
+ + \ No newline at end of file diff --git a/src/components/Dots.vue b/src/components/Dots.vue index 3193c439e..e8ba5295c 100644 --- a/src/components/Dots.vue +++ b/src/components/Dots.vue @@ -1,158 +1,42 @@ - - - \ No newline at end of file diff --git a/src/components/Ecosystem.vue b/src/components/Ecosystem.vue new file mode 100644 index 000000000..019068529 --- /dev/null +++ b/src/components/Ecosystem.vue @@ -0,0 +1,66 @@ + + + + + \ No newline at end of file diff --git a/src/components/Examples.vue b/src/components/Examples.vue new file mode 100644 index 000000000..477265686 --- /dev/null +++ b/src/components/Examples.vue @@ -0,0 +1,160 @@ + + + + + +query Example { + examples: allExample (sortBy: "order", order: ASC) { + edges { + node { + title + content + id + filepath + } + } + } +} + + + \ No newline at end of file diff --git a/src/components/Feature.vue b/src/components/Feature.vue index c1f9c639e..da07f0f15 100644 --- a/src/components/Feature.vue +++ b/src/components/Feature.vue @@ -1,5 +1,5 @@