├── .gitignore
├── exampleSite
├── content
│ ├── .gitignore
│ ├── happy
│ │ └── index.md
│ ├── people
│ │ └── index.md
│ ├── tints
│ │ └── index.md
│ ├── travel
│ │ └── index.md
│ ├── empty
│ │ └── index.md
│ ├── love
│ │ └── index.md
│ ├── animals
│ │ └── index.md
│ ├── flowers
│ │ └── index.md
│ ├── food
│ │ └── index.md
│ └── architecture
│ │ └── index.md
├── themes
│ └── photo-stream
├── .gitignore
├── config.toml
└── fetch-photos.sh
├── static
├── favicon.png
├── social-preview.png
├── touch-icon-iphone.png
├── img
│ ├── icon-rss.svg
│ ├── icon-info.svg
│ ├── icon-right.svg
│ ├── icon-left.svg
│ ├── icon-github.svg
│ └── icon-twitter.svg
├── js
│ ├── lazy-loading.js
│ └── photos.js
└── favicon.svg
├── layouts
├── _default
│ ├── baseof.html
│ ├── list.html
│ ├── single.html
│ └── album.html
├── 404.html
└── partials
│ ├── links.html
│ ├── photos.html
│ └── head.html
├── i18n
├── en.yaml
└── fr.yaml
├── archetypes
└── album.md
├── theme.toml
├── LICENSE
├── README.md
└── assets
└── css
└── master.scss
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 |
--------------------------------------------------------------------------------
/exampleSite/content/.gitignore:
--------------------------------------------------------------------------------
1 | *.jpeg
--------------------------------------------------------------------------------
/exampleSite/themes/photo-stream:
--------------------------------------------------------------------------------
1 | ../../
--------------------------------------------------------------------------------
/exampleSite/.gitignore:
--------------------------------------------------------------------------------
1 | resources
2 | public
--------------------------------------------------------------------------------
/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nmasse-itix/photo-stream/HEAD/static/favicon.png
--------------------------------------------------------------------------------
/static/social-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nmasse-itix/photo-stream/HEAD/static/social-preview.png
--------------------------------------------------------------------------------
/static/touch-icon-iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nmasse-itix/photo-stream/HEAD/static/touch-icon-iphone.png
--------------------------------------------------------------------------------
/exampleSite/config.toml:
--------------------------------------------------------------------------------
1 | title = "Photo Stream"
2 | theme = "photo-stream"
3 | [params]
4 | album_date_format = "2006"
5 |
--------------------------------------------------------------------------------
/exampleSite/content/happy/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2015-01-01"
3 | title: Happy
4 | sort_by: "Name"
5 | resources:
6 | - src: '**.jpeg'
7 | ---
8 |
--------------------------------------------------------------------------------
/exampleSite/content/people/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2013-01-01"
3 | title: People
4 | sort_by: "Name"
5 | resources:
6 | - src: '**.jpeg'
7 | ---
8 |
--------------------------------------------------------------------------------
/exampleSite/content/tints/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2012-01-01"
3 | title: Tints
4 | sort_by: "Name"
5 | resources:
6 | - src: '**.jpeg'
7 | ---
8 |
--------------------------------------------------------------------------------
/exampleSite/content/travel/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2011-01-01"
3 | title: Travel
4 | sort_by: "Name"
5 | resources:
6 | - src: '**.jpeg'
7 | ---
8 |
--------------------------------------------------------------------------------
/exampleSite/content/empty/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2020-01-01"
3 | title: Architecture
4 | sort_by: "Exif.Date"
5 | resources:
6 | - src: '**.jpeg'
7 | ---
8 |
--------------------------------------------------------------------------------
/layouts/_default/baseof.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{- partial "head.html" . -}}
4 |
5 | {{- block "main" . }}{{- end }}
6 |
7 |
8 |
--------------------------------------------------------------------------------
/layouts/_default/list.html:
--------------------------------------------------------------------------------
1 |
2 | {{ define "main"}}
3 |
4 | {{ range .Data.Pages.ByDate.Reverse }}
5 | {{ .Render "album" }}
6 | {{ end }}
7 |
8 | {{ end }}
9 |
--------------------------------------------------------------------------------
/exampleSite/content/love/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2014-01-01"
3 | title: Love
4 | sort_by: "Name"
5 | resources:
6 | - src: 'love02.jpeg'
7 | params:
8 | cover: true
9 | - src: '**.jpeg'
10 | ---
11 |
--------------------------------------------------------------------------------
/exampleSite/content/animals/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2016-01-01"
3 | title: Animals
4 | sort_by: "Name"
5 | resources:
6 | - src: 'camel.jpeg'
7 | params:
8 | cover: true
9 | - src: '**.jpeg'
10 | ---
11 |
--------------------------------------------------------------------------------
/exampleSite/content/flowers/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2019-01-01"
3 | title: Flowers
4 | sort_by: "Name"
5 | resources:
6 | - src: 'flower09.jpeg'
7 | params:
8 | cover: true
9 | - src: '**.jpeg'
10 | ---
11 |
--------------------------------------------------------------------------------
/exampleSite/content/food/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2017-01-01"
3 | title: Food and drink
4 | sort_by: "Name"
5 | resources:
6 | - src: 'food06.jpeg'
7 | params:
8 | cover: true
9 | - src: '**.jpeg'
10 | ---
11 |
--------------------------------------------------------------------------------
/exampleSite/content/architecture/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: "2020-01-01"
3 | title: Architecture
4 | sort_by: "Name"
5 | resources:
6 | - src: 'archi10.jpeg'
7 | params:
8 | cover: true
9 | - src: '**.jpeg'
10 | ---
11 |
--------------------------------------------------------------------------------
/layouts/404.html:
--------------------------------------------------------------------------------
1 | {{ define "main"}}
2 |
8 | {{ end }}
9 |
--------------------------------------------------------------------------------
/i18n/en.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | - id: pageNotFound
3 | translation: "Sorry, this page does not exist."
4 | - id: visitHomePage
5 | translation: "Return to home page"
6 | - id: open
7 | translation: "Open"
8 | - id: previous
9 | translation: "Previous"
10 | - id: next
11 | translation: "Next"
--------------------------------------------------------------------------------
/layouts/_default/single.html:
--------------------------------------------------------------------------------
1 | {{ define "main"}}
2 |
3 | {{ $photos := sort (.Resources.ByType "image") (index .Params "sort_by" |default "Name") (index .Params "sort_order" |default "asc") }}
4 | {{- partial "photos.html" $photos -}}
5 |
6 | {{ end }}
7 |
--------------------------------------------------------------------------------
/i18n/fr.yaml:
--------------------------------------------------------------------------------
1 | - id: pageNotFound
2 | translation: "Désolé, cette page n'existe pas !"
3 | - id: visitHomePage
4 | translation: "Retour à la page d'accueil"
5 | - id: open
6 | translation: "Ouvrir"
7 | - id: previous
8 | translation: "Précédent"
9 | - id: next
10 | translation: "Suivant"
11 |
--------------------------------------------------------------------------------
/archetypes/album.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "{{ replace .Name "-" " " | title }}"
3 | date: {{ .Date }}
4 | # By default, photos are sorted by filename
5 | sort_by: Name
6 | # But you can sort instead by EXIF date if you prefer
7 | # sort_by: Exif.Date
8 | resources:
9 | #
10 | # You can set the album cover image by setting the param 'cover: true'
11 | # on a photo.
12 | #
13 | # - src: 'IMG_1234.jpeg'
14 | # params:
15 | # cover: true
16 | #
17 | - src: '**.jpeg'
18 | - src: '**.jpg'
19 | ---
20 |
--------------------------------------------------------------------------------
/theme.toml:
--------------------------------------------------------------------------------
1 | # theme.toml template for a Hugo theme
2 | # See https://github.com/gohugoio/hugoThemes#themetoml for an example
3 |
4 | name = "Photo Stream"
5 | license = "MIT"
6 | licenselink = "https://github.com/nmasse-itix/photo-stream/blob/master/LICENSE"
7 | description = "A theme to host your photo albums, based on maxvoltar's photo-stream theme"
8 | homepage = "https://www.itix.fr/"
9 | tags = [ "photo-stream", "responsive", "flexbox", "minimalistic", "gallery" ]
10 | features = [ "rss", "page resources", "responsive" ]
11 | min_version = "0.68.0"
12 |
13 | [author]
14 | name = "Nicolas Massé"
15 | homepage = "https://www.itix.fr/"
16 |
17 | [original]
18 | name = "Tim Van Damme"
19 | homepage = "http://timvandamme.com/"
20 | repo = "https://github.com/maxvoltar/photo-stream"
21 |
--------------------------------------------------------------------------------
/static/img/icon-rss.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/layouts/partials/links.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/icon-info.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | icon-info
5 | Created with Sketch Beta.
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/static/img/icon-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 YOUR_NAME_HERE
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/static/img/icon-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | icon-left
5 | Created with Sketch Beta.
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
--------------------------------------------------------------------------------
/static/img/icon-github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/layouts/_default/album.html:
--------------------------------------------------------------------------------
1 | {{ $photos := .Resources.ByType "image" }}
2 | {{ $scratch := newScratch }}
3 | {{ $scratch.Set "index" 0 }}
4 | {{ range $i, $photo := $photos }}
5 | {{ if index $photo.Params "cover" }}
6 | {{ $scratch.Set "index" $i }}
7 | {{ end }}
8 | {{ end }}
9 | {{ if gt (len $photos) 0 }}
10 | {{ $photo := index $photos ($scratch.Get "index") }}
11 | {{ if $photo.Exif }}
12 | {{ $orientation := index $photo.Exif.Tags "Orientation" }}
13 | {{ if eq $orientation 6 }}
14 | {{ $scratch.Set "image_rotation" "r270" }}
15 | {{ else if eq $orientation 8 }}
16 | {{ $scratch.Set "image_rotation" "r90" }}
17 | {{ else if eq $orientation 3 }}
18 | {{ $scratch.Set "image_rotation" "r180" }}
19 | {{ else }}
20 | {{ $scratch.Set "image_rotation" "" }}
21 | {{ end }}
22 | {{ else }}
23 | {{ $scratch.Set "image_rotation" "" }}
24 | {{ end }}
25 | {{ $tint := $photo.Fill "1x1 Box png" }}
26 | {{ $thumbnail := $photo.Fit (print "800x800 Lanczos q80 " ($scratch.Get "image_rotation")) }}
27 |
28 |
29 | {{ i18n "open" }}
30 | {{ .Title }}
31 | {{ dateFormat ((index .Site.Params "album_date_format") | default "01/2006") .Date }}
32 |
33 | {{ end }}
34 |
--------------------------------------------------------------------------------
/static/js/lazy-loading.js:
--------------------------------------------------------------------------------
1 | // Name: Lazy Load 2.0.0-rc.2
2 | // Source: https://github.com/tuupola/lazyload
3 | // License: MIT license
4 | // Copyright: 2007-2019 Mika Tuupola
5 |
6 | !function(t,e){"object"==typeof exports?module.exports=e(t):"function"==typeof define&&define.amd?define([],e):t.LazyLoad=e(t)}("undefined"!=typeof global?global:this.window||this.global,function(t){"use strict";function e(t,e){this.settings=s(r,e||{}),this.images=t||document.querySelectorAll(this.settings.selector),this.observer=null,this.init()}"function"==typeof define&&define.amd&&(t=window);const r={src:"data-src",srcset:"data-srcset",selector:".lazyload",root:null,rootMargin:"0px",threshold:0},s=function(){let t={},e=!1,r=0,o=arguments.length;"[object Boolean]"===Object.prototype.toString.call(arguments[0])&&(e=arguments[0],r++);for(;r
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
--------------------------------------------------------------------------------
/layouts/partials/photos.html:
--------------------------------------------------------------------------------
1 | {{ $photos := . }}
2 | {{ $size := len $photos }}
3 | {{ $scratch := newScratch }}
4 | {{ range $index, $photo := $photos }}
5 | {{ if $photo.Exif }}
6 | {{ $orientation := index $photo.Exif.Tags "Orientation" }}
7 | {{ if eq $orientation 6 }}
8 | {{ $scratch.Set "image_rotation" "r270" }}
9 | {{ else if eq $orientation 8 }}
10 | {{ $scratch.Set "image_rotation" "r90" }}
11 | {{ else if eq $orientation 3 }}
12 | {{ $scratch.Set "image_rotation" "r180" }}
13 | {{ else }}
14 | {{ $scratch.Set "image_rotation" "" }}
15 | {{ end }}
16 | {{ else }}
17 | {{ $scratch.Set "image_rotation" "" }}
18 | {{ end }}
19 | {{ $tint := $photo.Fill "1x1 Box png" }}
20 | {{ $thumbnail := $photo.Fit (print "800x800 Lanczos q80 " ($scratch.Get "image_rotation")) }}
21 | {{ $large := $photo.Fit (print "2048x2048 Lanczos q85 " ($scratch.Get "image_rotation")) }}
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{ i18n "open" }}
29 | Close
30 |
31 | {{ if $index }}
32 | {{ $previous_photo := (index $photos (sub $index 1)) }}
33 |
34 | {{ i18n "previous" }}
35 |
36 | {{ end }}
37 |
38 | {{ if lt $index (sub $size 1) }}
39 | {{ $next_photo := (index $photos (add $index 1)) }}
40 |
41 | {{ i18n "next" }}
42 |
43 | {{ end }}
44 |
45 | {{ end }}
--------------------------------------------------------------------------------
/layouts/partials/head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ $url := replace .Permalink ( printf "%s" .Site.BaseURL) "" }}
4 | {{ if eq $url "/" }}
5 | {{ $.Scratch.Set "title" .Site.Title }}
6 | {{ else }}
7 | {{ $.Scratch.Set "title" .Title }}
8 | {{ end }}
9 |
11 |
12 | {{ $.Scratch.Get "title" }}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ range .AlternativeOutputFormats -}}
22 | {{ printf ` ` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
23 | {{ end -}}
24 |
25 |
26 | {{ template "_internal/opengraph.html" . }}
27 |
28 |
29 | {{ $scss := resources.Get "/css/master.scss" }}
30 | {{ $css := $scss | resources.ToCSS }}
31 |
32 |
33 | {{ if .Site.Params.cachebuster }}
34 | {{ $t := now }}
35 |
36 | {{ range .Site.Params.extracssfiles }}
37 |
38 | {{ end }}
39 |
40 |
41 | {{ else }}
42 |
43 | {{ range .Site.Params.extracssfiles }}
44 |
45 | {{ end }}
46 |
47 |
48 | {{ end }}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/static/js/photos.js:
--------------------------------------------------------------------------------
1 | const ESCAPE = 27;
2 | const RIGHT = 39;
3 | const LEFT = 37;
4 | const TARGET_CLASS = 'target';
5 |
6 | const clickNavigationButton = (buttonClass) => {
7 | const id = window.history.state && window.history.state.id;
8 | if (id) {
9 | const photo = document.getElementById(id);
10 | console.log(photo);
11 | const button = photo.querySelector(buttonClass);
12 | console.log(button);
13 | button && button.click();
14 | }
15 | }
16 |
17 | const openPhoto = (id, href) => {
18 | console.log(`Opening photo ${id}...`);
19 | const photo = document.getElementById(id);
20 | const title = photo.getAttribute('title');
21 | removeTargetClass();
22 | photo.classList.add(TARGET_CLASS);
23 | document.title = title;
24 | if (href) {
25 | window.history.pushState({id: id}, '', href);
26 | }
27 | }
28 |
29 | const closePhoto = (href) => {
30 | console.log(`Closing photo...`);
31 | const title = document.querySelector('head title').getAttribute('data-title');
32 | removeTargetClass();
33 | document.title = title;
34 | if (href) {
35 | window.history.pushState({}, '', href);
36 | }
37 | }
38 |
39 | const removeTargetClass = () => {
40 | let targets = document.querySelectorAll(`.${TARGET_CLASS}`);
41 | targets.forEach((target) => {
42 | target.classList.remove(TARGET_CLASS);
43 | });
44 | }
45 |
46 | const handleClick = (selector, event, callback) => {
47 | if (event.target.matches(selector)) {
48 | callback();
49 | event.preventDefault();
50 | }
51 | }
52 |
53 | const handleKey = (keyCode, event, callback) => {
54 | if (event.keyCode === keyCode) {
55 | callback();
56 | event.preventDefault();
57 | }
58 | }
59 |
60 | window.onpopstate = function(event) {
61 | if (event.state && event.state.id) {
62 | const id = event.state.id;
63 | openPhoto(id, null);
64 | } else {
65 | closePhoto(null);
66 | }
67 | }
68 |
69 | document.addEventListener('keydown', (event) => {
70 | handleKey(ESCAPE, event, () => {
71 | clickNavigationButton('.close');
72 | });
73 |
74 | handleKey(RIGHT, event, () => {
75 | clickNavigationButton('.next');
76 | });
77 |
78 | handleKey(LEFT, event, () => {
79 | clickNavigationButton('.previous');
80 | });
81 | });
82 |
83 | document.addEventListener('click', (event) => {
84 | handleClick('[data-target][href]', event, () => {
85 | const id = event.target.getAttribute('data-target');
86 | const href = event.target.getAttribute('href');
87 | openPhoto(id, href);
88 | });
89 |
90 | handleClick('[href].close', event, () => {
91 | const href = event.target.getAttribute('href');
92 | closePhoto(href);
93 | });
94 | });
95 |
96 | window.addEventListener('load', (event) => {
97 | console.log("Loaded !");
98 | const id = window.location.hash.substr(1);
99 | if (id != "") {
100 | openPhoto(id, "#" + id);
101 | }
102 | lazyload();
103 | });
104 |
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Photo Stream
2 |
3 | A theme to showcase your photo albums, powered by [Hugo](https://gohugo.io).
4 |
5 | **A live demo is available [at hugo-photo-stream.netlify.app](https://hugo-photo-stream.netlify.app/).**
6 |
7 | ## Features
8 |
9 | This theme is basically a port of [maxvoltar's photo-stream theme](https://github.com/maxvoltar/photo-stream).
10 | Thanks to him for this nice creation!
11 |
12 | This theme features:
13 |
14 | * Lazy loading of photos (a photo is downloaded when it appears in the viewport)
15 | * Albums containing photos
16 | * Photos thumbnail are resized to fit 800x800
17 | * The large version is resized to fit 2048x2048
18 | * The background is filled with a tint matching the photo
19 | * Keyboard shortcuts for previous / next / back to list
20 |
21 | ## Installation
22 |
23 | From the root of your Hugo site, type the following:
24 |
25 | ```sh
26 | git submodule add https://github.com/nmasse-itix/photo-stream.git themes/photo-stream
27 | git submodule init
28 | git submodule update
29 | ```
30 |
31 | Now you can get updates of this theme in the future by updating the submodule:
32 |
33 | ```sh
34 | git submodule update --remote themes/photo-stream
35 | ```
36 |
37 | ## Configuration
38 |
39 | After installation, take a look at the `exampleSite` folder inside `themes/photo-stream`.
40 |
41 | To get started, copy the `config.toml` file inside `exampleSite` to the root of your Hugo site:
42 |
43 | ```sh
44 | cp themes/photo-stream/exampleSite/config.toml .
45 | ```
46 |
47 | Now edit this file and add your own information. Note that some fields can be omitted.
48 |
49 | ## How to create an album
50 |
51 | The theme provides an **archetype** named `album`.
52 | Create a new album with the `hugo new` command.
53 |
54 | ```sh
55 | hugo new my-album/index.md -k album
56 | ```
57 |
58 | ## How to add photos
59 |
60 | To add photos to an album, simply copy your JPEG files in the album directory, **under content, NOT static!**
61 |
62 | ```sh
63 | cp path/to/DCIM_*.jpeg content/my-album/
64 | ```
65 |
66 | ## How to customize an album
67 |
68 | A minimal `index.md` looks like this:
69 |
70 | ```yaml
71 | ---
72 | date: "2016-01-01"
73 | title: Animals
74 | - src: '**.jpeg'
75 | ---
76 | ```
77 |
78 | This index file defines an album with a date, a title and instructs to add all JPEG files to the album.
79 |
80 | But a usual `index.md` would include more customization:
81 |
82 | ```yaml
83 | ---
84 | date: "2016-01-01"
85 | title: Animals
86 | sort_by: "Exif.Date"
87 | resources:
88 | - src: 'camel.jpeg'
89 | params:
90 | cover: true
91 | - src: '**.jpeg'
92 | - src: '**.jpg'
93 | ---
94 | ```
95 |
96 | This index also specifies:
97 |
98 | * To sort photos by date (specified in the EXIF metadata).
99 | * To also include files with `.jpg` extension.
100 | * To set `camel.jpeg` as the cover photo for the album.
101 |
102 | ## Global configuration
103 |
104 | The Date format for the album can be set in your `config.toml`.
105 |
106 | ```toml
107 | [params]
108 | album_date_format = "01/2006"
109 | ```
110 |
111 | Check the Go documentation for possible formats: [time.Format](https://golang.org/pkg/time/#Time.Format).
112 |
113 | ## Demo Website
114 |
115 | A live demo is available [at hugo-photo-stream.netlify.app](https://hugo-photo-stream.netlify.app/) but you can have a look by yourself at the example site.
116 |
117 | ```sh
118 | cd themes/photo-stream/exampleSite
119 | ./fetch-photos.sh
120 | hugo serve
121 | ```
122 |
123 | On netlify, you need to customize the **build command** and **publish directory**:
124 |
125 | * Build command: `cd exampleSite && ./fetch-photos.sh && hugo`
126 | * Publish directory: `exampleSite/public`
127 |
128 | And add an environment variable to install a recent version of Hugo.
129 |
130 | * `HUGO_VERSION=0.68.3`
131 |
132 |
--------------------------------------------------------------------------------
/static/img/icon-twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
10 |
--------------------------------------------------------------------------------
/assets/css/master.scss:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | list-style: none;
5 | font-size: 1em;
6 | box-sizing: border-box;
7 | }
8 |
9 | @mixin dark {
10 | @media (prefers-color-scheme: dark) {
11 | @content
12 | }
13 | }
14 |
15 | @mixin button {
16 | display: block;
17 | border-radius: 16px;
18 | text-indent: 150%;
19 | overflow: hidden;
20 | white-space: nowrap;
21 | width: 32px;
22 | height: 32px;
23 | background-repeat: no-repeat;
24 | background-position: 8px;
25 | background-size: 16px;
26 | transition: background-color .1s linear;
27 | -webkit-backdrop-filter: blur(20px);
28 | -moz-backdrop-filter: blur(20px);
29 | backdrop-filter: blur(20px);
30 | background-color: rgba(200, 200, 200, .25);
31 | // Disable until Safari supports `prefers-color-scheme` in SVG's
32 | // @include dark {
33 | // background-color: rgba(0, 0, 0, .25);
34 | // }
35 |
36 | &:hover,
37 | &:focus {
38 | background-color: rgba(200, 200, 200, .5);
39 | // Disable until Safari supports `prefers-color-scheme` in SVG's
40 | // @include dark {
41 | // background-color: rgba(0, 0, 0, .5);
42 | // }
43 | }
44 |
45 | &:active {
46 | background-color: rgba(200, 200, 200, .25);
47 | // Disable until Safari supports `prefers-color-scheme` in SVG's
48 | // @include dark {
49 | // background-color: rgba(0, 0, 0, .75);
50 | // }
51 | }
52 |
53 |
54 | }
55 |
56 | body,
57 | html {
58 | min-height: 100%;
59 | display: flex;
60 | }
61 |
62 | body {
63 | flex-grow: 1;
64 | font: 16px/24px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
65 | background: #fff;
66 | color: #2e2f30;
67 |
68 | @include dark {
69 | background: #000;
70 | color: #eee;
71 | }
72 | }
73 |
74 | // PHOTO GRID
75 |
76 | .grid {
77 | display: flex;
78 | flex-wrap: wrap;
79 | position: relative;
80 | align-content: flex-start;
81 |
82 | &:after {
83 | content: "";
84 | display: block;
85 | flex-grow: 10;
86 | outline: 2px solid #fff;
87 | position: relative;
88 |
89 | @include dark {
90 | outline-color: #000;
91 | }
92 | }
93 |
94 | .item {
95 | height: 40vh;
96 | flex-grow: 1;
97 | outline: 2px solid #fff;
98 | position: relative;
99 | background-size: 100%;
100 |
101 | @include dark {
102 | outline-color: #000;
103 | }
104 |
105 | img {
106 | max-height: 100%;
107 | min-width: 100%;
108 | min-height: 100%;
109 | width: auto;
110 | object-fit: cover;
111 | display: block;
112 | }
113 |
114 | .open,
115 | .close {
116 | position: absolute;
117 | top: 0;
118 | right: 0;
119 | bottom: 0;
120 | left: 0;
121 | text-indent: 150%;
122 | overflow: hidden;
123 | white-space: nowrap;
124 | }
125 |
126 | .open {
127 | cursor: zoom-in;
128 | background-color: rgba(0, 0, 0, 0);
129 | transition: background-color .15s ease-out;
130 |
131 | &:hover,
132 | &:focus {
133 | background-color: rgba(0, 0, 0, .25);
134 | }
135 |
136 | &:active {
137 | background-color: rgba(0, 0, 0, .5);
138 | }
139 | }
140 |
141 | .close {
142 | display: none;
143 | cursor: zoom-out;
144 | }
145 |
146 | .full {
147 | display: none;
148 | }
149 |
150 | .previous,
151 | .next {
152 | position: absolute;
153 | top: 0;
154 | bottom: 0;
155 | left: 0;
156 | z-index: 20;
157 | width: 80px;
158 | display: none;
159 | align-items: center;
160 | justify-content: center;
161 |
162 | span {
163 | @include button;
164 | background-image: url(../img/icon-left.svg);
165 | pointer-events: none;
166 | }
167 |
168 | &:hover,
169 | &:focus {
170 | span {
171 | background-color: rgba(200, 200, 200, .5);
172 | // Disable until Safari supports `prefers-color-scheme` in SVG's
173 | // @include dark {
174 | // background-color: rgba(0, 0, 0, .5);
175 | // }
176 | }
177 | }
178 |
179 | &:active {
180 | span {
181 | background-color: rgba(200, 200, 200, .25);
182 | // Disable until Safari supports `prefers-color-scheme` in SVG's
183 | // @include dark {
184 | // background-color: rgba(0, 0, 0, .75);
185 | // }
186 | }
187 | }
188 | }
189 |
190 | .next {
191 | right: 0;
192 | left: auto;
193 |
194 | span {
195 | background-image: url(../img/icon-right.svg);
196 | }
197 | }
198 |
199 | .name {
200 | display: flex;
201 | position: absolute;
202 | right: 4px;
203 | bottom: 12px;
204 | color: #fff;
205 | text-shadow: #000 0 1px 1px, #000 0 2px 4px;
206 | opacity: 1;
207 | font-size: larger;
208 | margin-right: 12px;
209 | }
210 |
211 | .date {
212 | display: flex;
213 | position: absolute;
214 | left: 4px;
215 | bottom: 12px;
216 | color: #fff;
217 | text-shadow: #000 0 1px 1px, #000 0 2px 4px;
218 | opacity: 1;
219 | margin-left: 12px;
220 | }
221 |
222 | // PHOTO DETAIL
223 |
224 | &.target {
225 | position: fixed;
226 | top: 0;
227 | right: 0;
228 | bottom: 0;
229 | left: 0;
230 | height: 100%;
231 | z-index: 10;
232 | background: #fff;
233 | display: flex;
234 | align-items: center;
235 |
236 | @media (prefers-color-scheme: dark) {
237 | background: #000;
238 | }
239 |
240 | .open {
241 | display: none;
242 | }
243 |
244 | .close {
245 | display: block;
246 | }
247 |
248 | img {
249 | object-fit: contain;
250 | animation: fade-in .5s ease-out;
251 | }
252 |
253 | .full {
254 | display: flex;
255 | position: absolute;
256 | top: 0;
257 | right: 0;
258 | bottom: 0;
259 | left: 0;
260 | animation: fade-in .5s ease-out;
261 |
262 | span {
263 | flex-grow: 1;
264 | background-size: contain;
265 | background-repeat: no-repeat;
266 | background-position: center;
267 | }
268 | }
269 |
270 | .meta {
271 | display: none !important;
272 | }
273 |
274 | .previous,
275 | .next {
276 | display: flex;
277 | }
278 | }
279 | }
280 | }
281 |
282 | // SOCIAL LINKS
283 |
284 | .links {
285 | position: fixed;
286 | bottom: 24px;
287 | right: 24px;
288 | display: flex;
289 | flex-wrap: wrap;
290 | margin-left: 16px;
291 |
292 | li {
293 | margin-left: 8px;
294 |
295 | a {
296 | @include button;
297 | }
298 |
299 | &.github {
300 | a {
301 | background-image: url(../img/icon-github.svg);
302 | }
303 | }
304 |
305 | &.rss {
306 | a {
307 | background-image: url(../img/icon-rss.svg);
308 | }
309 | }
310 |
311 | &.link {
312 | a {
313 | text-indent: 0;
314 | width: auto;
315 | font-size: 13px;
316 | line-height: 32px;
317 | text-transform: uppercase;
318 | padding: 0 12px;
319 | color: rgba(0, 0, 0, .75);
320 | font-weight: 600;
321 | text-decoration: none;
322 | }
323 | }
324 | }
325 | }
326 |
327 | // 404
328 |
329 | .four-oh-four {
330 | flex-grow: 1;
331 | display: flex;
332 | flex-direction: column;
333 | height: 100%;
334 | padding: 64px;
335 | align-items: center;
336 | justify-content: center;
337 | text-align: center;
338 |
339 | img {
340 | width: 64px;
341 | vertical-align: bottom;
342 | margin-bottom: 24px;
343 | }
344 |
345 | h1 {
346 | font-size: 32px;
347 | line-height: 48px;
348 | font-weight: 700;
349 | }
350 |
351 | p {
352 | margin-bottom: 32px;
353 | }
354 |
355 | a {
356 | @include button;
357 | text-indent: 0;
358 | width: auto;
359 | font-size: 13px;
360 | line-height: 32px;
361 | text-transform: uppercase;
362 | padding: 0 12px;
363 | color: rgba(0, 0, 0, .75);
364 | font-weight: 600;
365 | text-decoration: none;
366 | }
367 | }
368 |
369 | // RESPONSIVE
370 |
371 | @media (max-aspect-ratio: 1/1) {
372 | .grid {
373 | .item {
374 | height: 30vh;
375 | }
376 | }
377 | }
378 |
379 | @media (max-height: 480px) {
380 | .grid {
381 | .item {
382 | height: 80vh;
383 | }
384 | }
385 | }
386 |
387 | @media (max-aspect-ratio: 1/1) and (max-width: 480px) {
388 | .grid {
389 | flex-direction: row;
390 |
391 | .item {
392 | height: auto;
393 | width: 100%;
394 |
395 | img {
396 | width: 100%;
397 | height: auto;
398 | }
399 |
400 | .previous,
401 | .next {
402 | width: 25vw;
403 | max-width: auto;
404 |
405 | span {
406 | display: none;
407 | }
408 | }
409 |
410 | .previous {
411 | cursor: w-resize;
412 | }
413 |
414 | .next {
415 | cursor: e-resize;
416 | }
417 | }
418 | }
419 | }
420 |
421 | // ANIMATIONS
422 |
423 | @keyframes fade-in {
424 | 0% {
425 | opacity: 0;
426 | }
427 | 100% {
428 | opacity: 1;
429 | }
430 | }
--------------------------------------------------------------------------------
/exampleSite/fetch-photos.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | function fetch() {
4 | file=$1
5 | url=$2
6 | if [ ! -e "$1" ]; then
7 | echo "Fetching $1... "
8 | curl -sL -o "$1" "$2"
9 | if [ "$?" -gt 0 ]; then
10 | echo "Failed !"
11 | fi
12 | else
13 | echo "Skipping $1, the file already exists!"
14 | fi
15 | mime_type="$(file -b --mime-type "$1")"
16 | if [ "$mime_type" != "image/jpeg" ]; then
17 | echo "Removing image with wrong mime type!"
18 | rm "$1"
19 | fi
20 | }
21 |
22 | fetch "content/animals/dog.jpeg" "https://unsplash.com/photos/Am3RoG7GEl0/download?force=true"
23 | fetch "content/animals/cat.jpeg" "https://unsplash.com/photos/G21-GaAZrrs/download?force=true"
24 | fetch "content/animals/bee.jpeg" "https://unsplash.com/photos/VeRn-bKfoVA/download?force=true"
25 | fetch "content/animals/monkey.jpeg" "https://unsplash.com/photos/4nPq-CMKfvY/download?force=true"
26 | fetch "content/animals/turtle.jpeg" "https://unsplash.com/photos/_x-PryfGq0Y/download?force=true"
27 | fetch "content/animals/horse.jpeg" "https://unsplash.com/photos/dr1Hbu8Gwt8/download?force=true"
28 | fetch "content/animals/bird2.jpeg" "https://unsplash.com/photos/ZbMJ5VLrpQ4/download?force=true"
29 | fetch "content/animals/spider.jpeg" "https://unsplash.com/photos/BkV2pxgof-U/download?force=true"
30 | fetch "content/animals/wolf.jpeg" "https://unsplash.com/photos/9rloii_qmmw/download?force=true"
31 | fetch "content/animals/dog2.jpeg" "https://unsplash.com/photos/Oq9NDnlFQzU/download?force=true"
32 | fetch "content/animals/fox.jpeg" "https://unsplash.com/photos/oCxaclJklOI/download?force=true"
33 | fetch "content/animals/dog3.jpeg" "https://unsplash.com/photos/8o5uCdOQtko/download?force=true"
34 | fetch "content/animals/camel.jpeg" "https://unsplash.com/photos/i_Z1o10BPPk/download?force=true"
35 | fetch "content/animals/bee2.jpeg" "https://unsplash.com/photos/UPJ0vTjPFXE/download?force=true"
36 | fetch "content/animals/wasp.jpeg" "https://unsplash.com/photos/R_W86FHa-Sk/download?force=true"
37 | fetch "content/animals/dog4.jpeg" "https://unsplash.com/photos/Ugg-EIfzy0c/download?force=true"
38 | fetch "content/animals/rabbit.jpeg" "https://unsplash.com/photos/8bOwZ8ag9UY/download?force=true"
39 | fetch "content/animals/fish.jpeg" "https://unsplash.com/photos/l-QdJMZX7PU/download?force=true"
40 |
41 | fetch "content/architecture/archi01.jpeg" "https://unsplash.com/photos/37Hk9D4Ig_4/download?force=true"
42 | fetch "content/architecture/archi02.jpeg" "https://unsplash.com/photos/3pk1VnBeTQQ/download?force=true"
43 | fetch "content/architecture/archi03.jpeg" "https://unsplash.com/photos/t1tAOh-CaZ4/download?force=true"
44 | fetch "content/architecture/archi04.jpeg" "https://unsplash.com/photos/w6OniVDCfn0/download?force=true"
45 | fetch "content/architecture/archi05.jpeg" "https://unsplash.com/photos/zZ97YKTyj7s/download?force=true"
46 | fetch "content/architecture/archi06.jpeg" "https://unsplash.com/photos/tNGfc-2KNrc/download?force=true"
47 | fetch "content/architecture/archi07.jpeg" "https://unsplash.com/photos/LmS1g1fqyas/download?force=true"
48 | fetch "content/architecture/archi08.jpeg" "https://unsplash.com/photos/I-LFXWk3vLI/download?force=true"
49 | fetch "content/architecture/archi09.jpeg" "https://unsplash.com/photos/4VBFrMweUw8/download?force=true"
50 | fetch "content/architecture/archi13.jpeg" "https://unsplash.com/photos/IU1QUXkD-90/download?force=true"
51 | fetch "content/architecture/archi14.jpeg" "https://unsplash.com/photos/8o_x-NjXIcQ/download?force=true"
52 | fetch "content/architecture/archi15.jpeg" "https://unsplash.com/photos/YKAUA_Rt6xI/download?force=true"
53 | fetch "content/architecture/archi16.jpeg" "https://unsplash.com/photos/zUOqjnO_ZvM/download?force=true"
54 | fetch "content/architecture/archi17.jpeg" "https://unsplash.com/photos/s07In41ntgg/download?force=true"
55 | fetch "content/architecture/archi18.jpeg" "https://unsplash.com/photos/QsDEa0qvk20/download?force=true"
56 |
57 | fetch "content/flowers/flower01.jpeg" "https://unsplash.com/photos/EfhCUc_fjrU/download?force=true"
58 | fetch "content/flowers/flower02.jpeg" "https://unsplash.com/photos/9A_peGrSbZc/download?force=true"
59 | fetch "content/flowers/flower03.jpeg" "https://unsplash.com/photos/tu_mv6p2p5U/download?force=true"
60 | fetch "content/flowers/flower04.jpeg" "https://unsplash.com/photos/koy6FlCCy5s/download?force=true"
61 | fetch "content/flowers/flower06.jpeg" "https://unsplash.com/photos/5lRxNLHfZOY/download?force=true"
62 | fetch "content/flowers/flower07.jpeg" "https://unsplash.com/photos/iMdsjoiftZo/download?force=true"
63 | fetch "content/flowers/flower08.jpeg" "https://unsplash.com/photos/OWq8w3BYMFY/download?force=true"
64 | fetch "content/flowers/flower09.jpeg" "https://unsplash.com/photos/ATgfRqpFfFI/download?force=true"
65 | fetch "content/flowers/flower10.jpeg" "https://unsplash.com/photos/YmPqWIQcl9c/download?force=true"
66 | fetch "content/flowers/flower11.jpeg" "https://unsplash.com/photos/urUdKCxsTUI/download?force=true"
67 | fetch "content/flowers/flower12.jpeg" "https://unsplash.com/photos/p7mo8-CG5Gs/download?force=true"
68 | fetch "content/flowers/flower13.jpeg" "https://unsplash.com/photos/f0heeiu-Ec0/download?force=true"
69 | fetch "content/flowers/flower14.jpeg" "https://unsplash.com/photos/IicyiaPYGGI/download?force=true"
70 | fetch "content/flowers/flower15.jpeg" "https://unsplash.com/photos/KQ6sO8m1ZDE/download?force=true"
71 | fetch "content/flowers/flower16.jpeg" "https://unsplash.com/photos/kkJuQhp9Kw0/download?force=true"
72 | fetch "content/flowers/flower17.jpeg" "https://unsplash.com/photos/aolmXcUxr7Y/download?force=true"
73 | fetch "content/flowers/flower19.jpeg" "https://unsplash.com/photos/BlMj6RYy3c0/download?force=true"
74 | fetch "content/flowers/flower20.jpeg" "https://unsplash.com/photos/whOkVvf0_hU/download?force=true"
75 |
76 | fetch "content/food/food03.jpeg" "https://unsplash.com/photos/8W1KIj8iWX4/download?force=true"
77 | fetch "content/food/food04.jpeg" "https://unsplash.com/photos/D7NA2pEn3K0/download?force=true"
78 | fetch "content/food/food05.jpeg" "https://unsplash.com/photos/IlnF2g_3tpY/download?force=true"
79 | fetch "content/food/food06.jpeg" "https://unsplash.com/photos/qNhe2QXzLuo/download?force=true"
80 | fetch "content/food/food07.jpeg" "https://unsplash.com/photos/1J1mEZbag4I/download?force=true"
81 | fetch "content/food/food08.jpeg" "https://unsplash.com/photos/phEaeqe555M/download?force=true"
82 | fetch "content/food/food09.jpeg" "https://unsplash.com/photos/G3GxkxZOOYc/download?force=true"
83 | fetch "content/food/food10.jpeg" "https://unsplash.com/photos/dmnCGaqMEzE/download?force=true"
84 | fetch "content/food/food11.jpeg" "https://unsplash.com/photos/TAj4X5-eRqE/download?force=true"
85 | fetch "content/food/food12.jpeg" "https://unsplash.com/photos/H0fOnITjgw8/download?force=true"
86 | fetch "content/food/food13.jpeg" "https://unsplash.com/photos/MqT0asuoIcU/download?force=true"
87 | fetch "content/food/food14.jpeg" "https://unsplash.com/photos/EGvhPABaBos/download?force=true"
88 | fetch "content/food/food15.jpeg" "https://unsplash.com/photos/_MYcIi9DgYQ/download?force=true"
89 | fetch "content/food/food16.jpeg" "https://unsplash.com/photos/KG8ofkGRl1k/download?force=true"
90 | fetch "content/food/food17.jpeg" "https://unsplash.com/photos/C6JhUKs9q8M/download?force=true"
91 | fetch "content/food/food18.jpeg" "https://unsplash.com/photos/JyxMyWKOlSU/download?force=true"
92 | fetch "content/food/food20.jpeg" "https://unsplash.com/photos/aZfMW0hSnQI/download?force=true"
93 |
94 | fetch "content/happy/happy01.jpeg" "https://unsplash.com/photos/TyQ-0lPp6e4/download?force=true"
95 | fetch "content/happy/happy02.jpeg" "https://unsplash.com/photos/e3OUQGT9bWU/download?force=true"
96 | fetch "content/happy/happy03.jpeg" "https://unsplash.com/photos/FtZL0r4DZYk/download?force=true"
97 | fetch "content/happy/happy04.jpeg" "https://unsplash.com/photos/hRdVSYpffas/download?force=true"
98 | fetch "content/happy/happy05.jpeg" "https://unsplash.com/photos/1AhGNGKuhR0/download?force=true"
99 |
100 |
101 | fetch "content/love/love02.jpeg" "https://unsplash.com/photos/AsahNlC0VhQ/download?force=true"
102 | fetch "content/love/love03.jpeg" "https://unsplash.com/photos/EdULZpOKsUE/download?force=true"
103 | fetch "content/love/love04.jpeg" "https://unsplash.com/photos/Y9mWkERHYCU/download?force=true"
104 |
105 | fetch "content/people/people01.jpeg" "https://unsplash.com/photos/4nulm-JUYFo/download?force=true"
106 | fetch "content/people/people02.jpeg" "https://unsplash.com/photos/NAdFJtFFlHE/download?force=true"
107 | fetch "content/people/people03.jpeg" "https://unsplash.com/photos/2RhlxwRz4yc/download?force=true"
108 | fetch "content/people/people04.jpeg" "https://unsplash.com/photos/tokYjYqaPB0/download?force=true"
109 | fetch "content/people/people05.jpeg" "https://unsplash.com/photos/by0XNgDemsc/download?force=true"
110 |
111 | fetch "content/tints/tint01.jpeg" "https://unsplash.com/photos/kKvQJ6rK6S4/download?force=true"
112 | fetch "content/tints/tint02.jpeg" "https://unsplash.com/photos/Lw7BruqPnJY/download?force=true"
113 | fetch "content/tints/tint03.jpeg" "https://unsplash.com/photos/60eMQfQuGIk/download?force=true"
114 | fetch "content/tints/tint04.jpeg" "https://unsplash.com/photos/Q4q7kJxqfGI/download?force=true"
115 | fetch "content/tints/tint05.jpeg" "https://unsplash.com/photos/PsO_PfLXET4/download?force=true"
116 |
117 | fetch "content/travel/travel01.jpeg" "https://unsplash.com/photos/Q2ET6TX1poU/download?force=true"
118 | fetch "content/travel/travel02.jpeg" "https://unsplash.com/photos/c5F1hhK5t0Q/download?force=true"
119 | fetch "content/travel/travel03.jpeg" "https://unsplash.com/photos/A7KD1kdXD-o/download?force=true"
120 | fetch "content/travel/travel04.jpeg" "https://unsplash.com/photos/PSY_KuMcTJU/download?force=true"
121 | fetch "content/travel/travel05.jpeg" "https://unsplash.com/photos/rlIb6DLWcH8/download?force=true"
122 |
--------------------------------------------------------------------------------