├── LICENSE ├── README.md ├── index.html ├── qss.js └── tests ├── element-queries.html └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tommy Hodgins 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QSS 2 | 3 | **A Simple Query Syntax for CSS Element Queries** 4 | 5 | ![](https://i.imgur.com/rntpa7l.png) 6 | 7 | The goal of QSS is to define a simple syntax for specifying element queries by adding a new ending part between a CSS selector list and the block of rules that help define the breakpoints when those rules are to apply. 8 | 9 | Normally in CSS you have something like this: 10 | 11 | ```css 12 | selectorList { block } 13 | ``` 14 | 15 | We are going to add a new part for our query between the selector list and the block where we will store instructions for when the rule should apply. 16 | 17 | ```css 18 | selectorList { block } 19 | ``` 20 | 21 | Because this exists as a new part between the selector list and the block of rules, if you have a list of selectors like `h1, h2, h3, h4, h5, h6 {}` you only need to add the query once after the selector list is complete, like `h1, h2, h3, h4, h5, h6 {}` rather than `h1 , h2 , h3 , …`. 22 | 23 | This document describes two different formats for expressing element queries for individual CSS rules, one using an `if`-based structure, and another that uses the `@` symbol to declare when it should apply. 24 | 25 | ## Phrase Formats 26 | 27 | ### 1) if <condition> <comparator> <breakpoint> 28 | 29 | - operator: `if` 30 | - condition: `width` | `height` | `characters` | `children` | `xscroll` | `yscroll` 31 | - comparator: `<` | `below` | `under` | `<=` | `max` | `==` | `equals` | `>=` | `min` | `>` | `above` | `over` 32 | - breakpoint: <number> 33 | 34 | #### Examples 35 | 36 | ```css 37 | div if width above 500 {} 38 | ``` 39 | 40 | ```css 41 | input if characters under 1 {} 42 | ``` 43 | 44 | ### 2) @ <comparator> <breakpoint> <condition> 45 | 46 | - operator: `@` 47 | - comparator: `<` | `below` | `under` | `<=` | `max` | `==` | `equals` | `>=` | `min` | `>` | `above` | `over` 48 | - breakpoint: <number> 49 | - condition: `width` | `height` | `characters` | `children` | `xscroll` | `yscroll` 50 | 51 | #### Examples 52 | 53 | ```css 54 | div @ above 500 width {} 55 | ``` 56 | 57 | ```css 58 | input @ under 1 characters {} 59 | ``` 60 | 61 | In both phrase formats the whitespace between tokens is optional, this means that if you prefer to think about these as `@above` or `@min` you can express them that way. The following should all equivalent: 62 | 63 | ```css 64 | div if width >= 500 {} 65 | div if width >=500 {} 66 | div if width min 500 {} 67 | div @min 500 width {} 68 | div@min500width{} 69 | div @ >=500 width {} 70 | ``` 71 | 72 | ## How it works 73 | 74 | The queries parsed by QSS would be split into the following pieces: 75 | 76 | - selector list 77 | - rule block (or stylesheet?) 78 | - comparator 79 | - condition 80 | - breakpoint 81 | 82 | And these could also be used to construct Element Queries for other syntaxes like: 83 | 84 | - [EQCSS](https://github.com/eqcss/eqcss) 85 | - [Selectory](https://github.com/tomhodgins/cssplus#selectory-a-selector-resolver) 86 | - and using functions like the [container query mixin](https://gist.github.com/tomhodgins/fc42b334beaafc75a271b1ef7c8e33ee) 87 | 88 | Essentially QSS acts as a syntax to abstract away writing these: [Useful Tests for JS-powered Styling](https://codepen.io/tomhodgins/post/useful-tests-for-js-powered-styling) 89 | 90 | ## Plugin Usage 91 | 92 | This repository contains a working proof of concept of a plugin to parse and read QSS syntax. In order to use this plugin you just need to include QSS on the page where you want it to display: 93 | 94 | ```html 95 | 96 | ``` 97 | 98 | Then you're able to add queries written in QSS syntax to your site using one of the following methods: a ` 102 | ``` 103 | 104 | ```html 105 | 106 | ``` 107 | 108 | ```html 109 | 110 | ``` 111 | 112 | ```html 113 | 114 | ``` 115 | 116 | ## Links 117 | 118 | - Website: [tomhodgins.github.io/qss/](http://tomhodgins.github.io/qss/) 119 | - Element Query Demo: [tests/element-queries.html](http://tomhodgins.github.io/qss/tests/element-queries.html) 120 | - Test: [tests/](http://tomhodgins.github.io/qss/tests/) 121 | - [QSS Playground](https://codepen.io/tomhodgins/pen/zPzpVR) 122 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | QSS - A Simple Query Syntax 5 | 6 | 80 | 81 |

QSS

82 | 83 |

A Simple Query Syntax for CSS Element Queries

84 | 85 | 86 | 87 |

The goal of QSS is to define a simple syntax for specifying element queries by adding a new ending part between a CSS selector list and the block of rules that help define the breakpoints when those rules are to apply.

88 | 89 |

Normally in CSS you have something like this:

90 | 91 |
selectorList { block }
92 | 93 |

We are going to add a new part for our query between the selector list and the block where we will store instructions for when the rule should apply.

94 | 95 |
selectorList <query> { block }
96 | 97 |

Because this exists as a new part between the selector list and the block of rules, if you have a list of selectors like h1, h2, h3, h4, h5, h6 {} you only need to add the query once after the selector list is complete, like h1, h2, h3, h4, h5, h6 <query> {} rather than h1 <query>, h2 <query>, h3 <query>, ….

98 | 99 |

This document describes two different formats for expressing element queries for individual CSS rules, one using an if-based structure, and another that uses the @ symbol to declare when it should apply.

100 | 101 |

Phrase Formats

102 | 103 |

1) if <condition> <comparator> <breakpoint>

104 | 105 |
    106 |
  • operator: if 107 |
  • condition: width | height | characters | children | xscroll | yscroll 108 |
  • comparator: < | below | under | <= | max | == | equals | >= | min | > | above | over 109 |
  • breakpoint: <number> 110 |
111 | 112 |

Examples

113 | 114 |
div if width above 500 {}
115 | 116 |
input if characters under 1 {}
117 | 118 |

2) @ <comparator> <breakpoint> <condition>

119 | 120 |
    121 |
  • operator: @ 122 |
  • comparator: < | below | under | <= | max | == | equals | >= | min | > | above | over 123 |
  • breakpoint: <number> 124 |
  • condition: width | height | characters | children | xscroll | yscroll 125 |
126 | 127 |

Examples

128 | 129 |
div @ above 500 width {}
130 | 131 |
input @ under 1 characters {}
132 | 133 |

In both phrase formats the whitespace between tokens is optional, this means that if you prefer to think about these as @above or @min you can express them that way. The following should all equivalent:

134 | 135 |
div if width >= 500 {}
136 | div if width >=500 {}
137 | div if width min 500 {}
138 | div @min 500 width {}
139 | div@min500width{}
140 | div @ >=500 width {}
141 | 142 | 143 | 144 |

How it works

145 | 146 |

The queries parsed by QSS would be split into the following pieces:

147 | 148 |
    149 |
  • selector list 150 |
  • rule block (or stylesheet?) 151 |
  • comparator 152 |
  • condition 153 |
  • breakpoint 154 |
155 | 156 |

And these could also be used to construct Element Queries for other syntaxes like:

157 | 158 | 163 | 164 |

Essentially QSS acts as a syntax to abstract away writing these: Useful Tests for JS-powered Styling

165 | 166 |

Plugin Usage

167 | 168 |

This repository contains a working proof of concept of a plugin to parse and read QSS syntax. In order to use this plugin you just need to include QSS on the page where you want it to display:

169 | 170 |
<script src=qss.js></script>
171 | 172 |

Then you're able to add queries written in QSS syntax to your site using one of the following methods: a <style> tag, a <link> tag with type=text/qss set, or a <script> tag with a type of text/qss either inline or linked externally using a src="" attribute:

173 | 174 |
<style type="text/qss"></style>
175 | 176 |
<link type="text/qss" href=stylesheet.qss rel=stylesheet>
177 | 178 |
<script type="text/qss"></script>
179 | 180 |
<script type="text/qss" src=stylesheet.qss></script>
181 | 182 |

Links

183 | 184 | 190 | 191 | -------------------------------------------------------------------------------- /qss.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | # QSS 4 | version 1.0.0 5 | 6 | A Simple Query Syntax for CSS Element Queries 7 | 8 | The goal of QSS is to define a simple syntax for specifying element queries by adding a new ending part between a CSS selector list and the block of rules that help define the breakpoints when those rules are to apply. 9 | 10 | selectorList { block } 11 | 12 | Supported syntax includes: 13 | 14 | - Operators: `if`, `@` 15 | 16 | - Comparators: `<` | `below` | `under` | `<=` | `max` | `==` | `equals` | `>=` | `min` | `>` | `above` | `over` 17 | 18 | - Conditions: condition: `width` | `height` | `characters` | `children` | `xscroll` | `yscroll` 19 | 20 | - Breakpoint: 21 | 22 | There are two different formats for expressing element queries for individual CSS rules, one using an `if`-based structure, and another that uses the `@` symbol to declare when it should apply. 23 | 24 | ## If-formatted Query 25 | 26 | `if` { } 27 | 28 | ### Examples 29 | 30 | div if width above 500 {} 31 | 32 | input if characters under 1 {} 33 | 34 | ## At-formatted Query 35 | 36 | `@` { } 37 | 38 | ### Examples 39 | 40 | div @ above 500 width {} 41 | 42 | input @ under 1 characters {} 43 | 44 | ## Info 45 | 46 | - license: MIT 47 | - author: Tommy Hogins 48 | - website: https://github.com/tomhodgins/qss 49 | 50 | */ 51 | 52 | var qss = {} 53 | 54 | qss.stylesheets = [] 55 | 56 | qss.load = function() { 57 | 58 | qss.count = 0 59 | 60 | var script = document.getElementsByTagName('script') 61 | 62 | for (var i=0; i^%@#&|` 185 | } 186 | 187 | // Consume stream 188 | stream.forEach(data => { 189 | 190 | for (type in syntax) { 191 | 192 | if (syntax[type].indexOf(data) !== -1) { 193 | 194 | tokens.push({ 195 | data: data, 196 | type: type 197 | }) 198 | 199 | } 200 | 201 | } 202 | 203 | }) 204 | 205 | return tokens 206 | 207 | } 208 | 209 | qss.parser = function(tokens) { 210 | 211 | var ast = [] 212 | 213 | var blockOpen = false 214 | var selector = [] 215 | var block = [] 216 | 217 | // lump adjacent types together 218 | tokens.forEach(token => { 219 | 220 | if (blockOpen) { 221 | 222 | if (selector.length > 0) { 223 | 224 | ast.push({ 225 | type: 'selector', 226 | data: selector 227 | }) 228 | selector = [] 229 | 230 | } 231 | 232 | if (token.type === 'block') { 233 | 234 | ast.push({ 235 | type: 'block', 236 | data: block 237 | }) 238 | 239 | block = [] 240 | 241 | } else { 242 | 243 | block.push(token) 244 | 245 | } 246 | 247 | } else { 248 | 249 | if (token.type !== 'block') { 250 | 251 | selector.push(token) 252 | 253 | } 254 | 255 | } 256 | 257 | if (token.type === 'block') { 258 | 259 | blockOpen = blockOpen ? false : true 260 | 261 | } 262 | 263 | }) 264 | 265 | ast = qss.consolidate(ast) 266 | 267 | return ast 268 | 269 | } 270 | 271 | qss.consolidate = function(ast) { 272 | 273 | var grouped = [] 274 | 275 | ast.forEach(token => { 276 | 277 | var current = [] 278 | 279 | if (token.type === 'selector') { 280 | 281 | for (var i=0; i=', 'min', '>', 'above', 'over'] 323 | var queries = [] 324 | 325 | grouped.forEach(item => { 326 | 327 | if (item.type === 'selector') { 328 | 329 | selector = '' 330 | 331 | // Strip Whitespace 332 | var data = qss.stripWhitespace(item.data) 333 | var end = data.length 334 | 335 | // IF 336 | if ( 337 | 338 | // If last item is a 339 | data[end-1].type === 'number' 340 | 341 | // And second-last item is a 342 | && ( 343 | 344 | // Operator 345 | (data[end-2].type === 'operator' && comparators.indexOf(data[end-2].data) !== -1) 346 | 347 | // Word 348 | || (data[end-2].type === 'word' && comparators.indexOf(data[end-2].data) !== -1) 349 | 350 | ) 351 | 352 | // And third-last item is a 353 | && (data[end-3].type === 'word' && conditions.indexOf(data[end-3].data) !== -1) 354 | 355 | // And fourth-last item is 'if' 356 | && (data[end-4].type === 'word' && data[end-4].data === 'if') 357 | 358 | ) { 359 | 360 | // If query found! 361 | for (var i=0; i 376 | if ( 377 | 378 | // If last item is a 379 | (data[end-1].type === 'word' && conditions.indexOf(data[end-1].data) !== -1) 380 | 381 | // And second-last item is a 382 | && data[end-2].type === 'number' 383 | 384 | // And third-last item is a 385 | && ( 386 | 387 | // Operator 388 | (data[end-3].type === 'operator' && comparators.indexOf(data[end-3].data) !== -1) 389 | 390 | // Word 391 | || (data[end-3].type === 'word' && comparators.indexOf(data[end-3].data) !== -1) 392 | 393 | ) 394 | 395 | // And fourth-last item is '@' 396 | && (data[end-4].type === 'operator' && data[end-4].data === '@') 397 | 398 | ) { 399 | 400 | // If query found! 401 | for (var i=0; i ${condition} ${comparator} ${breakpoint}, '${rule}')\n`) 479 | 480 | } 481 | 482 | }) 483 | 484 | return queries 485 | 486 | } 487 | 488 | qss.stripWhitespace = function(ast) { 489 | 490 | for (var i=0; i { 515 | 516 | style += new Function('return ' + command)() 517 | 518 | }) 519 | 520 | return style 521 | 522 | } 523 | 524 | qss.elementQuery = function(selector, test, rule) { 525 | 526 | var tag = document.querySelectorAll(selector) 527 | var style = '' 528 | var count = 0 529 | 530 | for (var i=0; i 2 | 3 | 4 | 5 | 6 | Element Queries using QCSS 7 | 8 | 128 | 129 |

Element Queries using QSS

130 | 131 |

Width Queries

132 | 133 |

min-width

134 |
class="minwidth"
135 | 142 | 143 |

max-width

144 |
class="maxwidth"
145 | 152 | 153 |

Height Queries

154 | 155 |

min-height

156 |
class="minheight"
157 | 164 | 165 |

max-height

166 |
class="maxheight"
167 | 174 | 175 |

Quantity Queries

176 | 177 |

min-characters on block elements

178 |

(Use keyboard)

179 |
class="mincharacters"
180 | 187 | 188 |

min-characters on form inputs

189 |

(Use keyboard)

190 | 191 | 192 | 205 | 206 |

max-characters on block elements

207 |

(Use keyboard)

208 |
class="maxcharacters"
209 | 216 | 217 |

max-characters on form inputs

218 |

(Use keyboard)

219 | 220 | 221 | 234 | 235 |

min-children

236 | 237 |
238 | 239 |
240 | 247 | 248 |

max-children

249 | 250 |
251 | 252 |
253 | 260 | 261 |

Scroll queries

262 | 263 |

min-scroll-y

264 |
265 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

266 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

267 |
268 | 275 | 276 |

max-scroll-y

277 |
278 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

279 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

280 |
281 | 288 | 289 |

min-scroll-x

290 |
291 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

292 |
293 | 300 | 301 |

max-scroll-x

302 |
303 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

304 |
305 | 312 | 313 | 314 | 347 | 430 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | QSS Demo 5 | 6 | 34 | 35 |

QSS Demo

36 | 37 |

At-formatted examples

38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 63 | 64 |

If-formatted examples

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 90 | 91 | --------------------------------------------------------------------------------