Lichess maintains consistent code style across its Scala backend and TypeScript frontend. Follow these guidelines to ensure your contributions match the codebase conventions.
Scala Style
Lichess uses Scala 3 with automatic formatting via scalafmt.
Scalafmt Configuration
The project uses scalafmt version 3.10.7 with the following key settings:
version = "3.10.7"
runner.dialect = scala3
align.preset = none
maxColumn = 110
spaces.inImportCurlyBraces = true
Max Column Width
Import Spacing
Scala 3 Syntax
// Maximum 110 characters per line
maxColumn = 110
Rewrite Rules
Scalafmt automatically applies these transformations:
1. Sort Modifiers
// Modifiers are automatically sorted
private final def foo () = ??? // Good order
final private def foo () = ??? // Auto-fixed
2. Avoid Infix Notation
// Avoid infix notation (auto-converted)
list.map(x => x + 1 ) // Good
list map (x => x + 1 ) // Auto-fixed to method call syntax
3. String Interpolation
// Redundant braces in string interpolation removed
s "Hello ${ name } " // Auto-fixed to s"Hello $name"
s "Hello $ name " // Good
4. Import Cleanup
// Redundant import selectors removed automatically
import foo .{ Bar => Bar } // Auto-fixed to import foo.Bar
import foo . Bar // Good
File-Specific Overrides
Build files use Scala 2.13 dialect:
// build.sbt and project/** files
runner.dialect = scala213
Running Scalafmt
Format All Files
Check Formatting
Format Specific File
Most IDEs can run scalafmt automatically on save. Configure your editor to use the project’s .scalafmt.conf file.
TypeScript Style
Lichess uses oxlint for TypeScript/JavaScript linting.
Oxlint Configuration
The project uses oxlint with plugins for promises, TypeScript, and unicorn rules:
{
"plugins" : [ "promise" , "typescript" , "unicorn" ],
"env" : {
"builtin" : true
},
"ignorePatterns" : [ "**/dist/" , "**/public" ]
}
Key Rules
Variable Declarations
// Prefer const/let over var
const name = 'Lichess' ; // Good
let count = 0 ; // Good
var flag = true ; // Warning: use const or let
Imports
// No duplicate imports
import { foo } from './module' ;
import { bar } from './module' ; // Error: combine into one import
// Correct:
import { foo , bar } from './module' ;
Unused Variables
// Prefix unused variables with underscore
function handler ( _event : Event , data : any ) {
// _event is intentionally unused
console . log ( data );
}
Variables matching these patterns are ignored:
varsIgnorePattern: "^_"
argsIgnorePattern: "^_"
caughtErrorsIgnorePattern: "^_"
Promises
// Always reject with Error objects
Promise . reject ( new Error ( 'Something went wrong' )); // Good
Promise . reject ( 'error message' ); // Error
// No callbacks in promises
promise . then ( data => {
callback ( data ); // Error: avoid mixing callbacks and promises
});
// No multiple resolves
Promise (( resolve ) => {
resolve ( 1 );
resolve ( 2 ); // Error: promise already resolved
});
TypeScript-Specific Rules
// Use Record instead of index signatures when possible
type Config = Record < string , string >; // Good
type Config = { [ key : string ] : string }; // Error
// No empty interfaces
interface Props {} // Error: use type alias or add properties
// No unnecessary type assertions
const num = 5 as number ; // Error: already a number
const num = 5 ; // Good
// No unnecessary template expressions
const str = ` ${ name } ` ; // Error: use name directly
const str = name ; // Good
Modern Array Methods
// Prefer modern array methods
arr . flat () // Good
arr . reduce (( a , b ) => a . concat ( b ), []) // Error: use flat()
arr . flatMap ( x => [ x , x * 2 ]) // Good
arr . map ( x => [ x , x * 2 ]). flat () // Error: use flatMap()
arr . some ( x => x > 5 ) // Good
arr . find ( x => x > 5 ) !== undefined // Error: use some()
Restricted Global Variables
Direct access to window globals is restricted. Use explicit window. or document. prefixes to avoid accidental global usage.
The configuration restricts 200+ window globals like addEventListener, focus, blur, etc. to prevent implicit global access.
File-Specific Overrides
Utility scripts have relaxed rules:
{
"files" : [ "bin/**" , "cron/**" ],
"rules" : {
"no-unused-vars" : "off" ,
"unicorn/prefer-array-some" : "off"
}
}
Running Oxlint
Lint All Files
Auto-fix Issues
Lint Specific Directory
CSS/SCSS Style
Lichess uses stylelint for CSS and SCSS linting.
Stylelint Configuration
{
"plugins" : [ "stylelint-scss" ],
"extends" : "stylelint-config-standard-scss"
}
Key Rules
Color Notation
// Use legacy color notation with numbers for alpha
rgba(255, 0, 0, 0 .5 ) // Good
rgb(255 0 0 / 50%) // Error: use legacy notation
// Hue as number, not degree
hsl(120, 100%, 50%) // Good
hsl(120deg, 100%, 50%) // Error: use number
Variable Naming
// Any pattern allowed for variables and classes
$primaryColor : #3893E8 ; // Allowed
$primary-color : #3893E8 ; // Also allowed
The configuration disables pattern enforcement for:
custom-property-pattern
selector-class-pattern
selector-id-pattern
scss/dollar-variable-pattern
SCSS Features
// SCSS global functions allowed (color functions, etc.)
.button {
background : darken ( $primary , 10 % ); // Allowed
}
// Operators can span multiple lines
.container {
width : $base-width
+ $margin ; // Allowed
}
Running Stylelint
Lint All Styles
Auto-fix Issues
pnpm stylelint "**/*.scss"
Editor Integration
Configure your editor to use these tools automatically:
Install these extensions:
Scala (Metals)
oxlint
Stylelint
Configure format on save in settings: {
"editor.formatOnSave" : true ,
"scalafmt.configFilePath" : ".scalafmt.conf"
}
Enable scalafmt: Settings → Editor → Code Style → Scala → Scalafmt
Install oxlint plugin from marketplace
Enable Stylelint: Settings → Languages → Style Sheets → Stylelint
Use formatters via null-ls or conform.nvim:
scalafmt for Scala
oxlint for TypeScript
stylelint for SCSS
Example with null-ls: require ( "null-ls" ). setup ({
sources = {
require ( "null-ls" ). builtins . formatting . scalafmt ,
require ( "null-ls" ). builtins . diagnostics . oxlint ,
require ( "null-ls" ). builtins . formatting . stylelint ,
},
})
Pre-commit Checks
Before committing, ensure:
Run formatters
./lila.sh scalafmtAll
pnpm lint --fix
pnpm stylelint "**/*.scss" --fix
Check for errors
./lila.sh scalafmtCheckAll
pnpm lint
CI will automatically check code style on all pull requests. Fix any style violations before requesting review.
Next Steps
Pull Request Guidelines Learn how to submit your code
Contributing Overview Back to contributing overview