├── .eleventyignore ├── .gitignore ├── content ├── rss.md ├── media │ ├── banner.gif │ ├── poster.gif │ ├── eames-1.jpg │ ├── eames-2.jpg │ ├── eames-3.jpg │ ├── eames-4.jpg │ ├── letters-b.jpg │ ├── letters-m.jpg │ ├── letters-q.jpg │ ├── letters-r.jpg │ ├── optics-1.jpg │ ├── optics-2.jpg │ ├── optics-3.jpg │ ├── pavillon.jpg │ ├── traffic-1.png │ ├── traffic-2.png │ ├── traffic-3.png │ ├── traffic-4.png │ ├── traffic-5.png │ ├── muybridge-1.mp4 │ ├── muybridge-2.mp4 │ ├── muybridge-3.jpg │ └── muybridge-4.jpg ├── 404.md ├── posts.md ├── pages │ ├── github.md │ └── about.md ├── posts │ ├── its-here.md │ └── coming-soon.md ├── _data │ └── global.json ├── projects │ ├── portfolio-starter.md │ ├── sample-project-optics.md │ ├── sample-project-letters.md │ ├── sample-project-eames.md │ ├── sample-project-traffic-signs.md │ └── sample-project-muybridge.md └── index.md ├── fonts ├── Inter-Bold.woff2 ├── Inter-Italic.woff2 ├── Inter-Regular.woff2 └── Inter-BoldItalic.woff2 ├── _includes ├── layouts │ ├── error.njk │ ├── page.njk │ ├── post.njk │ ├── base.njk │ ├── feed.njk │ ├── project.njk │ ├── home.njk │ └── posts.njk └── snippets │ ├── meta.njk │ └── media.njk ├── LICENSE ├── package.json ├── .eleventy.js ├── css └── index.css └── README.md /.eleventyignore: -------------------------------------------------------------------------------- 1 | **/README.md 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | /node_modules 3 | /_site -------------------------------------------------------------------------------- /content/rss.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: feed 3 | title: "Blog" 4 | --- 5 | -------------------------------------------------------------------------------- /fonts/Inter-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/fonts/Inter-Bold.woff2 -------------------------------------------------------------------------------- /content/media/banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/banner.gif -------------------------------------------------------------------------------- /content/media/poster.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/poster.gif -------------------------------------------------------------------------------- /fonts/Inter-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/fonts/Inter-Italic.woff2 -------------------------------------------------------------------------------- /content/media/eames-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/eames-1.jpg -------------------------------------------------------------------------------- /content/media/eames-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/eames-2.jpg -------------------------------------------------------------------------------- /content/media/eames-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/eames-3.jpg -------------------------------------------------------------------------------- /content/media/eames-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/eames-4.jpg -------------------------------------------------------------------------------- /content/media/letters-b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/letters-b.jpg -------------------------------------------------------------------------------- /content/media/letters-m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/letters-m.jpg -------------------------------------------------------------------------------- /content/media/letters-q.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/letters-q.jpg -------------------------------------------------------------------------------- /content/media/letters-r.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/letters-r.jpg -------------------------------------------------------------------------------- /content/media/optics-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/optics-1.jpg -------------------------------------------------------------------------------- /content/media/optics-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/optics-2.jpg -------------------------------------------------------------------------------- /content/media/optics-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/optics-3.jpg -------------------------------------------------------------------------------- /content/media/pavillon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/pavillon.jpg -------------------------------------------------------------------------------- /content/media/traffic-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/traffic-1.png -------------------------------------------------------------------------------- /content/media/traffic-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/traffic-2.png -------------------------------------------------------------------------------- /content/media/traffic-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/traffic-3.png -------------------------------------------------------------------------------- /content/media/traffic-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/traffic-4.png -------------------------------------------------------------------------------- /content/media/traffic-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/traffic-5.png -------------------------------------------------------------------------------- /fonts/Inter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/fonts/Inter-Regular.woff2 -------------------------------------------------------------------------------- /content/media/muybridge-1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/muybridge-1.mp4 -------------------------------------------------------------------------------- /content/media/muybridge-2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/muybridge-2.mp4 -------------------------------------------------------------------------------- /content/media/muybridge-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/muybridge-3.jpg -------------------------------------------------------------------------------- /content/media/muybridge-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/content/media/muybridge-4.jpg -------------------------------------------------------------------------------- /fonts/Inter-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb-ph/portfolio-starter/HEAD/fonts/Inter-BoldItalic.woff2 -------------------------------------------------------------------------------- /content/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: error 3 | title: "Page not found (404 error)" 4 | --- 5 | 6 | [Return to homepage](/) 7 | -------------------------------------------------------------------------------- /content/posts.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: posts 3 | title: Blog 4 | eleventyNavigation: 5 | key: News 6 | order: 2 7 | --- 8 | -------------------------------------------------------------------------------- /_includes/layouts/error.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | permalink: 404.html 4 | eleventyExcludeFromCollections: true 5 | --- 6 | 7 | {{ content | safe }} 8 | -------------------------------------------------------------------------------- /content/pages/github.md: -------------------------------------------------------------------------------- 1 | --- 2 | eleventyNavigation: 3 | key: GitHub ↗ 4 | order: 3 5 | url: http://github.com/sb-ph/portfolio-starter 6 | permalink: false 7 | --- 8 | -------------------------------------------------------------------------------- /_includes/layouts/page.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | permalink: "/{{ page.fileSlug }}/index.html" 4 | --- 5 | 6 |
7 | 10 | {{ content | safe }} 11 |
12 | -------------------------------------------------------------------------------- /content/posts/its-here.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: It’s here 4 | date: 2020-04-10 15:00:00 5 | --- 6 | 7 | We just pushed the first version of Portfolio Starter. 🎉 [Check it out on GitHub](https://github.com/sb-ph/portfolio-starter). If you give it a try, [let us know](mailto:mail@sb-ph.com)! We’d love to see how people use it. 8 | -------------------------------------------------------------------------------- /content/posts/coming-soon.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Coming soon… 4 | date: 2020-04-06 15:00:00 5 | --- 6 | 7 | A starter kit for static portfolio sites. Lightweight, customisable, open source. By [Piper Haywood](https://piperhaywood.com) and [Sam Baldwin](https://sambaldwin.info). Coming Spring 2020. [Get in touch](mailto:mail@sb-ph.com) ✉️ 8 | -------------------------------------------------------------------------------- /content/_data/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Portfolio Starter", 3 | "lang": "en-gb", 4 | "footer": "Built with [Portfolio Starter](https://portfolio-starter.sb-ph.com) (v{{ pkg.version }}) by [Piper Haywood](https://piperhaywood.com) and [Sam Baldwin](https://sambaldwin.info)", 5 | "url": "https://portfolio-starter.sb-ph.com", 6 | "author": { 7 | "name": "Portfolio Starter", 8 | "email": "mail@sb-ph.com" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /content/projects/portfolio-starter.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Portfolio Starter 3 | layout: project 4 | dateEnd: 2020-04-07 19:00:00 5 | media: 6 | - type: image 7 | filename: poster.gif 8 | size: md 9 | alt: Poster for Portfolio Starter 10 | --- 11 | 12 | A starter kit for static portfolio sites. Lightweight, customisable, open source. By [Piper Haywood](https://piperhaywood.com) and [Sam Baldwin](https://sambaldwin.info). [Get started](https://github.com/sb-ph/portfolio-starter)! 13 | -------------------------------------------------------------------------------- /content/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | entries: 4 | - portfolio-starter 5 | - sample-project-muybridge 6 | - sample-project-optics 7 | - sample-project-eames 8 | - sample-project-letters 9 | - sample-project-traffic-signs 10 | --- 11 | 12 | A starter kit for static portfolio sites. Lightweight, customisable, open source. By [Piper Haywood](https://piperhaywood.com) and [Sam Baldwin](https://sambaldwin.info). Have a look around, or [take a look at the documentation](https://github.com/sb-ph/portfolio-starter) and make your own site for free. 13 | -------------------------------------------------------------------------------- /_includes/snippets/meta.njk: -------------------------------------------------------------------------------- 1 | 2 | {%- set absolutePageUrl -%}{{ page.url | url | absoluteUrl(meta.url) }}{%- endset -%} 3 | 4 | {# Meta Tags #} 5 | 6 | 7 | 8 | {% if description %} 9 | 10 | 11 | {% endif %} 12 | {% if image %} 13 | 14 | {% endif %} 15 | 16 | {# RSS Feed #} 17 | {% if collections.posts.length %} 18 | 19 | {% endif %} -------------------------------------------------------------------------------- /_includes/layouts/post.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | permalink: "/posts/{{ page.fileSlug }}/index.html" 4 | --- 5 | 6 |
7 |
8 |

{{ title | safe }}

9 | 10 |
11 | {{ content | safe }} 12 |
13 | 14 | {% set next = collections.posts | nextInCollection(page.fileSlug) %} 15 | {% set previous = collections.posts | prevInCollection(page.fileSlug) %} 16 | 17 | {% if next or previous%} 18 | 30 | {% endif %} -------------------------------------------------------------------------------- /_includes/layouts/base.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title ~ ' – ' ~ global.title if title else global.title }} 7 | 8 | {% include 'snippets/meta.njk' %} 9 | 10 | 11 | 12 | 20 | 21 |
22 | {{ content | safe }} 23 |
24 | 25 | {% if global.footer %} 26 | 29 | {% endif %} 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /_includes/snippets/media.njk: -------------------------------------------------------------------------------- 1 | {% set blockClasses = block.size if block.size else 'md' %} 2 |
3 | {% if block.type == 'image' %} 4 | {% set image = block.filename | urlencode %} 5 | 12 | {% elif block.type == 'video' %} 13 | 23 | {% endif %} 24 | {% if caption %} 25 |
{{ caption | markdownifyInline | safe }}
26 | {% endif %} 27 |
-------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SB-PH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_includes/layouts/feed.njk: -------------------------------------------------------------------------------- 1 | ---json 2 | { 3 | "permalink": "feed.xml", 4 | "eleventyExcludeFromCollections": true 5 | } 6 | --- 7 | {% if collections.posts.length %} 8 | 9 | 10 | {{ title }} 11 | 12 | 13 | {{ collections.posts | rssLastUpdatedDate }} 14 | {{ global.url }} 15 | {% if global.author %} 16 | 17 | {{ global.author.name }} 18 | {{ global.author.email }} 19 | 20 | {% endif %} 21 | {%- for post in collections.posts %} 22 | {% set absolutePostUrl %}{{ post.url | url | absoluteUrl(global.url) }}{% endset %} 23 | 24 | {{ post.data.title }} 25 | 26 | {{ post.date | rssDate }} 27 | {{ absolutePostUrl }} 28 | {{ post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }} 29 | 30 | {%- endfor %} 31 | 32 | {% endif %} -------------------------------------------------------------------------------- /content/pages/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: About 4 | eleventyNavigation: 5 | key: About 6 | order: 1 7 | --- 8 | 9 | This is a lightweight portfolio starter kit by [Sam Baldwin](https://sambaldwin.info) and [Piper Haywood](https://piperhaywood.com). The thinking behind it comes from a decade of experience designing and building portfolios for designers, illustrators, architects, and other people that want to share their work and activity online. 10 | 11 | Portfolio Starter is lightweight and open source. The intended user may not know how to code but is interested in the tech behind their website, is willing to write in Markdown, and is happy to follow a few instructions. The codebase is deliberately basic to encourage tinkering and customisation for everyone, including a beginner that is just beginning to learn why `alt` text is important (hint: it’s really important). Since it uses the static site generator [Eleventy](https://www.11ty.dev/), Portfolio Starter can be hooked up to free static hosting providers such as Netlify or ZEIT Now. 12 | 13 | [Check out the documentation](https://github.com/sb-ph/portfolio-starter) to start experimenting, you can get a website online with the click of a few buttons. If you give it a try, [let us know](mailto:mail@sb-ph.com)! We’d love to see how people use it and where they take it. 14 | -------------------------------------------------------------------------------- /content/projects/sample-project-optics.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: project 3 | title: Sample project 4 4 | dateEnd: 2020-04-02 5 | media: 6 | - type: image 7 | filename: optics-3.jpg 8 | size: md 9 | alt: Artistic depiction of a rainbow 10 | caption: "[Image source](https://publicdomainreview.org/collection/optics-illustrations-from-the-physics-textbooks-of-amedee-guillemin-1868-1882)" 11 | - type: image 12 | filename: optics-1.jpg 13 | size: sm 14 | alt: Colour chart 15 | caption: "[Image source](https://publicdomainreview.org/collection/optics-illustrations-from-the-physics-textbooks-of-amedee-guillemin-1868-1882)" 16 | - type: image 17 | filename: optics-2.jpg 18 | size: md 19 | alt: Colour chart 20 | caption: "[Image source](https://publicdomainreview.org/collection/optics-illustrations-from-the-physics-textbooks-of-amedee-guillemin-1868-1882)" 21 | --- 22 | 23 | Cras mattis consectetur purus sit amet fermentum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas sed diam eget risus varius blandit sit amet non magna. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Nullam id dolor id nibh ultricies vehicula ut id elit. 24 | -------------------------------------------------------------------------------- /_includes/layouts/project.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | permalink: "/projects/{{ page.fileSlug }}/index.html" 4 | --- 5 | 6 |
7 |
8 |

{{ title | safe }}

9 | {% if dateStart or dateEnd %} 10 |

{{ dateStart | yearRange(dateEnd) | safe }}

11 | {% endif %} 12 | {% if content %} 13 |
14 | {{ content | safe }} 15 |
16 | {% endif %} 17 |
18 | {% if media %} 19 | {% for block in media %} 20 |
21 | {% set caption = block.caption %} 22 | {% include 'snippets/media.njk' %} 23 |
24 | {% endfor %} 25 | {% endif %} 26 |
27 | 28 | {% set next = collections.projects | nextInCollection(page.fileSlug) %} 29 | {% set previous = collections.projects | prevInCollection(page.fileSlug) %} 30 | 31 | {% if next or previous%} 32 | 44 | {% endif %} -------------------------------------------------------------------------------- /content/projects/sample-project-letters.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: project 3 | title: Sample project 1 4 | dateEnd: 2020-04-05 5 | media: 6 | - type: image 7 | filename: letters-b.jpg 8 | size: sm 9 | alt: Letter B 10 | caption: "[Image source ↗](https://publicdomainreview.org/collection/an-alphabet-of-organic-type-ca-1650)" 11 | - type: image 12 | filename: letters-m.jpg 13 | size: sm 14 | alt: Letter M 15 | caption: "[Image source ↗](https://publicdomainreview.org/collection/an-alphabet-of-organic-type-ca-1650)" 16 | - type: image 17 | filename: letters-r.jpg 18 | size: sm 19 | alt: Letter R 20 | caption: "[Image source ↗](https://publicdomainreview.org/collection/an-alphabet-of-organic-type-ca-1650)" 21 | - type: image 22 | filename: letters-q.jpg 23 | size: sm 24 | alt: Letter Q 25 | caption: "[Image source ↗](https://publicdomainreview.org/collection/an-alphabet-of-organic-type-ca-1650)" 26 | --- 27 | 28 | Cras mattis consectetur purus sit amet fermentum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas sed diam eget risus varius blandit sit amet non magna. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Nullam id dolor id nibh ultricies vehicula ut id elit. 29 | -------------------------------------------------------------------------------- /content/projects/sample-project-eames.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: project 3 | title: Sample project 2 4 | dateEnd: 2020-04-04 5 | media: 6 | - type: image 7 | filename: eames-1.jpg 8 | size: lg 9 | alt: Eames plastic chairs 10 | caption: "[Image via Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Eames_chair-IMG_4611.jpg)" 11 | - type: image 12 | filename: eames-2.jpg 13 | size: sm 14 | alt: Eames plastic chairs, stacked 15 | caption: "[Image via Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Eames-stapelstuhl.jpeg)" 16 | - type: image 17 | filename: eames-3.jpg 18 | size: md 19 | alt: Eames chair 20 | caption: "[Image via Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Eames_chair-IMG_4624.jpg)" 21 | - type: image 22 | filename: eames-4.jpg 23 | size: sm 24 | alt: Eames chair 25 | caption: "[Image via Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Charles_and_Ray_Eames_-_Plastic_Chair_1950-53.jpg)" 26 | --- 27 | 28 | Cras mattis consectetur purus sit amet fermentum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas sed diam eget risus varius blandit sit amet non magna. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Nullam id dolor id nibh ultricies vehicula ut id elit. 29 | -------------------------------------------------------------------------------- /_includes/layouts/home.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | permalink: "/" 4 | --- 5 | 6 |
7 | {{ content | safe }} 8 |
9 | 10 | 11 | {% set entries = collections.all | getPagesByPaths(entries) if entries else collections.projects %} 12 | 13 | {# For each of the entries #} 14 | {% for entry in entries %} 15 | 16 | {# Set the defaults #} 17 | {% set image = false %} 18 | {% set width = false %} 19 | {% set height = false %} 20 | {% set blockClasses = 'md' %} 21 | 22 | {# If the entry has media #} 23 | {% if entry.data.media %} 24 | {# Get the first featured image block #} 25 | {% set block = entry.data.media | getFeaturedImage %} 26 | {% if block %} 27 | {% set blockClasses = block.size if block.size %} 28 | {% set image = block.filename | urlencode %} 29 | {% set width = block.width if block.width else false %} 30 | {% set height = block.height if block.height else false %} 31 | {% endif %} 32 | {% endif %} 33 | 34 | {# If there is an image we can use #} 35 | {% if image %} 36 |
37 | 38 | 45 |

{{ entry.data.title | safe }}

46 |
47 |
48 | {% endif %} 49 | {% endfor %} -------------------------------------------------------------------------------- /content/projects/sample-project-traffic-signs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: project 3 | title: Sample project 3 4 | dateEnd: 2020-04-03 5 | media: 6 | - type: image 7 | filename: traffic-5.png 8 | size: sm 9 | alt: Pictogram of a house-like building next to a tree 10 | caption: "[Image source](https://www.gov.uk/guidance/traffic-sign-images)" 11 | - type: image 12 | filename: traffic-1.png 13 | size: sm 14 | alt: Pictogram of a tree 15 | caption: "[Image source](https://www.gov.uk/guidance/traffic-sign-images)" 16 | - type: image 17 | filename: traffic-2.png 18 | size: sm 19 | alt: Pictogram of some oak leaves 20 | caption: "[Image source](https://www.gov.uk/guidance/traffic-sign-images)" 21 | - type: image 22 | filename: traffic-3.png 23 | size: sm 24 | alt: Pictogram of a castle 25 | caption: "[Image source](https://www.gov.uk/guidance/traffic-sign-images)" 26 | - type: image 27 | filename: traffic-4.png 28 | size: sm 29 | alt: Pictogram of an elephant 30 | caption: "[Image source](https://www.gov.uk/guidance/traffic-sign-images)" 31 | --- 32 | 33 | Cras mattis consectetur purus sit amet fermentum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas sed diam eget risus varius blandit sit amet non magna. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Nullam id dolor id nibh ultricies vehicula ut id elit. 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portfolio-starter", 3 | "version": "0.1.0", 4 | "description": "A starter kit for static portfolio sites", 5 | "scripts": { 6 | "build": "npx eleventy", 7 | "watch": "npx eleventy --watch", 8 | "serve": "npx eleventy --serve", 9 | "debug": "DEBUG=* npx eleventy" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/sb-ph/portfolio-starter.git" 14 | }, 15 | "keywords": [ 16 | "static-site-generator", 17 | "static-site", 18 | "ssg", 19 | "website", 20 | "blog", 21 | "templates", 22 | "generator", 23 | "framework", 24 | "eleventy", 25 | "11ty", 26 | "html", 27 | "markdown", 28 | "liquid", 29 | "nunjucks", 30 | "portfolio", 31 | "handlebars", 32 | "mustache", 33 | "ejs" 34 | ], 35 | "author": { 36 | "name": "Sam Baldwin", 37 | "email": "mail@sambaldwin.info", 38 | "url": "https://sambaldwin.info/" 39 | }, 40 | "contributors": [ 41 | { 42 | "name": "Piper Haywood", 43 | "email": "mail@piperhaywood.com", 44 | "url": "https://piperhaywood.com/" 45 | } 46 | ], 47 | "license": "MIT", 48 | "bugs": { 49 | "url": "https://github.com/sb-ph/portfolio-starter/issues" 50 | }, 51 | "homepage": "https://github.com/sb-ph/portfolio-starter#readme", 52 | "devDependencies": { 53 | "@11ty/eleventy": "^0.10.0", 54 | "@11ty/eleventy-navigation": "^0.1.5", 55 | "@11ty/eleventy-plugin-rss": "^1.0.7", 56 | "luxon": "^1.22.2", 57 | "markdown-it": "^10.0.0", 58 | "markdown-it-anchor": "^5.2.5" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /content/projects/sample-project-muybridge.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Muybridge 3 | layout: project 4 | dateStart: 1830-04-09 5 | dateEnd: 1904-05-08 6 | media: 7 | - type: image 8 | filename: muybridge-3.jpg 9 | size: lg 10 | alt: Grid of photographs of a mounted horse galloping 11 | caption: "[Source ↗](https://commons.wikimedia.org/wiki/File:Muybridge_horse_gallop.jpg)" 12 | - type: video 13 | filename: muybridge-2.mp4 14 | caption: "Auto-playing video ([source ↗](https://commons.wikimedia.org/wiki/File:BuffaloRunning1883EadweardMuybridgeVeryEarlyFilm-5bqu6ysqocu.ogv))" 15 | controls: false 16 | autoplay: true 17 | muted: true 18 | size: sm 19 | loop: true 20 | - type: video 21 | filename: muybridge-1.mp4 22 | caption: "Auto-playing video with controls ([source ↗](https://commons.wikimedia.org/wiki/File:Muybridge_race_horse.webm))" 23 | controls: true 24 | autoplay: true 25 | size: sm 26 | loop: true 27 | muted: true 28 | - type: image 29 | filename: muybridge-4.jpg 30 | size: md 31 | alt: Grid of photographs of a mounted horse galloping 32 | caption: "[Source ↗](https://commons.wikimedia.org/wiki/File:Le-galop-de-daisy.jpg)" 33 | --- 34 | 35 | Cras mattis consectetur purus sit amet fermentum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas sed diam eget risus varius blandit sit amet non magna. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Nullam id dolor id nibh ultricies vehicula ut id elit. 36 | -------------------------------------------------------------------------------- /_includes/layouts/posts.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | pagination: 4 | data: collections.posts 5 | size: 10 6 | alias: postslist 7 | permalink: "posts/{% if pagination.pageNumber > 0 %}{{ pagination.pageNumber + 1 }}/{% endif %}" 8 | --- 9 | 10 |
11 | {% for post in postslist %} 12 |
13 |
14 |

15 | {{ post.data.title | safe }} 16 |

17 | 18 |
19 | {% if post.data.page.excerpt %} 20 | {{ post.data.page.excerpt | markdownify | safe }} 21 |

Read more…

22 | {% else %} 23 | {{ post.templateContent | safe }} 24 | {% endif %} 25 |
26 | {% endfor %} 27 |
28 | 29 | {% if pagination.pages and pagination.pages.length > 1 %} 30 | 49 | {% endif %} 50 | -------------------------------------------------------------------------------- /.eleventy.js: -------------------------------------------------------------------------------- 1 | const pluginRss = require("@11ty/eleventy-plugin-rss"); 2 | const pluginNav = require("@11ty/eleventy-navigation"); 3 | 4 | const markdownIt = require("markdown-it"); 5 | const markdownItAnchor = require("markdown-it-anchor"); 6 | const { DateTime } = require("luxon"); 7 | const fs = require("fs"); 8 | 9 | var getIndex = (collection, currentSlug) => { 10 | let currentIndex = 0; 11 | collection.filter((page, index) => { 12 | currentIndex = page.fileSlug == currentSlug ? index : currentIndex; 13 | }); 14 | return currentIndex; 15 | }; 16 | 17 | module.exports = function (config) { 18 | // Plugins 19 | config.addPlugin(pluginRss); 20 | config.addPlugin(pluginNav); 21 | 22 | // Filters 23 | config.addFilter("dateToFormat", (date, format) => { 24 | return DateTime.fromJSDate(date, { zone: "utc" }).toFormat(String(format)); 25 | }); 26 | config.addFilter("yearRange", (date1Str, date2Str = false) => { 27 | let date = false; 28 | let end = false; 29 | let start = false; 30 | const date1 = date1Str 31 | ? DateTime.fromJSDate(date1Str, { zone: "utc" }) 32 | : false; 33 | const date2 = DateTime.fromJSDate(date2Str, { zone: "utc" }); 34 | if (date1 && date2) { 35 | end = date1 < date2 ? date2 : date1; 36 | start = date1 < date2 ? date1 : date2; 37 | } else { 38 | end = date2; 39 | } 40 | const endYear = end.toFormat("yyyy"); 41 | if (start && end) { 42 | const startYear = start.toFormat("yyyy"); 43 | if (startYear == endYear) { 44 | date = endYear; 45 | } else { 46 | const startCent = startYear.slice(0, 2); 47 | const endCent = endYear.slice(0, 2); 48 | if (startCent == endCent) { 49 | date = startYear + "–" + end.toFormat("yy"); 50 | } else { 51 | date = startYear + "–" + endYear; 52 | } 53 | } 54 | } else if (end) { 55 | date = endYear; 56 | } 57 | return date; 58 | }); 59 | config.addFilter("dateToISO", (date) => { 60 | return DateTime.fromJSDate(date, { zone: "utc" }).toISO({ 61 | includeOffset: false, 62 | suppressMilliseconds: true, 63 | }); 64 | }); 65 | config.addFilter("nextInCollection", (collection, currentSlug) => { 66 | const currentIndex = getIndex(collection, currentSlug); 67 | const pages = collection.filter((page, index) => { 68 | return index == currentIndex + 1 ? page : false; 69 | }); 70 | return pages.length ? pages[0] : false; 71 | }); 72 | config.addFilter("prevInCollection", (collection, currentSlug) => { 73 | const currentIndex = getIndex(collection, currentSlug); 74 | const pages = collection.filter((page, index) => { 75 | return index == currentIndex - 1 ? page : false; 76 | }); 77 | return pages.length ? pages[0] : false; 78 | }); 79 | config.addFilter("getPagesByPaths", (collection, paths) => { 80 | let pages = []; 81 | paths.forEach((path) => { 82 | let page = collection.filter((page) => { 83 | return page.filePathStem.includes(path) ? page : false; 84 | }); 85 | if (page.length) { 86 | pages.push(page[0]); 87 | } 88 | }); 89 | return pages; 90 | }); 91 | config.addFilter("getFeaturedImage", (blocks) => { 92 | // Get the featured images 93 | let images = blocks.filter((block) => { 94 | return block.type == "image" && block.featured ? block : false; 95 | }); 96 | // If no featured images, get all the images 97 | if (!images.length) { 98 | images = blocks.filter((block) => { 99 | return block.type == "image" ? block : false; 100 | }); 101 | } 102 | return images.length ? images[0] : false; 103 | }); 104 | 105 | // Collections 106 | config.addCollection("projects", (collection) => { 107 | const projects = collection.getFilteredByGlob("content/projects/*.md"); 108 | return projects.sort(function (a, b) { 109 | return b.data.dateEnd - a.data.dateEnd; 110 | }); 111 | }); 112 | config.addCollection("posts", function (collection) { 113 | const posts = collection.getFilteredByGlob("content/posts/*.md"); 114 | return posts.sort(function (a, b) { 115 | return b.data.date - a.data.date; 116 | }); 117 | }); 118 | config.addCollection("pages", function (collection) { 119 | return collection.getFilteredByGlob("content/pages/*.md"); 120 | }); 121 | 122 | // Markdown 123 | const markdownOptions = { 124 | html: true, 125 | breaks: true, 126 | linkify: true, 127 | typographer: true, 128 | }; 129 | const markdownLibrary = markdownIt(markdownOptions).use(markdownItAnchor, { 130 | permalink: false, 131 | }); 132 | config.setLibrary("md", markdownLibrary); 133 | config.addNunjucksFilter("markdownify", (markdownString) => 134 | markdownIt(markdownOptions).render(markdownString) 135 | ); 136 | config.addNunjucksFilter("markdownifyInline", (markdownString) => 137 | markdownIt(markdownOptions).renderInline(markdownString) 138 | ); 139 | config.setFrontMatterParsingOptions({ 140 | excerpt: true, 141 | excerpt_separator: "", // Matches WordPress style 142 | }); 143 | 144 | // BrowserSync 145 | config.setBrowserSyncConfig({ 146 | callbacks: { 147 | ready: function (err, browserSync) { 148 | const content_404 = fs.readFileSync("_site/404.html"); 149 | 150 | browserSync.addMiddleware("*", (req, res) => { 151 | // Provides the 404 content without redirect. 152 | res.write(content_404); 153 | res.end(); 154 | }); 155 | }, 156 | }, 157 | }); 158 | 159 | // Pass-thru files 160 | config.addPassthroughCopy({ "content/media": "media" }); 161 | config.addPassthroughCopy("css"); 162 | config.addPassthroughCopy("fonts"); 163 | 164 | // Layouts 165 | config.addLayoutAlias("base", "layouts/base.njk"); 166 | config.addLayoutAlias("page", "layouts/page.njk"); 167 | config.addLayoutAlias("error", "layouts/error.njk"); 168 | config.addLayoutAlias("feed", "layouts/feed.njk"); 169 | config.addLayoutAlias("home", "layouts/home.njk"); 170 | config.addLayoutAlias("post", "layouts/post.njk"); 171 | config.addLayoutAlias("posts", "layouts/posts.njk"); 172 | config.addLayoutAlias("project", "layouts/project.njk"); 173 | 174 | // Base Config 175 | return { 176 | dir: { 177 | data: "content/_data", 178 | }, 179 | templateFormats: ["njk", "md", "html", "liquid"], 180 | htmlTemplateEngine: "njk", 181 | dataTemplateEngine: "njk", 182 | passthroughFileCopy: true, 183 | }; 184 | }; 185 | -------------------------------------------------------------------------------- /css/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Variables 4 | 5 | */ 6 | 7 | :root { 8 | /* Typography */ 9 | --font-family: "Inter", sans-serif; 10 | --font-size-body: 1rem; 11 | --font-size-small: 0.75rem; 12 | --font-weight-normal: 400; 13 | --font-weight-bold: 700; 14 | --line-height: 1.375; 15 | 16 | /* Layout */ 17 | --padding-container: 1rem; 18 | --width-container: 100rem; 19 | --width-textcol: 37.5rem; 20 | --width-media-sm: 30rem; 21 | --width-media-md: 50rem; 22 | --width-media-lg: var(--width-container); 23 | } 24 | 25 | /* 26 | 27 | Fonts 28 | 29 | */ 30 | 31 | @font-face { 32 | font-display: swap; 33 | font-family: "Inter"; 34 | font-style: normal; 35 | font-weight: 400; 36 | src: url("../fonts/Inter-Regular.woff2") format("woff2"); 37 | } 38 | 39 | @font-face { 40 | font-display: swap; 41 | font-family: "Inter"; 42 | font-style: italic; 43 | font-weight: 400; 44 | src: url("../fonts/Inter-Italic.woff2") format("woff2"); 45 | } 46 | 47 | @font-face { 48 | font-display: swap; 49 | font-family: "Inter"; 50 | font-style: normal; 51 | font-weight: 700; 52 | src: url("../fonts/Inter-Bold.woff2") format("woff2"); 53 | } 54 | 55 | @font-face { 56 | font-display: swap; 57 | font-family: "Inter"; 58 | font-style: italic; 59 | font-weight: 700; 60 | src: url("../fonts/Inter-BoldItalic.woff2") format("woff2"); 61 | } 62 | 63 | /* 64 | 65 | Base styles 66 | 67 | */ 68 | 69 | html { 70 | box-sizing: border-box; 71 | } 72 | 73 | *, 74 | *::after, 75 | *::before { 76 | box-sizing: inherit; 77 | } 78 | 79 | body { 80 | font-family: var(--font-family); 81 | font-size: var(--font-size-body); 82 | font-weight: var(--font-weight-normal); 83 | line-height: var(--line-height); 84 | margin: 0; 85 | min-height: 100vh; 86 | display: flex; 87 | flex-direction: column; 88 | padding: 0 var(--padding-container); 89 | } 90 | 91 | h1, 92 | h2, 93 | h3, 94 | h4, 95 | h5, 96 | h6 { 97 | font-size: inherit; 98 | font-weight: inherit; 99 | margin-bottom: 1em; 100 | margin-top: 3em; 101 | } 102 | 103 | p { 104 | margin-bottom: 1em; 105 | margin-top: 0; 106 | } 107 | 108 | a { 109 | color: inherit; 110 | text-decoration: underline; 111 | } 112 | 113 | a:focus, 114 | a:hover { 115 | text-decoration: none; 116 | } 117 | 118 | body > header a { 119 | text-decoration: none; 120 | } 121 | 122 | body > header a:focus, 123 | body > header a:hover { 124 | text-decoration: underline; 125 | } 126 | 127 | b, 128 | strong { 129 | font-weight: var(--font-weight-bold); 130 | } 131 | 132 | code, 133 | samp, 134 | kbd { 135 | font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; 136 | } 137 | 138 | pre { 139 | overflow-x: auto; 140 | } 141 | 142 | img, 143 | video, 144 | iframe { 145 | display: inline-block; 146 | height: auto; 147 | max-width: 100%; 148 | vertical-align: middle; 149 | } 150 | 151 | figure { 152 | margin: 1em 0; 153 | } 154 | 155 | figcaption { 156 | font-size: var(--font-size-small); 157 | margin-top: 0.5rem; 158 | } 159 | 160 | /* 161 | 162 | Layout / global includes 163 | 164 | */ 165 | 166 | .site-header { 167 | align-items: baseline; 168 | display: flex; 169 | flex-wrap: wrap; 170 | margin-bottom: 4rem; 171 | margin-top: 1rem; 172 | margin-left: auto; 173 | margin-right: auto; 174 | max-width: 100%; 175 | width: var(--width-container); 176 | } 177 | 178 | .site-name { 179 | font-weight: inherit; 180 | margin-bottom: 0.25rem; 181 | margin-right: auto; 182 | margin-top: 0; 183 | padding-right: 1rem; 184 | } 185 | 186 | .site-nav ul { 187 | display: flex; 188 | flex-wrap: wrap; 189 | list-style: none; 190 | margin: 0; 191 | padding-left: 0; 192 | } 193 | 194 | .site-nav li:not(:last-child) { 195 | padding-bottom: 0.25rem; 196 | padding-right: 1rem; 197 | } 198 | 199 | .site-main { 200 | margin-bottom: 4rem; 201 | } 202 | 203 | .site-footer { 204 | font-size: var(--font-size-small); 205 | margin-top: auto; 206 | margin-bottom: 1rem; 207 | margin-left: auto; 208 | margin-right: auto; 209 | max-width: 100%; 210 | width: var(--width-container); 211 | } 212 | 213 | .site-footer > p { 214 | margin-bottom: 0; 215 | } 216 | 217 | /* 218 | 219 | Homepage 220 | 221 | */ 222 | 223 | .home-intro { 224 | margin-bottom: 5rem; 225 | margin-left: auto; 226 | margin-right: auto; 227 | max-width: var(--width-textcol); 228 | } 229 | 230 | .home-block { 231 | align-items: center; 232 | display: flex; 233 | flex-direction: column; 234 | margin-bottom: 8rem; 235 | text-align: center; 236 | } 237 | 238 | .home-block > a { 239 | max-width: 100%; 240 | text-decoration: none; 241 | } 242 | 243 | .home-block > a:focus, 244 | .home-block > a:hover { 245 | text-decoration: underline; 246 | } 247 | 248 | .home-block.sm > a { 249 | width: var(--width-media-sm); 250 | } 251 | 252 | .home-block.md > a { 253 | width: var(--width-media-md); 254 | } 255 | 256 | .home-block.lg > a { 257 | width: var(--width-media-lg); 258 | } 259 | 260 | .home-block.lg { 261 | margin-left: calc(var(--padding-container) * -1); 262 | margin-right: calc(var(--padding-container) * -1); 263 | } 264 | 265 | .home-block img:not([width]), 266 | .home-block video:not([width]) { 267 | width: 100%; 268 | } 269 | 270 | .home-block-title { 271 | margin-bottom: 0; 272 | margin-top: 2rem; 273 | } 274 | 275 | /* 276 | 277 | Page 278 | 279 | */ 280 | 281 | .page { 282 | margin-bottom: 8rem; 283 | margin-left: auto; 284 | margin-right: auto; 285 | max-width: var(--width-textcol); 286 | } 287 | 288 | .page-header { 289 | margin-bottom: 1rem; 290 | } 291 | 292 | .page-title { 293 | font-weight: inherit; 294 | margin: 0; 295 | } 296 | 297 | /* 298 | 299 | Post 300 | 301 | */ 302 | 303 | .post { 304 | margin-bottom: 8rem; 305 | margin-left: auto; 306 | margin-right: auto; 307 | max-width: var(--width-textcol); 308 | } 309 | 310 | .post-header { 311 | margin-bottom: 1rem; 312 | } 313 | 314 | .post-title { 315 | font-weight: inherit; 316 | margin: 0; 317 | } 318 | 319 | .post-date { 320 | display: block; 321 | } 322 | 323 | /* 324 | 325 | Project page 326 | 327 | */ 328 | 329 | .project { 330 | margin-bottom: 8rem; 331 | } 332 | 333 | .project-header { 334 | margin-bottom: 4rem; 335 | margin-left: auto; 336 | margin-right: auto; 337 | max-width: var(--width-textcol); 338 | } 339 | 340 | .project-title { 341 | font-weight: inherit; 342 | margin: 0; 343 | } 344 | 345 | .project-details { 346 | margin: 0; 347 | } 348 | 349 | .project-text { 350 | margin-top: 1rem; 351 | } 352 | 353 | .project-block { 354 | margin-bottom: 6rem; 355 | text-align: center; 356 | } 357 | 358 | .project-block figure { 359 | margin-left: auto; 360 | margin-right: auto; 361 | } 362 | 363 | .project-block figure.sm { 364 | max-width: var(--width-media-sm); 365 | } 366 | 367 | .project-block figure.md { 368 | max-width: var(--width-media-md); 369 | } 370 | 371 | .project-block figure.lg { 372 | max-width: var(--width-media-lg); 373 | margin-left: calc(var(--padding-container) * -1); 374 | margin-right: calc(var(--padding-container) * -1); 375 | } 376 | 377 | .project-block img:not([width]), 378 | .project-block video:not([width]) { 379 | width: 100%; 380 | } 381 | 382 | /* 383 | 384 | Pagination 385 | 386 | */ 387 | 388 | .pagination { 389 | margin-bottom: 4rem; 390 | margin-top: 4rem; 391 | text-align: center; 392 | } 393 | 394 | .pagination-list { 395 | display: flex; 396 | justify-content: center; 397 | list-style: none; 398 | padding-left: 0; 399 | } 400 | 401 | .pagination-list li:not(:last-child) { 402 | padding-right: 1em; 403 | } 404 | 405 | .pagination-item { 406 | margin-bottom: 0.5rem; 407 | margin-top: 0; 408 | } 409 | 410 | .pagination-item a:not([aria-current]) { 411 | text-decoration: none; 412 | } 413 | 414 | .pagination-item a:focus, 415 | .pagination-item a:hover { 416 | text-decoration: underline; 417 | } 418 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ![Portfolio Starter](/content/media/banner.gif) 4 | 5 | This is a lightweight portfolio starterkit built with [Eleventy](https://www.11ty.dev/). It is geared towards designers, illustrators, architects, and any other individuals who are interested in sharing their work and activity. 6 | 7 | The intended user may not know how to code but is interested in the tech behind their website, is willing to write in [Markdown](https://daringfireball.net/projects/markdown/), and is happy to follow along with this documentation. 8 | 9 | Visit [portfolio-starter.sb-ph.com](https://portfolio-starter.sb-ph.com/) to check it out. The content and code in this repository drives the demo site. 10 | 11 | - [Features](#features) 12 | - [No-code setup](#no-code-setup) 13 | - [1. Get a GitHub account](#1-get-a-github-account) 14 | - [2. Get an account with a static hosting provider](#2-get-an-account-with-a-static-hosting-provider) 15 | - [3. Deploy your website](#3-deploy-your-website) 16 | - [4. Edit content in GitHub](#4-edit-content-in-github) 17 | - [Command line setup](#command-line-setup) 18 | - [1. Set up repo locally and on GitHub](#1-set-up-your-repository) 19 | - [2. Build or serve your website](#2-build-or-serve-your-website) 20 | - [3. Deploy your website](#3-deploy-your-website) 21 | - [4. Edit content locally](#4-edit-content-locally) 22 | - [Using a custom domain](#using-a-custom-domain) 23 | - [Updates and backups](#updates-and-backup) 24 | - [Content reference](#content-reference) 25 | - [Global data](#global-data) 26 | - [Markdown files and YAML frontmatter](#markdown-files-and-yaml-frontmatter) 27 | - [Basic properties](#basic-properties) 28 | - [Pages](#pages) 29 | - [Posts](#posts) 30 | - [Projects](#projects) 31 | - [Homepage](#homepage) 32 | - [Blog page](#blog-page) 33 | - [Error page](#error-page) 34 | - [RSS feed](#rss-feed) 35 | - [Media](#media) 36 | - [Altering and extending your site](#altering-and-extending-your-site) 37 | 38 | ## Features 39 | 40 | - Understated design with an eye towards accessibility [TODO] 41 | - A deliberately basic codebase that encourages tinkering and customisation even for people unfamiliar with code 42 | - Generates a static site; static sites tend to load a lot faster than database-driven websites and can be hosted for free 43 | - Supports posts, projects, pages, a one-level menu, and RSS out-of-the-box 44 | - Command line not required; you don’t have to use a code editor or the command line to edit or even deploy this website 45 | 46 | ## No-code setup 47 | 48 | “No-code” is a _little_ bit of a misnomer. Your content files are technically written in code, but it is a very readable syntax called Markdown (more on this later). What we mean by “no-code” is that you won’t have to touch the command line, Git, or open a code editor on your computer. 49 | 50 | ### 1. Get a GitHub account 51 | 52 | GitHub is a platform that stores code. Your website code and content are going to live on GitHub. If you have an account already, go ahead and log in. If not, [sign up for an account](https://github.com/join). The free individual account is sufficient. 53 | 54 | ### 2. Get an account with a static hosting provider 55 | 56 | [Netlify](https://www.netlify.com/) and [ZEIT](https://zeit.co/) are hosting providers that offer generous free tiers for people with static websites such as this. Netlify is perhaps a _tiny_ bit more straightforward for the following steps and seems to be used by more Eleventy users, but they are both solid platforms. If you have an account with either of these platforms already, log in. If you don’t, sign up for one. 57 | 58 | ### 3. Deploy your website 59 | 60 | By clicking one of the buttons below, you will clone this repository (i.e. create a duplicate version that lives in your own GitHub account) and then deploy this new website to your static hosting. 61 | 62 | Before proceeding, decide on a name for your repository. It should be something that is similar to the name of your site, but it should only include lowercase letters, numbers, and dashes. For example, the name of this repository is `portfolio-starter`. 63 | 64 | **If you have a Netlify account**, click this button and follow Netlify’s simple instructions to connect Netlify and GitHub: 65 | 66 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/sb-ph/portfolio-starter) 67 | 68 | **If you have a ZEIT account**, click the button below and then follow Zeit’s instructions: 69 | 70 | [![Deploy with ZEIT](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/sb-ph/portfolio-starter) 71 | 72 | Zeit’s instructions will walk you through how to install Zeit Now for GitHub so that it can create a repository for you and can deploy your changes. Give permission to all repositories when installing Zeit Now. Zeit should auto-detect that you are using Eleventy with settings as follows (you don’t need to worry about the development command): 73 | 74 | Build command: `npx @11ty/eleventy` 75 | Output directory: `_site` 76 | 77 | **When you have finished following the instructions above for Netlify or Zeit**, you will be redirected to your website dashboard. This displays your default subdomain and other important information about your site. You’ll also receive some emails letting you know that the services are connected. 78 | 79 | Zeit allows you to select the visibility of your repository when you set it up. Netlify, on the other hand, automatically creates a public repository. See the [GitHub documentation](https://help.github.com/en/github/administering-a-repository/setting-repository-visibility) if you want to adjust the visibility of your repository. 80 | 81 | ### 4. Edit content in GitHub 82 | 83 | To edit or add content without the command line, you need to use GitHub’s interface to navigate your files within the [`/content`](/content) folder in your repository. See the [content reference](#content-reference) to learn more about the structure of the `/content` folder and each of the files within it, particularly the [global data](#global-data) file that includes your website title and URL. 84 | 85 | To edit or delete an existing file, you must open the relevant file in GitHub and then click either the Edit button (the button with the pencil icon) or the Delete button (the button with the bin) in the upper-right corner above the page contents. 86 | 87 | If you want to add a new page, you must navigate to the [`posts`](/content/posts), [`projects`](/content/projects), or [`pages`](/content/pages) folder depending on what you want to add and then click the “Create new file” button near the top of the page. This will open a new editor page where you can add your filename and file contents. Since all text content is written in Markdown, the filename must end in `md` (i.e. `my-post-name.md`). 88 | 89 | If you want to add media, you must navigate to the [`media`](/content/media) folder and then click the “Upload files” button near the top of the page. This will give you an area to upload one or more files. See the [media guidance](#media) for tips on file types and sizes. 90 | 91 | To save edits or additions made in GitHub, you must commit your changes using GitHub’s interface at the base of the page. If you hooked up Netlify or ZEIT in the previous step, a commit will also tell GitHub to automatically deploy your changes. For more on what a commit is, see the [GitHub Glossary](https://help.github.com/en/github/getting-started-with-github/github-glossary). When you commit an edit or an addition directly in GitHub, you can use the default commit message that is pre-filled and should commit directly to the `master` branch (the default setting). 92 | 93 | ## Command line setup 94 | 95 | The command line setup instructions assume that you have familiarity with the command line, that you have version 8 or higher of Node.js installed on your computer, and that you have a GitHub account. 96 | 97 | ### 1. Set up your repository 98 | 99 | Clone repository locally into a named project folder by running `git clone https://github.com/sb-ph/portfolio-starter.git my-website` and then change to the new project folder by running `cd my-website`. Run `npm install` to install the dependencies including Eleventy. 100 | 101 | Run `rm -rf .git` to remove the Git history for a fresh start, and then run `git init` to initialise a new git repo. Commit all of your files to create a new `master` branch, and then [add your project to GitHub using the command line](https://help.github.com/en/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line). 102 | 103 | ### 2. Build or serve your website 104 | 105 | To build the website in to the gitignored `/_site` directory, run `npx @11ty/eleventy`. To spin up a server for local development or content editing, run `npx @11ty/eleventy --serve`. This will make your site available at , and the site will automatically reload when you make any changes. 106 | 107 | ### 3. Deploy your website 108 | 109 | If you want to use Netlify or ZEIT, follow their documentation to get your repository hooked up to your hosting account for continuous deployment. If you want to use another hosting provider, you can find your static files in the `/_site` directory after you generate a build. 110 | 111 | ### 4. Edit content locally 112 | 113 | To edit content locally, spin up a server by running `npx @11ty/eleventy --serve` and then adjust the Markdown and JSON files within the `/content` directory. If your site is hooked up to continuous deployment, make sure you commit your changes to the right branch (probably `master`) otherwise they will not be deployed. 114 | 115 | ## Using a custom domain 116 | 117 | Netlify and ZEIT give you a default subdomain automatically when you deploy your website, so this is an optional step. If you want to use your own domain, you should follow their instructions to set this up. [Read more about custom domains on ZEIT](https://zeit.co/docs/v2/custom-domains), or [read more about custom domains on Netlify](https://docs.netlify.com/domains-https/custom-domains/). 118 | 119 | Both of these guides will walk you through how to set up your domain’s Domain Name System (DNS). Note that **DNS can be delicate.** The DNS records for your domain tell browsers where to find your website and tell email servers how to direct emails to you. Be sure to write down any existing DNS records before changing anything. 120 | 121 | If you repoint the nameservers as part of the custom domain setup process and are already using your domain for email, you must add your MX records and any other records related to email to your new hosting provider _before_ you repoint the nameservers. If you do not, your email may go down. 122 | 123 | ## Updates and backups 124 | 125 | If you’re on an older version of Portfolio Starter and want to update it to a newer version, we recommend doing this manually by replacing all of the folders and files _except_ for the `/content` folder. You should back up your website before making any updates or major changes. Visit your repository homepage and click the green “Clone or download” button to download a zipped file of the entire repository, then store it somewhere safe. 126 | 127 | ## Content reference 128 | 129 | All of the content lives in the [`/content`](/content) folder. By default, Portfolio Starter is filled with the content in use on the [demo site](https://portfolio-starter.sb-ph.com/). 130 | 131 | The content includes the [global data](#global-data), [Markdown files for each page](#markdown-files-and-yaml-frontmatter), and [media](#media). It is important to format and organise each of these files in a particular way so that your site deploys without errors and looks as expected. 132 | 133 | ### Global data 134 | 135 | Certain metadata such as the site title and URL are used throughout the website. All of this global data is contained in the [`/content/_data/global.json`](/content/_data/global.json) file. 136 | 137 | The details in this file should be set when the website is first set up and then likely will not be touched again unless the website is moved or the domain changes. This is an example of the `global.json` file: 138 | 139 | ```json 140 | { 141 | "title": "Tortor Parturient Ridiculus", 142 | "lang": "en", 143 | "footer": "(c) Jane Doe, 2020", 144 | "url": "https://tpr.com", 145 | "author": { 146 | "name": "Jane Doe", 147 | "email": "mail@tpr.com" 148 | } 149 | } 150 | ``` 151 | 152 | This is the only piece of content that must be written in JSON, a text format that is used to structure data. JSON syntax is _very_ strict. All keys (i.e. `title`) and all strings (i.e. `My website`) are enclosed in double quotes, and all properties (i.e. `"title": "My website"`) are separated by commas except for the last property. All JSON objects are enclosed in curly brackets `{}`. **Incorrect syntax in this file will result in an error, meaning your changes will not be deployed.** 153 | 154 | These are the properties in more detail. 155 | 156 | | Key | Format | Default | Description | 157 | | --------- | ----------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------- | 158 | | `author` | JSON object | - | A JSON object that includes the name and email address of the website author | 159 | | `footer` | Markdown | a short credit | The footer text written in Markdown | 160 | | `lang`\* | text | `en` | The [IANA language tag](https://www.w3.org/International/questions/qa-html-language-declarations) that declares your website language | 161 | | `title`\* | text | - | The title of your website | 162 | | `url`\* | url | - | Your website URL | 163 | 164 | The footer includes a short credit by default. Feel free to replace it with whatever text works best for you. This might include a copyright notice, a colophon, contact details or other salient information. The footer does not support line breaks. 165 | 166 | ### Markdown files and YAML frontmatter 167 | 168 | All of the main content pages including the [posts](#posts), [projects](#projects), [pages](#pages), [homepage](#homepage), [404 error page](#error), [blog](#blog-page), and [RSS feed](#rss-feed) are written in [Markdown](https://daringfireball.net/projects/markdown/). Markdown allows you to write using an easy-to-read, easy-to-write plain text format that can be converted to valid HTML. Visit the [markdown-it](https://markdown-it.github.io/) website for a full list of formatting options including lists, links, headings, and more. You can also take a look at the source for this README file; it’s written in Markdown too! 169 | 170 | All Markdown files end in the `.md` extension, and this starterkit uses the rest of the filename to generate the page slug (the final part of a page’s URL). 171 | 172 | Any folders or files that are preceded by an underscore `_` will not be published. Because of that, you can use an underscore to create drafts such as `/content/posts/_testing-a-draft.md`. _However_, it is critical to remember that your draft will be visible to others in GitHub if your repository is public. 173 | 174 | Each Markdown file begins with [YAML](https://yaml.org/) frontmatter. YAML is a plain text syntax that allows human-readable text to be formatted as structured data. Frontmatter is the text at the top of the file that is fenced in by three dashes on either side, like so: 175 | 176 | ```yaml 177 | --- 178 | layout: page 179 | title: Contact 180 | description: Get in touch with me via email or phone. 181 | image: cloud-01.jpeg 182 | --- 183 | 184 | ``` 185 | 186 | The YAML frontmatter includes a series of properties—keys and values separated by a colon—that define page-specific metadata. Keys must always be written exactly as shown in this documentation. Writing `Layout` instead of `layout` for example will result in an error. 187 | 188 | ### Basic properties 189 | 190 | These are the basic YAML properties that should be used in the frontmatter on every page. 191 | 192 | | Key | Format | Default | Description | 193 | | -------------------- | ------ | ------- | -------------------------------------------------------------------- | 194 | | `description` | text | - | Short description of the page contents | 195 | | `eleventyNavigation` | YAML | - | A YAML object, described below | 196 | | `image` | text | - | The filename of the image that should be used for social media cards | 197 | | `layout`\* | text | - | The page layout | 198 | | `permalink` | text | various | The page permalink | 199 | | `title`\* | text | - | The page title | 200 | 201 | The page title and layout are required on every Markdown file. The page layout determines how the content is displayed. When creating new pages, you should use the `project`, `post`, or `page` layouts. 202 | 203 | The `description` property is highly recommended for all pages since it is used for social media cards and displayed in search engine results. It should be between 50 and 160 characters, and it should never be duplicated across different pages. 204 | 205 | If using an `image` for social media, refer to the documentation provided by the social media platforms for guidance about an appropriate image size. As a rule of thumb, a landscape-format 1200px wide JPG should be appropriate across most platforms. 206 | 207 | The `permalink` property allows you to set the URL for a page or to turn it off completely. The permalinks are always generated automatically, so you should rarely need to use this property. 208 | 209 | The `eleventyNavigation` property is slightly more complex. It tells the [Eleventy Navigation Plugin](https://www.11ty.dev/docs/plugins/navigation/) what to put in the menu. This is an example of the property in use on the About page [`/content/pages/about.md`](/content/pages/about.md): 210 | 211 | ```yaml 212 | --- 213 | layout: default 214 | title: About 215 | eleventyNavigation: 216 | key: About 217 | order: 0 218 | --- 219 | 220 | ``` 221 | 222 | The `key` sub-property tells Eleventy to add this page to the navigation with the text “About”. The `order` sub-property tells Eleventy that it should come first in the navigation. 223 | 224 | You can also add external links to the navigation, for example a link to your GitHub. See the [`/content/pages/github.md`](/content/pages/github.md) page for an example with the following frontmatter: 225 | 226 | If you want to add an external link to the navigation, you can create a new Markdown file (for example, `/content/external.md`) with the below frontmatter: 227 | 228 | ```yaml 229 | --- 230 | eleventyNavigation: 231 | key: GitHub ↗ 232 | order: 3 233 | url: http://github.com/sb-ph/portfolio-starter 234 | permalink: false 235 | --- 236 | 237 | ``` 238 | 239 | The `key` sub-property tells Eleventy to add this page to the navigation with the text “GitHub ” (`↗` is the HTML code for a northeast arrow). The `order` sub-property is set to 3 so that it comes last in the menu, and the `url` sub-property is set to the desired URL. The `permalink` property is set to false so that this doesn’t publish a corresponding page on our website. 240 | 241 | ### Pages 242 | 243 | Pages are found in the [`/content/pages`](/content/pages) folder. 244 | 245 | A page’s `layout` must be set to `page` in the frontmatter. Pages support only the [basic properties](#basic-properties) listed above. 246 | 247 | ### Posts 248 | 249 | Posts are found in the [`/content/posts`](/content/posts) folder. 250 | 251 | A post’s `layout` must be set to `post` in the frontmatter. Posts support the [basic properties](#basic-properties) as well as the `date` property. This is an example of a post’s frontmatter: 252 | 253 | ```yaml 254 | --- 255 | layout: post 256 | title: Demo post 257 | date: 2020-03-29 18:00:00 258 | --- 259 | 260 | ``` 261 | 262 | The `date` property determines the publish date of the post and thus the order in the blog and RSS. 263 | 264 | You can use the HTML comment `` within your Markdown content to generate an excerpt for your posts like so: 265 | 266 | ```markdown 267 | Maecenas faucibus mollis interdum. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. 268 | 269 | 270 | 271 | Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Donec ullamcorper nulla non metus auctor fringilla. Sed posuere consectetur est at lobortis. 272 | ``` 273 | 274 | If you use the `more` comment, only the text preceding the comment will be displayed on the blog and a “Read more” link will be displayed after this excerpt. 275 | 276 | ### Projects 277 | 278 | Projects are found in the [`/content/projects`](/content/projects) folder. 279 | 280 | A project’s `layout` must be set to `project` in the frontmatter. Projects support the [basic properties](#basic-properties) as well as the `dateStart`, `dateEnd`, and `media` properties. This is an example of the frontmatter for a project page. 281 | 282 | ```yaml 283 | --- 284 | layout: project 285 | title: Your project title 286 | dateStart: 1988-02-01 287 | dateEnd: 2001-03-01 288 | media: 289 | - type: image 290 | filename: cloud-01.jpeg 291 | size: md 292 | alt: A cloud 293 | caption: A collaboration with [Piper Haywood](https://piperhaywood.com) 294 | - type: video 295 | filename: ria-pacquee.mp4 296 | controls: false 297 | caption: A piece by [Ria Pacquée](http://riapacquee.com/) 298 | --- 299 | 300 | ``` 301 | 302 | And these are the project-specific properties in more detail: 303 | 304 | | Key | Format | Default | Description | 305 | | ----------- | ------------ | ------- | ------------------------------------------------------- | 306 | | `dateEnd`\* | `YYYY-MM-DD` | - | The end date of your project, used for sorting purposes | 307 | | `dateStart` | `YYYY-MM-DD` | - | The start date of your project | 308 | | `media` | YAML | - | A YAML list of media blocks, described below | 309 | 310 | The media property is a strictly-formatted YAML list that can contain image and video blocks. The properties that apply to both images and videos are outlined below. 311 | 312 | | Key | Format | Default | Description | 313 | | ------------ | -------- | ------- | ---------------------------------------------------------------- | 314 | | `caption` | markdown | - | A caption describing your media | 315 | | `filename`\* | text | - | The filename of your media | 316 | | `height` | integer | - | The height of your media in pixels | 317 | | `width` | integer | - | The width of your media in pixels | 318 | | `size` | text | `lg` | The size that the media should be displayed; `sm`, `md`, or `lg` | 319 | | `type`\* | text | - | `image` or `video` | 320 | 321 | Image blocks support the following additional properties: 322 | 323 | | Key | Format | Default | Description | 324 | | ---------- | ------- | ------- | ----------------------------------------------------------------- | 325 | | `alt`\* | text | - | The alt text for your image | 326 | | `featured` | boolean | false | Whether or not the image should be used to represent this project | 327 | 328 | The `featured` property is used to determine which image should be used to represent this project on the homepage. If multiple images are marked as featured, then the first one will be used. 329 | 330 | Video blocks allow the following additional properties. Note that video autoplay is only supported in certain browsers and devices. 331 | 332 | | Key | Format | Default | Description | 333 | | ---------- | ------- | ------- | ----------------------------------------------------- | 334 | | `controls` | boolean | false | Whether or not the video controls should be displayed | 335 | | `loop` | boolean | false | Whether or not the video should loop | 336 | | `autoplay` | boolean | false | Whether or not the video should automatically play | 337 | | `muted` | boolean | false | Whether or not the video should be muted | 338 | 339 | ### Homepage 340 | 341 | The homepage is the [`/content/index.md`](/content/index.md) file. 342 | 343 | The homepage’s `layout` must be set to `home` in the frontmatter. The homepage supports most [basic properties](#basic-properties), however the `permalink` property must not be used. 344 | 345 | The additional `entries` property allows you to specify exactly which projects you want to appear on the homepage. If the `entries` property is not filled out, then the homepage will automatically display all of the projects in order with the most recent first. 346 | 347 | This is an example of the `entries` property in use: 348 | 349 | ```yaml 350 | --- 351 | layout: home 352 | entries: 353 | - portfolio-starter 354 | - sample-project-muybridge 355 | - sample-project-optics 356 | - sample-project-eames 357 | - sample-project-letters 358 | - sample-project-traffic-signs 359 | --- 360 | 361 | ``` 362 | 363 | The first `featured` image for each project is shown. If no images are `featured`, then the first image will be shown. 364 | 365 | ### Blog page 366 | 367 | The blog page is the [`/content/posts.md`](/content/posts.md) file. 368 | 369 | The blog page’s `layout` must be set to `posts` in the frontmatter. The blog page supports the [basic properties](#basic-properties). Markdown written beneath the frontmatter is not displayed. 370 | 371 | ### Error page 372 | 373 | The 404 error page is the [`/content/404.md`](/content/404.md) file. 374 | 375 | The 404 error page’s `layout` must be set to `error` in the frontmatter. The error page supports only the `layout` and `title` [basic properties](#basic-properties). The `permalink` property must not be used. 376 | 377 | ### RSS feed 378 | 379 | The RSS page is the [`/content/rss.md`](/content/rss.md) file. 380 | 381 | The RSS page’s `layout` must be set to `feed` in the frontmatter. The RSS page supports only the `layout` and `title` [basic properties](#basic-properties). The `permalink` property must not be used. 382 | 383 | The RSS feed is automatically published to `/feed.xml`, so for example `https://yoursite.com/feed.xml`. If you want to add an RSS link in your navigation, see the [basic properties](#basic-properties) guidance to add an external link. 384 | 385 | ## Media 386 | 387 | All of the media is stored in the `/content/media` folder. 388 | 389 | Media files must be as small as possible to save space in your GitHub repository and your hosting. [Read more about GitHub’s repository limits](https://help.github.com/en/github/managing-large-files/what-is-my-disk-quota). Smaller media files will also load faster for your readers. 390 | 391 | Small images should be around 800px wide, medium images should be around 1400px wide, and large images should be around 3000px wide. Images with large areas of flat colour may work best as PNGs. Images with more detail such as photography should be saved as JPGs. 392 | 393 | The more images you add to a page, the longer it will take for that page to load. Be judicious about how many images you add to any one page. 394 | 395 | ## Altering and extending your site 396 | 397 | As mentioned above, this is a deliberately basic codebase that welcomes tinkering. 398 | 399 | Smaller customisations such as altering the CSS can be done without the command line by editing the CSS file directly in GitHub. More extensive customisations are best done by developing locally with the command line. Refer to the [Eleventy documentation](https://www.11ty.dev/docs/) if you are interested in altering the [Nunjucks](https://mozilla.github.io/nunjucks/) layouts or snippets in [`/_includes`](/_includes). 400 | 401 | These are a few suggestions for altering and extending this site: 402 | 403 | - Edit the CSS to change the fonts or background colours 404 | - Edit the [`/_includes/layouts/base.njk`](/_includes/layouts/base.njk) layout to create a more complex footer 405 | - Add tags to the blog 406 | - Add project attributes such as `client` or `category` 407 | - Add a Projects page that displays a list of all projects 408 | - Alter the templates to work with a content delivery network (CDN) such as [`imgix`](https://www.imgix.com/) so that your media doesn’t live on GitHub 409 | - Add your projects to AirTable and use their API to populate your website 410 | - Hook your site up to a content management system (CMS) like [Forestry](https://forestry.io/) or [Sanity](https://www.sanity.io/) 411 | 412 | If you’re interested in our help making some modifications, just [get in touch](mailto:mail@sb-ph.com) and we’ll discuss! 413 | --------------------------------------------------------------------------------