How To Build A Search Engine In Pure CSS
Tim Carry
@pixelastic
Algolia: Search as a Service
CSS API Client
Works offline!
No JavaScript Needed!
Flexbox!
CSS API Client
Works offline!
No JavaScript Needed!
Flexbox!
Live demo
Relevance
Speed
Highlighting
Typo-tolerance
Synonyms
Facetting
CSS? Really?
CSS is awesome
  • No vars
  • No conditions
  • No loops
  • No functions
  • No regular expressions
Selectors
  • tag
    ,
    .class
    or
    #id
  • [attribute=foo]
  • hierarchical > tree
  • siblings ~ elements
Three tricks
Trick #1: Attribute selectors
HTML
<div foo="bar">1</div>
<div foo="baz">2</div>
CSS
div[foo="bar"] {
  background: green;
}
foo="bar"
foo="baz"
Trick #2: Pseudo-elements
HTML
<div>1</div>
<div id="foo">2</div>
CSS
#foo:before {
  content: "♥ "
}
1
♥ 2
Trick #3: Tilde selector
HTML
<div class="foo">foo</div>
<div class="bar">bar</div>
<div class="baz">baz</div>
<div class="foo">foo</div>
CSS
.bar ~ .foo {
  background: green;
}
.foo
.bar
.baz
.foo
#1 + #2 + #3
HTML
<input type="text" value="Tim" />
<div id="result"></div>
CSS
input[value="Tim"] ~ #result:before {
  content: "Tim Carry";
}
Tim Carry
Search as-you-type
<input type="text" value=""/>
<div id="result"></div>
input[value="Tim"] ~ #result:before {
 content: "Tim Carry";
}
<input type="text" oninput="this.setAttribute('value', this.value)" />
It works!
input[value="tim" i] ~ #result:before {
 content: "Tim Carry";
}
input[value="emily" i] ~ #result:before {
 content: "Emily Hayman";
}
input[value="kumiko" i] ~ #result:before {
 content: "Kumiko Larroque";
}
Tim Carry
Emily Hayman
Kumiko Larroque
well...
input[value="tim" i] ~ #result:before {
 content: "Tim Carry";
}
input[value="emily" i] ~ #result:before {
 content: "Emily Hayman";
}
input[value="kumiko" i] ~ #result:before {
 content: "Kumiko Larroque";
}
Inside the engine
Searching Vs Indexing
Searching
What will I find if I type
Tim
?
Indexing
What do I need to type to find
Tim Carry
?
Splitting words
Tim Carry - Developer Advocate
N-grams
Tim
Carry
-
Developer
Advocate
T
Ti
Tim
C
Ca
Car
Carr
Carry
D
De
Dev
Deve
Devel
Develop
Develope
Developer
A
Ad
Adv
Advo
Advoc
Advoca
Advocat
Advocate
Prefix matching
input[value="t" i]     ~ #result:before,
input[value="ti" i]    ~ #result:before,
input[value="tim" i]   ~ #result:before,
input[value="c" i]     ~ #result:before,
input[value="ca" i]    ~ #result:before,
input[value="car" i]   ~ #result:before,
input[value="carr" i]  ~ #result:before,
input[value="carry" i] ~ #result:before {
 content: "Tim Carry";
}
Tim Carry
Tim Carry
Tim Carry
Tim Carry
Edge-cases
Edge-cases and features
Accented characters
"Raphaël", "Raphael"
Compound words
"Marie-Laure", "Marie Laure"
Synonyms
"Sylvain", "Utard", "Redox"
Typo-tolerance
"Schermesser", "Scermesser", "Schermeser"
, "Remy", "Rémy", "Rémy-Christophe", "Remy-Christophe", "RCS", "Scala", "Shermesser", "Scermesser", "ph'nglui mglw'nafh Schermesser R'lyeh wgah'nagl fhtagn",
Make it pretty
Layout
Several results
<input type="text" id="input"/>

<div id="result_1"></div>
<div id="result_2"></div>
<div id="result_3"></div>
//Pre-fill results
#result_1:before { content: "Julien Lemoine"; }
#result_2:before { content: "Josh Dzielak"; }
#result_3:before { content: "Jonas Badalic"; }
// Hide them all
#results div { display: none; }

// Display some on specific queries
#input[value="jo" i] ~ #result_2:before,
#input[value="jo" i] ~ #result_3:before {
  display: block;
}
Julien Lemoine
Josh Dzielak
Jonas Badalic
Styling in one div
  <div id="result_42"></div>
cloudinary.com
http://res.cloudinary.com/{app_id}/image/fetch/
w_220
,
h_220
,
r_max
,
e_grayscale
,
q_90
/
https://www.algolia.com/gianluca.png
Multi-line content
#result_42 {
  &:before {
    content: "Gianluca Bargelli Full-Stack Engineer";
  }
}
Multi-line content
#result_42 {
  &:before {
    content: "Gianluca Bargelli \A Full-Stack Engineer";
    white-space: pre;
  }
  &:first-line {
    font-weight: bold;
  }
}
Explaining results
Highlighting
Highlight in generated content?
#result_42:before {
  content: "Gianluca Bargelli \A Full-Stack Engineer";
}
Merge of two fonts
+
U+E000 – U+F8FF
#result_42:before {
  content: "\e647 \e669 \e661 \e66e luca Bargelli \A Full-Stack Engineer";
}
Subtle but useful
Empty query
Changing the order
HTML
<div id="result_1"></div>
<div id="result_2"></div>
<div id="result_3"></div>
CSS
div { flex: 1; }
#result_1 { order: 3; }
#result_2 { order: 1; }
#result_3 { order: 2; }
#result_2
#result_3
#result_1
Random in CSS?
  • No vars
  • No functions
  • No
    order: random;
SCSS? Less? Stylus?
webtask.io
webtask.io/edit/
{appid}
/
random-css
#result_1 { order: 4; }
#result_2 { order: 8; }
#result_3 { order: 15; }
#result_4 { order: 16; }
#result_5 { order: 23; }
#result_6 { order: 42; }
[…]
{appid}
.webtask.io/
random-css
Learnings
Performance tricks
#id
 is fast
:nth-child
 is slow
Reduce filesize with
#i
or
#r12
Use
#r12[id]
instead of
!important
Don't use more than
8192
selectors in one rule
Don't follow random performance tricks
Impossible only means you haven't found the solution
yet
.
How To Build A Search Engine In Pure CSS
Tim Carry
www.algolia.com/css
How To Build A Search Engine In Pure CSS
Tim Carry