Skip to content

Commit e94ea30

Browse files
authored
Add support for Lunr and Algolia search (#48)
* Add basic support for Lunr and Algolia search * Fix skip links * Fix URL paths * Add toggle for Algolia "powered by" badge * Improve placement of search toggle * Fix search icon fill color * Add search documentation * Update CHANGELOG * Enable search on demo site * Update TOC
1 parent 6d43c0f commit e94ea30

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+826
-44
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## Unreleased
99

10+
### Added
11+
- Add site-wide search toggle.
12+
- Add support for Lunr search. [#48](https://github.com/mmistakes/jekyll-theme-basically-basic/pull/48)
13+
- Add support for Algolia search. [#48](https://github.com/mmistakes/jekyll-theme-basically-basic/pull/48)
14+
1015
### Changed
1116
- New installation and upgrade instructions.
17+
- Absolutely position navigation menu instead of sticking it to the top.
18+
- Visually hide "Menu" label.
19+
- Improve alignment of menu toggle when search is enabled.
1220

1321
### Fixed
1422
- Fix `border-bottom` for Gist line numbers.

README.md

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@ with a few enhancements thrown in for good measure:
4242
3. [Text](#text)
4343
4. [Navigation](#navigation)
4444
5. [Pagination](#pagination)
45-
6. [Author](#author)
46-
7. [Reading Time](#reading-time)
47-
8. [Comments (via Disqus)](#comments-via-disqus)
48-
9. [Google Analytics](#google-analytics)
45+
6. [Search](#search)
46+
1. [Lunr (default)](#lunr-default)
47+
2. [Algolia](#algolia)
48+
7. [Author](#author)
49+
8. [Reading Time](#reading-time)
50+
9. [Comments (via Disqus)](#comments-via-disqus)
51+
10. [Google Analytics](#google-analytics)
4952
5. [Layouts](#layouts)
5053
1. [`layout: default`](#layout-default)
5154
2. [`layout: post`](#layout-post)
@@ -63,9 +66,6 @@ with a few enhancements thrown in for good measure:
6366
8. [Contributing](#contributing)
6467
1. [Pull Requests](#pull-requests)
6568
9. [Credits](#credits)
66-
1. [Creator](#creator)
67-
2. [Icons + Demo Images:](#icons--demo-images)
68-
3. [Other:](#other)
6969
10. [License](#license)
7070

7171
## Installation
@@ -402,6 +402,67 @@ add the following front matter:
402402
paginate: true
403403
```
404404

405+
### Search
406+
407+
To enable site-wide search add `search: true` to your `_config.yml`.
408+
409+
#### Lunr (default)
410+
411+
The default search uses [**Lunr**](https://lunrjs.com/) to build a search index of all your documents. This method is 100% compatible with sites hosted on GitHub Pages.
412+
413+
**Note:** Only the first 50 words of a post or page's body content is added to the Lunr search index. Setting `search_full_content` to `true` in your `_config.yml` will override this and could impact page load performance.
414+
415+
#### Algolia
416+
417+
For faster and more relevant search:
418+
419+
1. Add the [`jekyll-algolia`](https://github.com/algolia/jekyll-algolia) gem to your `Gemfile`, in the `:jekyll_plugins` section.
420+
421+
```ruby
422+
group :jekyll_plugins do
423+
gem "jekyll-feed"
424+
gem "jekyll-seo-tag"
425+
gem "jekyll-sitemap"
426+
gem "jekyll-paginate"
427+
gem "jekyll-algolia"
428+
end
429+
```
430+
431+
Once this is done, download all dependencies by running `bundle install`.
432+
433+
2. Switch search providers from `lunr` to `algolia` in your `_config.yml` file:
434+
435+
```yaml
436+
search_provider: algolia
437+
```
438+
439+
3. Add the following Algolia credentials to your `_config.yml` file. *If you don't have an Algolia account, you can open a free [Community plan](https://www.algolia.com/users/sign_up/hacker). Once signed in, you can grab your credentials from [your dashboard](https://www.algolia.com/licensing).*
440+
441+
```yaml
442+
algolia:
443+
application_id: # YOUR_APPLICATION_ID
444+
index_name: # YOUR_INDEX_NAME
445+
search_only_api_key: # YOUR_SEARCH_ONLY_API_KEY
446+
powered_by: # true (default), false
447+
```
448+
449+
4. Once your credentials are setup, you can run the indexing with the following command:
450+
451+
```
452+
ALGOLIA_API_KEY=your_admin_api_key bundle exec jekyll algolia
453+
```
454+
455+
For Windows users you will have to use `set` to assigned the `ALGOLIA_API_KEY` environment variable.
456+
457+
```
458+
set ALGOLIA_API_KEY=your_admin_api_key
459+
bundle exec jekyll algolia
460+
```
461+
462+
Note that `ALGOLIA_API_KEY` should be set to your admin API key.
463+
464+
To use the Algolia search with GitHub Pages hosted sites follow [this deployment guide](https://community.algolia.com/jekyll-algolia/github-pages.html). Or this guide for [deploying on Netlify](https://community.algolia.com/jekyll-algolia/netlify.html).
465+
405466
### Author
406467

407468
Author information is used as meta data for post "by lines" and propagates the

_config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ author:
2626
twitter_username:
2727
github_username:
2828
logo: # path of site logo, e.g. "/assets/images/logo.png"
29+
search: # true, false (default)
30+
search_full_content: false # true, false (default)
31+
search_provider: # lunr (default), algolia
32+
algolia:
33+
application_id: # YOUR_APPLICATION_ID
34+
index_name: # YOUR_INDEX_NAME
35+
search_only_api_key: # YOUR_SEARCH_ONLY_API_KEY
36+
powered_by: # true (default), false
2937

3038
# Build settings
3139
markdown: kramdown

_data/theme.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ t:
88
skip_content: "Skip to content"
99
skip_footer: "Skip to footer"
1010
menu: "Menu"
11+
search: "Search"
12+
results_found: "Result(s) found"
13+
search_placeholder_text: "Enter your search term..."
1114
home: "Home"
1215
newer: "Newer"
1316
older: "Older"

_includes/scripts.html

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,15 @@
22
{% include google-analytics.html %}
33
{% endif %}
44

5-
<script async src="{{ '/assets/javascripts/main.js' | relative_url }}"></script>
5+
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
6+
<script async src="{{ '/assets/javascripts/main.js' | relative_url }}"></script>
7+
8+
{% if site.search %}
9+
{%- assign search_provider = site.search_provider | default: "lunr" -%}
10+
{%- case search_provider -%}
11+
{%- when "lunr" -%}
12+
{% include search/lunr-search-scripts.html %}
13+
{%- when "algolia" -%}
14+
{% include search/algolia-search-scripts.html %}
15+
{%- endcase -%}
16+
{% endif %}

_includes/search-form.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<div class="inner">
2+
{% if site.search %}
3+
{%- assign search_provider = site.search_provider | default: "lunr" -%}
4+
{%- case search_provider -%}
5+
{%- when "lunr" -%}
6+
<input type="text" id="search" class="search-input" tabindex="-1" placeholder="{{ site.data.theme.t.menu.search_placeholder_text | default: 'Enter your search term...' }}" />
7+
<div id="results" class="results"></div>
8+
{%- when "algolia" -%}
9+
<div tabindex="-1" class="search-searchbar"></div>
10+
<div class="search-hits"></div>
11+
{%- endcase -%}
12+
{% endif %}
13+
</div>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!-- Including InstantSearch.js library and styling -->
2+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/instantsearch.min.js"></script>
3+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/instantsearch.min.css">
4+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/instantsearch-theme-algolia.min.css">
5+
6+
<script>
7+
// Instanciating InstantSearch.js with Algolia credentials
8+
const search = instantsearch({
9+
appId: '{{ site.algolia.application_id }}',
10+
apiKey: '{{ site.algolia.search_only_api_key }}',
11+
indexName: '{{ site.algolia.index_name }}',
12+
searchParameters: {
13+
restrictSearchableAttributes: [
14+
'title',
15+
'content'
16+
]
17+
}
18+
});
19+
20+
const hitTemplate = function(hit) {
21+
const url = hit.url;
22+
const title = hit._highlightResult.title.value;
23+
const content = hit._highlightResult.html.value;
24+
25+
return `
26+
<article class="entry">
27+
<h3 class="entry-title"><a href="{{ site.baseurl }}${url}">${title}</a></h3>
28+
<div class="entry-excerpt">${content}</div>
29+
</article>
30+
`;
31+
}
32+
33+
// Adding searchbar and results widgets
34+
search.addWidget(
35+
instantsearch.widgets.searchBox({
36+
container: '.search-searchbar',
37+
{% unless site.algolia.powered_by == false %}poweredBy: true,{% endunless %}
38+
placeholder: '{{ site.data.theme.t.menu.search_placeholder_text | default: "Enter your search term..." }}'
39+
})
40+
);
41+
search.addWidget(
42+
instantsearch.widgets.hits({
43+
container: '.search-hits',
44+
templates: {
45+
item: hitTemplate
46+
}
47+
})
48+
);
49+
50+
// Starting the search
51+
search.start();
52+
</script>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
{%- assign lang = site.lang | slice: 0, 2 | default: "en" -%}
2+
{%- case lang -%}
3+
{%- when "da" -%}
4+
{%- assign lang = "da" -%}
5+
{%- when "de" -%}
6+
{%- assign lang = "de" -%}
7+
{%- when "du" -%}
8+
{%- assign lang = "du" -%}
9+
{-% when "es" -%}
10+
{%- assign lang = "es" -%}
11+
{%- when "fi" -%}
12+
{%- assign lang = "fi" -%}
13+
{%- when "fr" -%}
14+
{%- assign lang = "fr" -%}
15+
{%- when "hu" -%}
16+
{%- assign lang = "hu" -%}
17+
{%- when "it" -%}
18+
{%- assign lang = "it" -%}
19+
{%- when "ja" -%}
20+
{%- assign lang = "ja" -%}
21+
{%- when "jp" -%}
22+
{%- assign lang = "jp" -%}
23+
{%- when "no" -%}
24+
{%- assign lang = "no" -%}
25+
{%- when "pt" -%}
26+
{%- assign lang = "pt" -%}
27+
{%- when "ro" -%}
28+
{%- assign lang = "ro" -%}
29+
{%- when "ru" -%}
30+
{%- assign lang = "ru" -%}
31+
{%- when "sv" -%}
32+
{%- assign lang = "sv" -%}
33+
{%- when "tr" -%}
34+
{%- assign lang = "tr" -%}
35+
{%- else -%}
36+
{%- assign lang = "en" -%}
37+
{%- endcase -%}
38+
<script src="{{ '/assets/javascripts/lunr/lunr.min.js' | absolute_url }}"></script>
39+
<script src="{{ '/assets/javascripts/search-data.json' | absolute_url }}"></script>
40+
{%- unless lang == "en" -%}
41+
<script src="{{ '/assets/javascripts/lunr/lunr.stemmer.support.min.js' | absolute_url }}"></script>
42+
<script src="{{ '/assets/javascripts/lunr/lunr.' | append: lang | append: '.min.js' | absolute_url }}"></script>
43+
{%- endunless %}
44+
<script>
45+
var idx = lunr(function () {
46+
{% unless lang == "en" %}
47+
// use the language
48+
this.use(lunr.{{ lang }});
49+
{% endunless %}
50+
// the, the normal lunr index initialization
51+
this.field('title')
52+
this.field('excerpt')
53+
this.field('categories')
54+
this.field('tags')
55+
this.ref('id')
56+
57+
this.pipeline.remove(lunr.trimmer)
58+
59+
// add documents to index
60+
for (var item in store) {
61+
this.add({
62+
title: store[item].title,
63+
excerpt: store[item].excerpt,
64+
categories: store[item].categories,
65+
tags: store[item].tags,
66+
id: item
67+
})
68+
}
69+
});
70+
71+
console.log(jQuery.type(idx));
72+
73+
$(document).ready(function () {
74+
$('input#search').on('keyup', function () {
75+
var resultdiv = $('#results');
76+
var query = $(this).val().toLowerCase();
77+
var result =
78+
idx.query(function (q) {
79+
query.split(lunr.tokenizer.separator).forEach(function (term) {
80+
q.term(term, { boost: 100 })
81+
if (query.lastIndexOf(" ") != query.length - 1) {
82+
q.term(term, { usePipeline: false, wildcard: lunr.Query.wildcard.TRAILING, boost: 10 })
83+
}
84+
if (term != "") {
85+
q.term(term, { usePipeline: false, editDistance: 1, boost: 1 })
86+
}
87+
})
88+
});
89+
resultdiv.empty();
90+
resultdiv.prepend('<p class="results-found">' + result.length + ' {{ site.data.theme.t.menu.results_found | default: "Result(s) found" }}</p>');
91+
for (var item in result) {
92+
var ref = result[item].ref;
93+
var searchitem =
94+
'<article class="entry">' +
95+
'<h3 class="entry-title">' +
96+
'<a href="' + store[ref].url + '">' + store[ref].title + '</a>' +
97+
'</h3>' +
98+
'<div class="entry-excerpt">' +
99+
'<p>' + store[ref].excerpt.split(" ").splice(0, 20).join(" ") + '...</p>' +
100+
'</div>' +
101+
'</article>';
102+
resultdiv.append(searchitem);
103+
}
104+
});
105+
});
106+
</script>

_layouts/default.html

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,18 @@
1313
{% include skip-links.html %}
1414

1515
<div class="sidebar-toggle-wrapper">
16+
{% if site.search %}
17+
<button class="search-toggle" type="button">
18+
<svg class="icon" width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.99 16">
19+
<title>{{ site.data.theme.t.search | default: 'Search' }}</title>
20+
<path d="M15.5,13.12L13.19,10.8a1.69,1.69,0,0,0-1.28-.55l-0.06-.06A6.5,6.5,0,0,0,5.77,0,6.5,6.5,0,0,0,2.46,11.59a6.47,6.47,0,0,0,7.74.26l0.05,0.05a1.65,1.65,0,0,0,.5,1.24l2.38,2.38A1.68,1.68,0,0,0,15.5,13.12ZM6.4,2A4.41,4.41,0,1,1,2,6.4,4.43,4.43,0,0,1,6.4,2Z" transform="translate(-.01)"></path>
21+
</svg>
22+
</button>
23+
{% endif %}
24+
1625
<button class="toggle navicon-button larr" type="button">
1726
<span class="toggle-inner">
18-
<span class="sidebar-toggle-label">{{ site.data.theme.t.menu | default: 'Menu' }}</span>
27+
<span class="sidebar-toggle-label visually-hidden">{{ site.data.theme.t.menu | default: 'Menu' }}</span>
1928
<span class="navicon"></span>
2029
</span>
2130
</button>
@@ -31,7 +40,13 @@
3140
<div class="canvas">
3241
<div class="wrapper">
3342
{% include masthead.html %}
34-
{{ content }}
43+
<div class="initial-content">
44+
{{ content }}
45+
</div>
46+
47+
<div class="search-content">
48+
{% include search-form.html %}
49+
</div>
3550
</div>
3651
</div>
3752

_sass/basically-basic.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
@import "basically-basic/global";
2323
@import "basically-basic/sidebar";
2424
@import "basically-basic/navigation";
25+
@import "basically-basic/search";
2526
@import "basically-basic/footer";
2627
@import "basically-basic/entries";
2728
@import "basically-basic/buttons";

0 commit comments

Comments
 (0)