Contributing.md
Python script, ASCII text executable
Contributing Requirements
Style Guide
This document provides conventions for the coding style used in the Roundabout project. Rules can be broken -- just try to make the code as readable as you can.
When something is not covered by this document, refer to PEP 8. However, please read this even if you know PEP 8, as it diverges in some places.
Code Layout
Indents and Line Continuation
Keep lines up to 80 characters long. Use 4 spaces for indentation, per level. Do not use tabs, and do not put trailing whitespace.
For parentheses, three styles are accepted:
# No indent. thing = function(arg1, arg2) # Hanging indent. Align to the contents, not to the bracket. thing = function(arg1, arg2 arg3, arg4, arg5) # Full indent. Here only one argument or variable or whatever is allowed per line. # The closing bracket must be at the previous indent level. # Any level is allowed, but it must be a multiple of 4. thing = function( arg1, arg2, arg3, arg4 )
Collections should read like lists, not tables.
fruits = ["apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine",] fruits = [ "apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine", ] # Don't! fruits = [ "apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine", ]
Additionally, for a collection meant to be expanded use a trailing comma.
For long expressions, begin the line with the operator and align the operand with the indentation level. This is not permitted by PEP 8, but it follows mathematics and leads to nicer layouts.
population = (population + births + immigrants - deaths - emigrants)
When the hanging indent could be mistaken for a block, add an empty line.
Backslashes are discouraged, but allowed if it is the only way to write your expression. For example:
really_long_variable_name_hopefully_yours_wont_be_as_long = \ "Really long value."
Blank Line Rules
Two between top-level class or function definitions.
One between local functions or methods.
One is allowed to separate related groups and logical sections.
One at the end of the file.
One below imports.
Imports
Imports should be on separate lines. from
-imports must import all objects on the same line though.
import flask import os from models import User, Post
Imports should be ordered like this:
__future__
statementsone blank line
magic names (
__all__
,__version__
)one blank line
library imports
library
from
-importsone blank line
application imports
application
from
-imports
Wildcard imports are discouraged and prohibited for libraries. However, they are fine if the module
defines __all__
.
Interior Whitespace
Never insert more than one space around operators and other symbols. Never add spaces just to align lines.
When to use
around the lowest priority operators in an expression, comparisons, assignments, and logical operators, as well as the function annotation arrow
# Do thing += 10 + 2*thing # Don't thing+=10+2 * thing # Acceptable; use your best judgement thing += 10 + 2 * thing
after the colon when defining a single-line clause, or an annotation
after commas or semicolons
after the comment sign
#
When to avoid
when passing keyword arguments or defining argument defaults, unless they are annotated
inside any brackets
# Do function(arg1, arg2) # Don't! function( arg1, arg2 )
after a trailing comma
# Do (0,) # Don't! (0, )
before commas, semicolons or colons
# Do x, y = y, x # Don't! x , y = y , x
around the slice operator
:
before an argument list
# Do function(arg1, arg2) # Don't! function (arg1, arg2)
before the indexing operator
# Do dictionary["key"] = "Hello World!" # Don't! dictionary ["key"] = "Hello World!"
Other
Never use the semicolon for multiple statements; while allowed, it is discouraged as it hurts readability. Similarly discouraged are one-line clauses.
Strings
Only use double quotes like "
for strings: single quotes are harder to spot, and they conflict with the common apostrophe.
For docstrings you should follow PEP 257. Additionally, docstrings should use Markdown to allow for a future tool to convert them to docs. HOWEVER arguments should be described using the ReST syntax, as it is more readable in the code. Other than this, they should be kept plaintext to prevent markup language battles (Python prefers RST, but most use Markdown).
Naming
Identifiers must only use ASCII characters. Abbreviations should be kept to a minimum and it should be
made sure that they are widely used and accepted already.
(id
for identifier
or repo
for repository
is acceptable, but avg
for average
is not). Generally, names should be easy to pronounce.
Class names must use UpperCamelCase
with the first letter of each word capitalised and no underscores.
Other names (function, variable) should use snake_case
with all words lowercase and separated by underscores.
For instance and class methods, only name the first argument self
or cls
, respectively.
To avoid conflicts, append an underscore or use a synonym but do not corrupt the spelling (class_
is better than clss
or klass
or classs
or whatever).
Constants are an exception. They should use UPPER_CASE
with underscores.
Non-public names should be prefixed with an underscore. In case it's really important to not use them, use a double underscore to invoke name mangling.
Comments
Block comments should be complete sentences; line comments don't need to. Write comments in
English and always use a space after the comment sign, as stated above, unless it's an UNIX
interpreter descriptor (#!
) where you should not. Inside block comments, separate paragraphs
with an empty comment, like in Markdown. For a solo sentence, full stops are optional.
Avoid stating the obvious or contradicting the code.
Inline comments must be separated with more than one space from the statement, and they may be aligned.
In comments, never alter the case of identifiers, as it may lead to confusion.
Leaving TODO comments as personal notes is allowed, but they should be removed before merging or a release.
Programming guidelines
OOP Guidelines
Do not use getters and setters for class attributes. If you do need to change some other things, use properties.
Prefer overloading the operators; make using your objects as natural and Pythonic as possible.
Exceptions
Make exceptions specific enough, so catching them can be explicit.
Do not use the bare except
clause. Make try
clauses as short as possible to avoid silencing
unrelated bugs.
Other
Comparisons to singletons (True
, False
, None
etc.) should be done with the identity operators
is
and is not
, not with the comparison operators. Use is not
, not not ... is
.
Unless it would be ambiguous, use the implicit truth test to check that numbers are different to 0, that containers have contents and similar tests.
Do not assign lambdas to identifiers. Make a real function instead.
Use with
context managers to ensure resources are properly cleaned up.
Prefer making functions that take arguments and return a value instead of making them directly take global variables or process the information such as writing.
Use the methods startswith()
and endswith()
instead of string slicing to check for prefixes
or suffixes.
To compare types, use isinstance()
instead of the is
operator with type()
(this is one of
my problems with Python, but it's the standard).
Use a proper condition for while
instead of a while True
that break
s.
Use for
loops instead of while
loops when possible, and use Pythonic iteration instead of
C-style iteration.
To call shells, use the subprocess
module instead of os.system()
and use the functions
that allow giving a list of arguments instead of a string, it leads to better security.
Similarly, avoid writing SQL manually, use Alchemy. If you must write SQL manually, be extra careful.
Jinja style guide
Jinja should be written like Python, with the following additions:
Tags should be written as {% <content> %}
and expressions as {{ <content> }}
. That is,
put spaces around the content.
Always indent tag contents, just like you would indent HTML tags! If a tag contains other tags
and it wouldn't disrupt whitespace, you should indent the contents. An exception can be made
for top-level {% block %}
tags, because indenting them would add an extra level of indentation
to all HTML.
The filter operator |
should have spaces around it, unless it's in a more complex expression
when it shouldn't.
Translations should always be done with the {% trans %}
tag provided by Babel, not with
gettext()
, _()
or others. No exceptions.
The quoting rules are as in Python, unless it's in an HTML attribute, in which case you should use single quotes, as HTML takes precedence.
If you're trying to use some function that transforms or checks values, create additional filters or tests; they look cleaner.
HTML style guide
HTML tags should be written in lowercase, with attributes in lowercase as well. Attribute values should be quoted with double quotes. Attribute minimisation is suggested for boolean attributes.
Always indent tag contents with 4 spaces, except in plaintext tags like pre
or textarea
, where
you should not indent as it affects rendering.
The tag should be multiple lines if the content is complex. Otherwise, mirror the page layout
so use:
<button>Label</button> <!-- Good, because the content is simple AND the button is inline --> <p> <!-- Good, because even though the content is simple, the tag is a block --> Content </p> <button> <!-- Good, because the content is complex --> <iconify-icon icon="mdi:plus"></iconify-icon> Add </button>
IDs or classes should be written in kebab-case
. Names should be written in snake_case
to
provide better compatibility with Python.
Also, when making custom tags, always use a hyphen in the tag name, to make sure they won't be standardised in the future.
Inline CSS and JS should end with a semicolon, even if it's the only instruction.
CSS style guide
CSS selectors, properties and values should be written in lowercase. Custom properties should be
written in kebab-case
.
Always indent the contents of a ruleset with 4 spaces.
Unlike some other style guides, we do not require each selector after a comma to be on a new line. However, if they're too long, very complex or similar and they benefit from alignment, you should do so.
IDs are preferred over classes when the element only appears once on the page.
JavaScript style guide
JS should use double quotes for strings, and lowerCamelCase for names, and indenting should be 4 spaces. Otherwise I can't comment, because JS is ugly by nature. Try to make it as readable as possible and consistent with the other code. Also, use semicolons.
var
is not deprecated and may be the best choice in some cases. Use let
for temporary variables,
but not const
, as it provides a false sense of security by forbidding reassignment. However, if
you const
a mutable object, you can still change its contents.
Web programming guidelines
As a modern web developer, you will be disappointed. In the roundabout project, we write HTML and generate it on the server (which is not JavaScript, but Python/Flask). You may have to relearn web development if you want to contribute.
Also, we use semantic HTML, which means we use tags for their intended purpose. If you're using
<div>
, <span>
, <input>
and <img>
for everything, you're doing it wrong.
We don't like bloat either, so we don't use <button class="btn btn-primary color-blue btn-large">
when <button>
is enough. If you insist on something like Tailwind, you're in the wrong place.
It does not mean we do web development like in 2004. We use the modern features browsers gift us:
The powerful CSS layout engines, flexbox and gridbox
HTML5 semantic tags
JavaScript (plain!) for interactivity
SVG
Web fonts
and, most importantly, we use Flask, which didn't even exist in 2004.
In short:
The roundabout is not a SPA.
JS should be used to enhance the experience, not to create it.
We don't use client-side frameworks.
The server is a safe environment, so we can access the database and filesystem right from the server-side code.
JSON APIs are for specialised clients, not for the web interface.
This is a Python project, not a JavaScript project; treat it as such.
For live updates, get a segment of HTML, not JSON.
HTML
Use semantic tags where possible, and minimise the reliance of classes. If more than 10% of your
tags are <div>
or <span>
, you're doing it wrong.
Use id
if there is only one instance of the element on the page, not class
.
Don't define custom attributes except data-*
attributes.
CSS
Tag selectors are allowed! Style the default widgets as you see fit, because it leads to
cleaner HTML. We also apply a reset stylesheet to make sure the default styles are consistent.
In what scenario would you want an unstyled button in your site? Never! Then why always use
<button class="btn btn-primary">
when it's the only kind of <button>
your site has?
However, provide class-based alternatives for tag styles. For example, Efficient UI styles
button
by default, but it also styles .button
to allow hyperlinks or other elements to look
like buttons. Using both isn't needed though.
Also, selectors can be nested where it makes sense, however the >
selector is preferred over
plain nesting, which is generally discouraged.
For more information, read my article, Let's write more semantic CSS.
Use of fancy counters and data-attributes is allowed, but only for cosmetic purposes. We've got server-side templating, profit from it!
Inline CSS is only allowed to affect layout, not appearance.
New, application-specific styles should be added to /static/style.css
. The CSS framework
located in /static/efficient-ui/*
should be kept reusable and generic; it will be published
separately in the future. Improvements to the framework are welcome, but they should keep these
goals in mind.
JavaScript
Event attributes are allowed, but please keep the JS inside shorter than a few tens of characters and limited to a single instruction. If you need more, use a separate script tag. Acceptable event JS includes, but is not limited to:
document.getElementById("dialog").showModal();
document.getElementById("id").classList.toggle("class");
myFunction();
document.getElementById("id").innerText = this.value;
If you're repeating the same event handler, put a class on the element and use addEventListener
;
it will produce smaller HTML.
Scripts should be small, independent and reusable, only added to the pages that require them,
in the Jinja block {% block scripts %}
.
Other
You may not call third-party JavaScripts or CSS; you must copy them to the repository. If you do, make sure they are licensed under a compatible licence.
Do not add features to snoop on users.
Make sure the site stays clean and loads quickly.
1Contributing Requirements 2========================= 3 4Style Guide 5----------- 6 7This document provides conventions for the coding style used in the Roundabout project. 8Rules can be broken -- just try to make the code as readable as you can. 9 10When something is not covered by this document, refer to [PEP 8](https://peps.python.org/pep-0008/). 11However, please read this even if you know PEP 8, as it diverges in some places. 12 13### Code Layout 14 15#### Indents and Line Continuation 16Keep lines up to 80 characters long. Use 4 spaces for indentation, per level. Do not use tabs, and do not put trailing whitespace. 17 18For parentheses, three styles are accepted: 19~~~python 20# No indent. 21thing = function(arg1, arg2) 22 23# Hanging indent. Align to the contents, not to the bracket. 24thing = function(arg1, arg2 25arg3, arg4, arg5) 26 27# Full indent. Here only one argument or variable or whatever is allowed per line. 28# The closing bracket must be at the previous indent level. 29# Any level is allowed, but it must be a multiple of 4. 30thing = function( 31arg1, 32arg2, 33arg3, 34arg4 35) 36~~~ 37 38Collections should read like lists, not tables. 39~~~python 40fruits = ["apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine",] 41 42fruits = [ 43"apple", 44"tomato", 45"pear", 46"cherry", 47"plum", 48"melon", 49"grape", 50"aubergine", 51] 52 53# Don't! 54fruits = [ 55"apple", "tomato", "pear", "cherry", 56"plum", "melon", "grape", "aubergine", 57] 58~~~ 59 60Additionally, for a collection meant to be expanded use a trailing comma. 61 62For long expressions, begin the line with the operator and align the operand with the indentation level. 63This is not permitted by PEP 8, but it follows mathematics and leads to nicer layouts. 64~~~python 65population = (population 66+ births 67+ immigrants 68- deaths 69- emigrants) 70~~~ 71 72When the hanging indent could be mistaken for a block, add an empty line. 73 74Backslashes are discouraged, but allowed if it is the only way to write your expression. For 75example: 76 77~~~python 78really_long_variable_name_hopefully_yours_wont_be_as_long = \ 79"Really long value." 80~~~ 81 82#### Blank Line Rules 83* Two between top-level class or function definitions. 84* One between local functions or methods. 85* One is allowed to separate related groups and logical sections. 86* One at the end of the file. 87* One below imports. 88 89#### Imports 90 91Imports should be on separate lines. `from`-imports must import all objects on the same line though. 92~~~python 93import flask 94import os 95 96from models import User, Post 97~~~ 98 99Imports should be ordered like this: 100* `__future__` statements 101* one blank line 102* magic names (`__all__`, `__version__`) 103* one blank line 104* library imports 105* library `from`-imports 106* one blank line 107* application imports 108* application `from`-imports 109 110Wildcard imports are discouraged and prohibited for libraries. However, they are fine if the module 111defines `__all__`. 112 113#### Interior Whitespace 114Never insert more than one space around operators and other symbols. Never add spaces just to align lines. 115 116##### When to use 117* around the lowest priority operators in an expression, comparisons, assignments, and logical operators, as well as the function annotation arrow 118~~~python 119# Do 120thing += 10 + 2*thing 121# Don't 122thing+=10+2 * thing 123# Acceptable; use your best judgement 124thing += 10 + 2 * thing 125~~~ 126* after the colon when defining a single-line clause, or an annotation 127* after commas or semicolons 128* after the comment sign `#` 129#### When to avoid 130* when passing keyword arguments or defining argument defaults, unless they are annotated 131* inside any brackets 132~~~python 133# Do 134function(arg1, arg2) 135# Don't! 136function( arg1, arg2 ) 137~~~ 138* after a trailing comma 139~~~python 140# Do 141(0,) 142# Don't! 143(0, ) 144~~~ 145* before commas, semicolons or colons 146~~~python 147# Do 148x, y = y, x 149# Don't! 150x , y = y , x 151~~~ 152* around the slice operator `:` 153* before an argument list 154~~~python 155# Do 156function(arg1, arg2) 157# Don't! 158function (arg1, arg2) 159~~~ 160* before the indexing operator 161~~~python 162# Do 163dictionary["key"] = "Hello World!" 164# Don't! 165dictionary ["key"] = "Hello World!" 166~~~ 167 168#### Other 169Never use the semicolon for multiple statements; while allowed, it is discouraged as it hurts readability. Similarly discouraged are one-line clauses. 170 171### Strings 172 173Only use double quotes like `"` for strings: single quotes are harder to spot, and they conflict with the common apostrophe. 174 175For docstrings you should follow [PEP 257](https://peps.python.org/pep-0257/). Additionally, docstrings should use Markdown to allow for a future tool to convert them to docs. 176HOWEVER arguments should be described using the ReST syntax, as it is more readable in the code. 177Other than this, they should be kept plaintext to prevent markup language battles (Python prefers 178RST, but most use Markdown). 179 180### Naming 181 182Identifiers must only use ASCII characters. Abbreviations should be kept to a minimum and it should be 183made sure that they are widely used and accepted already. 184(`id` for `identifier` or `repo` for `repository` is acceptable, but `avg` for `average` is not). Generally, names should be easy to pronounce. 185 186Class names must use `UpperCamelCase` with the first letter of each word capitalised and no underscores. 187 188Other names (function, variable) should use `snake_case` with all words lowercase and separated by underscores. 189 190For instance and class methods, only name the first argument `self` or `cls`, respectively. 191 192To avoid conflicts, append an underscore or use a synonym but do not corrupt the spelling (`class_` is better than `clss` or `klass` or `classs` or whatever). 193 194Constants are an exception. They should use `UPPER_CASE` with underscores. 195 196Non-public names should be prefixed with an underscore. In case it's really important to not use 197them, use a double underscore to invoke name mangling. 198 199### Comments 200 201Block comments should be complete sentences; line comments don't need to. Write comments in 202English and always use a space after the comment sign, as stated above, unless it's an UNIX 203interpreter descriptor (`#!`) where you should not. Inside block comments, separate paragraphs 204with an empty comment, like in Markdown. For a solo sentence, full stops are optional. 205Avoid stating the obvious or contradicting the code. 206 207Inline comments must be separated with more than one space from the statement, and they may be 208aligned. 209 210In comments, never alter the case of identifiers, as it may lead to confusion. 211 212Leaving TODO comments as personal notes is allowed, but they should be removed before merging 213or a release. 214 215Programming guidelines 216---------------------- 217 218### OOP Guidelines 219Do not use getters and setters for class attributes. If you do need to change some other things, 220use properties. 221 222Prefer overloading the operators; make using your objects as natural and Pythonic as possible. 223 224### Exceptions 225Make exceptions specific enough, so catching them can be explicit. 226 227Do not use the bare `except` clause. Make `try` clauses as short as possible to avoid silencing 228unrelated bugs. 229 230### Other 231Comparisons to singletons (`True`, `False`, `None` etc.) should be done with the identity operators 232`is` and `is not`, not with the comparison operators. Use `is not`, not `not ... is`. 233 234Unless it would be ambiguous, use the implicit truth test to check that numbers are different to 2350, that containers have contents and similar tests. 236 237Do not assign lambdas to identifiers. Make a real function instead. 238 239Use `with` context managers to ensure resources are properly cleaned up. 240 241Prefer making functions that take arguments and return a value instead of making them directly take 242global variables or process the information such as writing. 243 244Use the methods `startswith()` and `endswith()` instead of string slicing to check for prefixes 245or suffixes. 246 247To compare types, use `isinstance()` instead of the `is` operator with `type()` (this is one of 248my problems with Python, but it's the standard). 249 250Use a proper condition for `while` instead of a `while True` that `break`s. 251 252Use `for` loops instead of `while` loops when possible, and use Pythonic iteration instead of 253C-style iteration. 254 255To call shells, use the `subprocess` module instead of `os.system()` and use the functions 256that allow giving a *list* of arguments instead of a string, it leads to better security. 257 258Similarly, avoid writing SQL manually, use Alchemy. If you must write SQL manually, be extra 259careful. 260 261Jinja style guide 262----------------- 263 264Jinja should be written like Python, with the following additions: 265 266Tags should be written as `{% <content> %}` and expressions as `{{ <content> }}`. That is, 267put spaces around the content. 268 269Always indent tag contents, just like you would indent HTML tags! If a tag contains other tags 270and it wouldn't disrupt whitespace, you should indent the contents. An exception can be made 271for top-level `{% block %}` tags, because indenting them would add an extra level of indentation 272to all HTML. 273 274The filter operator `|` should have spaces around it, unless it's in a more complex expression 275when it shouldn't. 276 277Translations should always be done with the `{% trans %}` tag provided by Babel, not with 278`gettext()`, `_()` or others. No exceptions. 279 280The quoting rules are as in Python, unless it's in an HTML attribute, in which case you should 281use single quotes, as HTML takes precedence. 282 283If you're trying to use some function that transforms or checks values, create additional filters 284or tests; they look cleaner. 285 286HTML style guide 287---------------- 288 289HTML tags should be written in lowercase, with attributes in lowercase as well. Attribute values 290should be quoted with double quotes. Attribute minimisation is suggested for boolean attributes. 291 292Always indent tag contents with 4 spaces, except in plaintext tags like `pre` or `textarea`, where 293you should not indent as it affects rendering. 294 295The tag should be multiple lines if the content is complex. Otherwise, mirror the page layout 296- so use: 297 298```html 299<button>Label</button> <!-- Good, because the content is simple AND the button is inline --> 300<p> <!-- Good, because even though the content is simple, the tag is a block --> 301Content 302</p> 303<button> <!-- Good, because the content is complex --> 304<iconify-icon icon="mdi:plus"></iconify-icon> 305Add 306</button> 307``` 308 309IDs or classes should be written in `kebab-case`. Names should be written in `snake_case` to 310provide better compatibility with Python. 311 312Also, when making custom tags, always use a hyphen in the tag name, to make sure they won't be 313standardised in the future. 314 315Inline CSS and JS should end with a semicolon, even if it's the only instruction. 316 317CSS style guide 318--------------- 319 320CSS selectors, properties and values should be written in lowercase. Custom properties should be 321written in `kebab-case`. 322 323Always indent the contents of a ruleset with 4 spaces. 324 325Unlike some other style guides, we do not require each selector after a comma to be on a new line. 326However, if they're too long, very complex or similar and they benefit from alignment, you should 327do so. 328 329IDs are preferred over classes when the element only appears once on the page. 330 331JavaScript style guide 332---------------------- 333 334JS should use double quotes for strings, and lowerCamelCase for names, and indenting should be 4 spaces. 335Otherwise I can't comment, because JS is ugly by nature. Try to make it as readable as possible 336and consistent with the other code. Also, use semicolons. 337 338`var` is not deprecated and may be the best choice in some cases. Use `let` for temporary variables, 339but not `const`, as it provides a false sense of security by forbidding reassignment. However, if 340you `const` a mutable object, you can still change its contents. 341 342Web programming guidelines 343-------------------------- 344 345As a modern web developer, you **will** be disappointed. In the roundabout project, we write 346HTML and generate it on the server (which is not JavaScript, but Python/Flask). You may have 347to relearn web development if you want to contribute. 348 349Also, we use semantic HTML, which means we use tags for their intended purpose. If you're using 350`<div>`, `<span>`, `<input>` and `<img>` for everything, you're doing it wrong. 351 352We don't like bloat either, so we don't use `<button class="btn btn-primary color-blue btn-large">` 353when `<button>` is enough. If you insist on something like Tailwind, you're in the wrong place. 354 355It does not mean we do web development like in 2004. We use the modern features browsers gift us: 356* The powerful CSS layout engines, flexbox and gridbox 357* HTML5 semantic tags 358* JavaScript (plain!) for interactivity 359* SVG 360* Web fonts 361 362and, most importantly, we use Flask, which didn't even exist in 2004. 363 364In short: 365* The roundabout is not a SPA. 366* JS should be used to enhance the experience, not to create it. 367* We don't use client-side frameworks. 368* The server is a safe environment, so we can access the database and filesystem right from the 369server-side code. 370* JSON APIs are for specialised clients, not for the web interface. 371* This is a Python project, not a JavaScript project; treat it as such. 372* For live updates, get a segment of HTML, not JSON. 373 374### HTML 375 376Use semantic tags where possible, and minimise the reliance of classes. If more than 10% of your 377tags are `<div>` or `<span>`, you're doing it wrong. 378 379Use `id` if there is only one instance of the element on the page, not `class`. 380 381Don't define custom attributes except `data-*` attributes. 382 383### CSS 384 385Tag selectors are **allowed**! Style the default widgets as you see fit, because it leads to 386cleaner HTML. We also apply a reset stylesheet to make sure the default styles are consistent. 387In what scenario would you want an *unstyled* button in your site? Never! Then why always use 388`<button class="btn btn-primary">` when it's the only kind of `<button>` your site has? 389 390However, provide class-based alternatives for tag styles. For example, Efficient UI styles 391`button` by default, but it also styles `.button` to allow hyperlinks or other elements to look 392like buttons. Using both isn't needed though. 393 394Also, selectors can be nested where it makes sense, however the `>` selector is preferred over 395plain nesting, which is generally discouraged. 396 397For more information, read my article, 398[Let's write more semantic CSS](https://roundabout.roundabout-host.com/articles/semantic-css.html). 399 400Use of fancy counters and data-attributes is allowed, but only for cosmetic purposes. We've got 401server-side templating, profit from it! 402 403Inline CSS is only allowed to affect layout, *not* appearance. 404 405New, application-specific styles should be added to `/static/style.css`. The CSS framework 406located in `/static/efficient-ui/*` should be kept reusable and generic; it will be published 407separately in the future. Improvements to the framework are welcome, but they should keep these 408goals in mind. 409 410### JavaScript 411 412Event attributes are allowed, but please keep the JS inside shorter than a few tens of characters 413and limited to a single instruction. If you need more, use a separate script tag. Acceptable event 414JS includes, but is not limited to: 415 416* `document.getElementById("dialog").showModal();` 417* `document.getElementById("id").classList.toggle("class");` 418* `myFunction();` 419* `document.getElementById("id").innerText = this.value;` 420 421If you're repeating the same event handler, put a class on the element and use `addEventListener`; 422it will produce smaller HTML. 423 424Scripts should be small, independent and reusable, only added to the pages that require them, 425in the Jinja block `{% block scripts %}`. 426 427Other 428----- 429 430You may not call third-party JavaScripts or CSS; you must copy them to the repository. If you 431do, make sure they are licensed under a compatible licence. 432 433Do not add features to snoop on users. 434 435Make sure the site stays clean and loads quickly. 436