Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 83 additions & 41 deletions CODING_STANDARDS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Angular Material Coding Standards


## Code style

The [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html) is the
Expand All @@ -12,6 +11,7 @@ ES6 or TypeScript.
### General

#### Write useful comments

Comments that explain what some block of code does are nice; they can tell you something in less
time than it would take to follow through the code itself.

Expand All @@ -21,6 +21,7 @@ the original author. When collaborators are in the same room, this hurts product
When collaborators are in different timezones, this can be devastating to productivity.

For example, this is a not-very-useful comment:

```ts
// Set default tabindex.
if (!$attrs['tabindex']) {
Expand All @@ -29,6 +30,7 @@ if (!$attrs['tabindex']) {
```

While this is much more useful:

```ts
// Unless the user specifies so, the calendar should not be a tab stop.
// This is necessary because ngAria might add a tabindex to anything with an ng-model
Expand All @@ -48,20 +50,23 @@ In HTML code, use `<!-- ... -->` comments, which will be stripped when packaging
#### Prefer more focused, granular components vs. complex, configurable components.

For example, rather than doing this:

```html
<mat-button>Basic button</mat-button>
<mat-button class="mat-fab">FAB</mat-button>
<mat-button class="mat-icon-button">pony</mat-button>
```

do this:

```html
<mat-button>Basic button</mat-button>
<mat-fab>FAB</mat-fab>
<mat-icon-button>pony</mat-icon-button>
```

#### Prefer small, focused modules

Keeping modules to a single responsibility makes the code easier to test, consume, and maintain.
ES6 modules offer a straightforward way to organize code into logical, granular units.
Ideally, individual files are 200 - 300 lines of code.
Expand All @@ -70,6 +75,7 @@ As a rule of thumb, once a file draws near 400 lines (barring abnormally long co
start considering how to refactor into smaller pieces.

#### Less is more

Once a feature is released, it never goes away. We should avoid adding features that don't offer
high user value for price we pay both in maintenance, complexity, and payload size. When in doubt,
leave it out.
Expand All @@ -78,12 +84,14 @@ This applies especially to providing two different APIs to accomplish the same t
prefer sticking to a _single_ API for accomplishing something.

### 100 column limit

All code and docs in the repo should be 100 columns or fewer. This applies to TypeScript, SCSS,
HTML, bash scripts, and markdown files.

### API Design

#### Boolean arguments

Avoid adding boolean arguments to a method in cases where that argument means "do something extra".
In these cases, prefer breaking the behavior up into different functions.

Expand All @@ -108,6 +116,7 @@ function createTargetElement() {
### TypeScript

#### Typing

Avoid `any` where possible. If you find yourself using `any`, consider whether a generic may be
appropriate in your case.

Expand All @@ -116,7 +125,9 @@ specified because our documentation tooling cannot currently infer types in plac
can.

#### Fluent APIs

When creating a fluent or builder-pattern style API, use the `this` return type for methods:

```
class ConfigBuilder {
withName(name: string): this {
Expand All @@ -127,27 +138,29 @@ class ConfigBuilder {
```

#### Access modifiers
* Omit the `public` keyword as it is the default behavior.
* Use `private` when appropriate and possible, prefixing the name with an underscore.
* Use `protected` when appropriate and possible with no prefix.
* Prefix *library-internal* properties and methods with an underscore without using the `private`
keyword. This is necessary for anything that must be public (to be used by Angular), but should not
be part of the user-facing API. This typically applies to symbols used in template expressions,
`@ViewChildren` / `@ContentChildren` properties, host bindings, and `@Input` / `@Output` properties
(when using an alias).

Additionally, the `@docs-private` JsDoc annotation can be used to hide any symbol from the public
API docs.

- Omit the `public` keyword as it is the default behavior.
- Use `private` when appropriate and possible, prefixing the name with an underscore.
- Use `protected` when appropriate and possible with no prefix.
- Prefix _library-internal_ properties and methods with an underscore without using the `private`
keyword. This is necessary for anything that must be public (to be used by Angular), but should not
be part of the user-facing API. This typically applies to symbols used in template expressions,
`@ViewChildren` / `@ContentChildren` properties, host bindings, and `@Input` / `@Output` properties
(when using an alias).

Additionally, the `@nodoc` JsDoc annotation can be used to hide any symbol from the public
API docs.

#### Getters and Setters
* Only use getters and setters for `@Input` properties or when otherwise required for API
compatibility.
* Avoid long or complex getters and setters. If the logic of an accessor would take more than
three lines, introduce a new method to contain the logic.
* A getter should immediately precede its corresponding setter.
* Decorators such as `@Input` should be applied to the getter and not the setter.
* Always use a `readonly` property instead of a getter (with no setter) when possible.

- Only use getters and setters for `@Input` properties or when otherwise required for API
compatibility.
- Avoid long or complex getters and setters. If the logic of an accessor would take more than
three lines, introduce a new method to contain the logic.
- A getter should immediately precede its corresponding setter.
- Decorators such as `@Input` should be applied to the getter and not the setter.
- Always use a `readonly` property instead of a getter (with no setter) when possible.

```ts
/** YES */
readonly active: boolean;
Expand All @@ -165,17 +178,19 @@ All public APIs must have user-facing comments. These are extracted and shown in
on [material.angular.dev](https://material.angular.dev).

Private and internal APIs should have JsDoc when they are not obvious. Ultimately it is the purview
of the code reviewer as to what is "obvious", but the rule of thumb is that *most* classes,
of the code reviewer as to what is "obvious", but the rule of thumb is that _most_ classes,
properties, and methods should have a JsDoc description.

Properties should have a concise description of what the property means:

```ts
/** The label position relative to the checkbox. Defaults to 'after' */
@Input() labelPosition: 'before' | 'after' = 'after';
```

Methods blocks should describe what the function does and provide a description for each parameter
and the return value:

```ts
/**
* Opens a modal dialog containing the given component.
Expand All @@ -187,6 +202,7 @@ and the return value:
```

Boolean properties and return values should use "Whether..." as opposed to "True if...":

```ts
/** Whether the button is disabled. */
disabled: boolean = false;
Expand All @@ -198,21 +214,24 @@ Avoid `try-catch` blocks, instead preferring to prevent an error from being thro
place. When impossible to avoid, the `try-catch` block must include a comment that explains the
specific error being caught and why it cannot be prevented.


#### Naming

##### General
* Prefer writing out words instead of using abbreviations.
* Prefer *exact* names to short names (within reason). E.g., `labelPosition` is better than
`align` because the former much more exactly communicates what the property means.
* Except for `@Input` properties, use `is` and `has` prefixes for boolean properties / methods.

- Prefer writing out words instead of using abbreviations.
- Prefer _exact_ names to short names (within reason). E.g., `labelPosition` is better than
`align` because the former much more exactly communicates what the property means.
- Except for `@Input` properties, use `is` and `has` prefixes for boolean properties / methods.

##### Observables
* Don't suffix observables with `$`.

- Don't suffix observables with `$`.

##### Classes

Classes should be named based on what they're responsible for. Names should capture what the code
*does*, not how it is used:
_does_, not how it is used:

```
/** NO: */
class RadioService { }
Expand All @@ -229,7 +248,8 @@ CDK classes should only have a `Cdk` prefix when the class is a directive with a
prefix.

##### Methods
The name of a method should capture the action that is performed *by* that method rather than

The name of a method should capture the action that is performed _by_ that method rather than
describing when the method will be called. For example,

```ts
Expand All @@ -245,11 +265,12 @@ activateRipple() {
```

##### Selectors
* Component selectors should be lowercase and delimited by hyphens. Components should use element
selectors except when the component API uses a native HTML element.
* Directive selectors should be camel cased. Exceptions may be made for directives that act like a
component but would have an empty template, or when the directive is intended to match some
existing attribute.

- Component selectors should be lowercase and delimited by hyphens. Components should use element
selectors except when the component API uses a native HTML element.
- Directive selectors should be camel cased. Exceptions may be made for directives that act like a
component but would have an empty template, or when the directive is intended to match some
existing attribute.

#### Inheritance

Expand All @@ -258,20 +279,25 @@ behaviors can be composed. Instead, [TypeScript mixins][ts-mixins] can be used t
common behaviors into a single component.

#### Coercion

Component and directive inputs for boolean and number values must use an input transform function
to coerce values to the expected type.
For example:

```ts
import {Input, booleanAttribute} from '@angular/core';

@Input({transform: booleanAttribute}) disabled: boolean = false;
```

The above code allows users to set `disabled` similar to how it can be set on native inputs:

```html
<component disabled></component>
```

#### Expose native inputs

Native inputs used in components should be exposed to developers through `ng-content`. This allows
developers to interact directly with the input and allows us to avoid providing custom
implementations for all the input's native behaviors.
Expand All @@ -281,11 +307,13 @@ For example:
**Do:**

Implementation

```html
<ng-content></ng-content>
```

Usage

```html
<your-component>
<input>
Expand All @@ -295,41 +323,45 @@ Usage
**Don't:**

Implementation

```html
<input>
```

Usage

```html
<component></component>
```


### Angular

#### Host bindings

Prefer using the `host` object in the directive configuration instead of `@HostBinding` and
`@HostListener`. We do this because TypeScript preserves the type information of methods with
decorators, and when one of the arguments for the method is a native `Event` type, this preserved
type information can lead to runtime errors in non-browser environments (e.g., server-side
pre-rendering).


### CSS

#### Be cautious with use of `display: flex`
* The [baseline calculation for flex elements](https://www.w3.org/TR/css-flexbox-1/#flex-baselines)
is different from other display values, making it difficult to align flex elements with standard
elements like input and button.
* Component outermost elements are never flex (block or inline-block)
* Don't use `display: flex` on elements that will contain projected content.

- The [baseline calculation for flex elements](https://www.w3.org/TR/css-flexbox-1/#flex-baselines)
is different from other display values, making it difficult to align flex elements with standard
elements like input and button.
- Component outermost elements are never flex (block or inline-block)
- Don't use `display: flex` on elements that will contain projected content.

#### Use the lowest specificity possible

Always prioritize lower specificity over other factors. Most style definitions should consist of a
single element or css selector plus necessary state modifiers. **Avoid SCSS nesting for the sake of
code organization.** This will allow users to much more easily override styles.

For example, rather than doing this:

```scss
.mat-calendar {
display: block;
Expand All @@ -345,6 +377,7 @@ For example, rather than doing this:
```

do this:

```scss
.mat-calendar {
display: block;
Expand All @@ -360,9 +393,11 @@ do this:
```

#### Never set a margin on a host element.

The end-user of a component should be the one to decide how much margin a component has around it.

#### Prefer styling the host element vs. elements inside the template (where possible).

This makes it easier to override styles when necessary. For example, rather than

```scss
Expand All @@ -376,6 +411,7 @@ the-host-element {
```

you can write

```scss
the-host-element {
// ...
Expand All @@ -386,7 +422,9 @@ the-host-element {
The latter is equivalent for the component, but makes it easier override when necessary.

#### Support styles for Windows high-contrast mode

This is a low-effort task that makes a big difference for low-vision users. Example:

```css
@include cdk-high-contrast(active, off) {
.unicorn-motocycle {
Expand All @@ -396,7 +434,9 @@ This is a low-effort task that makes a big difference for low-vision users. Exam
```

#### Explain what CSS classes are for

When it is not super obvious, include a brief description of what a class represents. For example:

```scss
// The calendar icon button used to open the calendar pane.
.mat-datepicker-button { ... }
Expand All @@ -411,9 +451,11 @@ When it is not super obvious, include a brief description of what a class repres
[ts-mixins]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-mix-in-classes

#### Prefer CSS classes to tag names and attributes for styling

Targeting tag names can cause conflicts with the MDC version of the component. For this reason, use
CSS class names defined by us instead of tag names. We also prefer classes to attributes for
consistency.

```scss
/** Do: */
.mat-mdc-slider { ... }
Expand All @@ -426,4 +468,4 @@ mdc-slider { ... }

/** Don't: */
input[type="button"] { ... }
```
```
Loading
Loading