Skip to content

Latest commit

 

History

History
292 lines (221 loc) · 14.4 KB

File metadata and controls

292 lines (221 loc) · 14.4 KB

Coding standards

This document is the human- and agent-readable summary of the lint rules enforced across the Magma monorepo. The canonical sources of truth are the config files; if anything here ever conflicts with them, the config wins.

Layer Root config Project overrides
TypeScript / JS eslint.config.mjs projects/stencil/eslint.config.mjs, projects/design-tokens/eslint.config.mjs
CSS stylelint.config.mjs projects/stencil/stylelint.config.mjs

Enforcement runs in three places:

  • On save - if your editor has ESLint and Stylelint extensions installed
  • On commit - via the .husky/pre-commit hook (lint + format on staged files)
  • In CI - the lint workflow (.github/workflows/lint.yml) runs npm run lint on every pull request and on every push to dev, main and beta; a lint error fails the check

TypeScript / JavaScript

Base rules

The root config extends:

  • @eslint/js recommended
  • typescript-eslint recommended

Common settings:

  • Target: ECMAScript 2021 (ecmaVersion: 12), module source type
  • Globals: browser + node
  • Ignored: **/node_modules, **/dist, **/.cache, **/*.config.*js, plus everything in .gitignore

Stencil project

projects/stencil/eslint.config.mjs layers additional rules on top of the base:

  • JSX pragmas not unused - h and Fragment imports are not flagged as unused. They're required for Stencil's TSX compilation.

    // ✅ allowed even if `h` looks unused - Stencil's JSX needs it
    import { h, Fragment, Component } from '@stencil/core';
  • @typescript-eslint/no-unused-vars is warn, not error - drift won't block commits, but should be cleaned up.

  • Stencil plugin (@stencil/eslint-plugin) - its flat/recommended rule set applies to src/components/**/*.tsx (excluding .storybook/** and *.stories.*).

  • stencil/strict-mutable is off - mutable @Prop() is allowed when needed.

  • stencil/reserved-member-names is off - the autofocus/autoFocus props of mds-button, mds-button-dropdown, mds-input, mds-input-select and mds-input-switch mirror the native attribute on purpose; renaming them would break the public API.

  • Storybook plugin (eslint-plugin-storybook) - flat/recommended applies to story files.

  • Ignored directories: react, angular (these are auto-generated framework adapters and are not linted).

Design tokens project

projects/design-tokens/eslint.config.mjs adds Jest globals (the token build has test fixtures) and ignores node_modules, dist, build.

Running ESLint

# In projects/stencil/
npm run lint.ts       # check
npm run lint:fix      # auto-fix what's fixable

# Monorepo-wide
nx run-many --target=lint --all

CSS

stylelint.config.mjs extends:

  • stylelint-config-standard
  • stylelint-config-recess-order
  • stylelint-order

The rules below are the ones that constrain authoring decisions. Many additional internal-correctness rules are also enforced - see the config for the full list.

Property ordering and layout

Rule Effect
order/order Custom properties (--foo: …) come before regular declarations within a block
order/properties-alphabetical-order Declarations inside a block are sorted A→Z
declaration-block-single-line-max-declarations: 1 One declaration per line
block-no-empty Empty blocks are forbidden
declaration-empty-line-before Blank line before declarations, with exceptions for first-nested / after-comment / after-declaration
custom-property-empty-line-before Blank line before custom properties, with same exceptions plus after-custom-property
rule-empty-line-before Blank line before multi-line rules, except first-nested
/* ✅ correct */
.foo {
  --foo-bg: rgb(0 0 0);

  background-color: rgb(255 255 255);
  color: rgb(0 0 0);
  display: flex;
}

/* 🚫 wrong: property order, multiple decls on one line, empty block */
.bar {
  display: flex;
  color: rgb(0 0 0);
  background-color: rgb(255 255 255);
}

.baz {
}

Colors

Rule Effect
color-function-notation: 'modern' Use rgb(0 0 0 / 0.5), not rgba(0, 0, 0, 0.5)
color-hex-length: 'long' Use #ffffff, not #fff
color-named: 'never' No CSS named colors (red, white, aliceblue, …)
alpha-value-notation: 'number' Use / 0.5, not / 50%
hue-degree-notation: 'number' Use hsl(120 …), not hsl(120deg …) - though hsl is also disallowed (below)
function-disallowed-list: ['hsl', 'lch'] Use rgb() (typically with token vars), not hsl() or lch()
function-no-unknown Unknown color functions are rejected
/* ✅ correct */
.foo {
  background-color: rgb(255 255 255);
  color: rgb(var(--tone-neutral-03) / 0.5);
}

/* 🚫 wrong */
.foo {
  background-color: white; /* named color */
  background-color: #fff; /* short hex */
  color: rgba(0, 0, 0, 0.5); /* legacy notation */
  color: rgb(0 0 0 / 50%); /* percentage alpha */
  color: hsl(0 0% 0%); /* hsl disallowed */
}

Units

Rule Effect
unit-disallowed-list: ['cm','em','ex','in','mm','pc','pt'] Only px, rem, %, vh, vw, fr, s, ms, etc. Never em
number-max-precision: 4 Max 4 decimal places (units % and unitless are exempt)
time-min-milliseconds: 100 Animation/transition durations must be ≥ 100 ms
length-zero-no-unit: null 0px is allowed (rule is disabled)
/* ✅ correct */
.foo {
  font-size: 1rem;
  padding: 16px;
  transition: opacity 200ms ease;
}

/* 🚫 wrong */
.foo {
  font-size: 1em; /* em disallowed */
  padding: 1in; /* in disallowed */
  transition: opacity 50ms; /* below 100ms minimum */
}

Selectors

Rule Effect
selector-class-pattern: '^[a-z-_]+' Class names are lowercase, kebab- or snake-case
selector-attribute-quotes: 'always' Always quote attribute selectors: [type="button"], not [type=button]
selector-type-case: 'lower' Element selectors lowercase
selector-pseudo-class-no-unknown Unknown pseudo-classes rejected
selector-pseudo-element-no-unknown Unknown pseudo-elements rejected
selector-type-no-unknown Unknown element selectors rejected, except custom elements (mds-*) and namespaces
no-duplicate-selectors Same selector cannot appear twice in a file
/* ✅ correct */
.my-button { … }
mds-button[type="primary"] { … }

/* 🚫 wrong */
.MyButton { … }                /* PascalCase */
[type=button] { … }            /* unquoted attribute */

Properties and values

Rule Effect
property-disallowed-list: ['background'] Don't use the background shorthand. Use background-color, background-image, etc.
property-no-vendor-prefix No -webkit-, -moz-, etc. Exception: -webkit-overflow-scrolling
value-no-vendor-prefix No vendor-prefixed values either
value-keyword-case: 'lower' Keyword values lowercase: display: flex, not display: Flex
font-weight-notation: 'numeric' Use font-weight: 400, not font-weight: normal
shorthand-property-no-redundant-values padding: 0 0 0 0 is rejected - use padding: 0
declaration-block-no-shorthand-property-overrides Don't override a longhand with a later shorthand (and vice versa) in the same block
at-rule-no-vendor-prefix No vendor-prefixed at-rules
/* ✅ correct */
.foo {
  background-color: rgb(255 255 255);
  background-image: url('hero.png');
  font-weight: 400;
}

/* 🚫 wrong */
.foo {
  background: white url('hero.png'); /* shorthand disallowed */
  -webkit-transform: scale(1); /* vendor prefix */
  font-weight: bold; /* must be numeric */
}

At-rules

@import, @layer, @apply, @include, @tailwind, @reference, @use, @container, @custom-variant, @function, etc. are allowed and recognised. The full list of permitted custom at-rules is in at-rule-no-unknown in the config. Stick to recognised at-rules; if you need a new one, add it to the config first.

/* ✅ correct */
@import 'normalize.css' layer(reset);

@layer base {
  body {
    color: rgb(0 0 0);
  }
}

import-notation: 'string' requires the bare-string form (@import 'foo.css'), not the url() form (@import url('foo.css')). This is reiterated in projects/stencil/stylelint.config.mjs.

Comments

Rule Effect
comment-word-disallowed-list: ['/A-Z/', 'todo:'] Comments cannot start with an uppercase letter and cannot contain todo:
comment-empty-line-before: 'always' Blank line before comments
comment-no-empty No empty comments
comment-whitespace-inside: 'always' Whitespace inside the delimiters: /* foo */, not /*foo*/
/* ✅ correct */

/* base layout overrides */
.foo {
  display: flex;
}

/* 🚫 wrong: leading uppercase, todo, no whitespace */
/* Base layout */
/* todo: change later */
/*foo*/

Animations and timing

  • Durations must be ≥ 100ms (time-min-milliseconds)
  • keyframe-declaration-no-important - never !important inside @keyframes
  • function-linear-gradient-no-nonstandard-direction - gradients use modern direction syntax

Project-specific notes

projects/stencil/stylelint.config.mjs extends the root and ignores compiled output (**/dist/*.css) and non-CSS files (*.js, *.jsx, *.svg). The only rule override is import-notation: 'string'.

Running Stylelint

# In projects/stencil/
npm run lint.css

# Auto-fix where possible
npx stylelint --fix 'src/**/*.css'

Quick reference for AI agents

When writing or editing code in this monorepo:

  • TypeScript: keep h and Fragment imports even if unused; respect Stencil plugin rules in .tsx files
  • CSS authoring:
    • Use rgb(r g b / a) modern syntax with numeric alpha; never named colors, never hsl/lch, never short hex
    • Use px, rem, %, vh/vw for sizing; never em/cm/in/mm/pc/pt
    • Sort declarations alphabetically; custom props first; one declaration per line
    • Use specific background-* properties, never the background shorthand
    • No vendor prefixes (except -webkit-overflow-scrolling)
    • Class names lowercase kebab/snake; quote attribute selectors
    • Numeric font-weight (400, not normal)
    • No todo: comments; comments must start lowercase
    • Animation/transition durations ≥ 100 ms
  • Before committing: the pre-commit hook will run lint + format on staged files; fix the issues it reports rather than bypassing the hook.