title | version | tags | |||||
---|---|---|---|---|---|---|---|
conformance configuration for linting |
2022.06 |
|
Engineering and Design Practices
- Conformance
- Naming Convention
- Naming Components
- Class vs
React.createClass
vs stateless - TypeScript
- Names
- Exports
- Components
- Types
null
andundefined
- General Assumptions
- Flags
- Comments
- Strings
- When to use
any
- Diagnostic Messages
- General Constructs
- Style
- Reference Specification
Conformance objectives are to reduce AST diff churn and improve developer experience
source@airbnb/javascript#whitespace--in-braces
- 19.12 Add spaces inside curly braces. eslint: object-curly-spacing
// bad
const foo = { clark: 'kent' };
// good
const foo = { clark: 'kent' };
- 8.4 Always include parentheses around arguments for clarity and consistency. eslint: arrow-parens
source@airbnb/javascript#arrows--one-arg-parens
Why? Minimizes diff churn when adding or removing arguments.
// bad
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map((x) => x * x);
// bad
[1, 2, 3].map(
(number) =>
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`,
);
// good
[1, 2, 3].map(
(number) =>
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`,
);
// bad
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
- 13.2 Use one const or let declaration per variable or assignment. eslint: one-var
Why? It’s easier to add new variable declarations this way, and you never have to worry about swapping out a ; for a , or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
goSportsTeam = true;
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
20.2 Additional trailing comma: Yup. eslint:
Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the trailing comma problem in legacy browsers.
// bad - git diff without trailing comma
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};
// good - git diff with trailing comma
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};
Enforce the location of arrow function bodies with implicit returns. eslint:
// bad
(foo) => bar;
(foo) => bar;
// good
(foo) => bar;
(foo) => bar;
(foo) => bar;
We recommend using camel case, pascal case or snake case for your theme tokens. Other word separators may not work as expected.
// recommended
tokenName
token_name
token-name
// avoid
token.name
token$name
token*name
- Extensions: Use
.jsx
extension for React components. eslint:react/jsx-filename-extension
- Filename: Use PascalCase for filenames. E.g.,
ReservationCard.jsx
. - Reference Naming: Use PascalCase for React components and camelCase for their instances. eslint:
react/jsx-pascal-case
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
- Component Naming: Use the filename as the component name. For example,
ReservationCard.jsx
should have a reference name ofReservationCard
. However, for root components of a directory, useindex.jsx
as the filename and use the directory name as the component name:
// bad
import Footer from './Footer/Footer';
// bad
import Footer from './Footer/index';
// good
import Footer from './Footer';
- If you have internal state and/or refs, prefer
class extends React.Component
overReact.createClass
. eslint:react/prefer-es6-class
react/prefer-stateless-function
// bad
const Listing = React.createClass({
// ...
render() {
return <div>{this.state.hello}</div>;
}
});
// good
class Listing extends React.Component {
// ...
render() {
return <div>{this.state.hello}</div>;
}
}
And if you don’t have state or refs, prefer normal functions (not arrow functions) over classes:
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
<div>{hello}</div>
);
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}
These guidelines are adapted from the TypeScript core's contributor coding guidelines.
- Names
- Exports
- Components
- Types
null
andundefined
- General Assumptions
- Flags
- Comments
- Strings
- When to use
any
- Diagnostic Messages
- General Constructs
- Style
- Use PascalCase for type names.
- Do not use "I" as a prefix for interface names.
- Use PascalCase for enum values.
- Use camelCase for function names.
- Use camelCase for property names and local variables.
- Do not use "_" as a prefix for private properties.
- Use whole words in names when possible.
- Use
isXXXing
orhasXXXXed
for variables representing states of things (e.g.isLoading
,hasCompletedOnboarding
). - Give folders/files/components/functions unique names.
- Only use named exports. The only exceptions are a tool requires default exports (e.g
React.lazy()
, Gatsby and Next.jspages
,typography.js
)
- 1 file per logical component (e.g. parser, scanner, emitter, checker).
- If not kept in a separate folder, files with ".generated.*" suffix are auto-generated, do not hand-edit them.
- Tests should be kept in the same directory with ".test.*" suffix
- Filename should match the component name. Interfaces for React components should be called
<ComponentName>Props
and<ComponentName>State
. The only exception is when writing a render prop. In this situation, you, the author, should call the interface for your component's props<ComponentName>Config
and then the render prop interface<ComponentName>Props
so it is easy for everyone else to use.
- Do not export types/functions unless you need to share it across multiple components.
- Do not introduce new types/values to the global namespace.
- Shared types should be defined in 'types.ts'.
- Within a file, type definitions should come first (after the imports).
- Use undefined. Do not use
null
. EVER. If null is used (like in legacy Redux code), it should be kept isolated from other code with selectors.
- Consider objects like Nodes, Symbols, etc. as immutable outside the component that created them. Do not change them.
- Consider arrays as immutable by default after creation.
- More than 2 related Boolean properties on a type should be turned into a flag.
- Use JSDoc style comments for functions, interfaces, enums, and classes.
- Document crazy stuff. Always add
@see <url>
and the current date when referencing external resources, blog posts, tweets, snippets, gists, issues etc. - Make note conditions upon which hacks and smelly code can be removed.
- Use single quotes for strings. Double quotes around JSX string props.
- All strings visible to the user need to be localized (make an entry in diagnosticMessages.json).
- If something takes you longer than 10 minutes to type or you feel the need to read through TS Advanced Types docs, you should take a step back and ask for help, or use
any
. - Custom typings of 3rd-party modules should be added to a
.d.ts
file in atypings
directory. Document the date and version of the module you are typing at the top of the file. - Consider rewriting tiny modules in typescript if types are too hard to think through.
- Use a period at the end of a sentence.
- Use indefinite articles for indefinite entities.
- Definite entities should be named (this is for a variable name, type name, etc..).
- When stating a rule, the subject should be in the singular (e.g. "An external module cannot..." instead of "External modules cannot...").
- Use present tense.
- Use active voice.
For a variety of reasons, we avoid certain constructs, and use some of our own. Among them:
- Do not use
for..in
statements; instead, usets.forEach
,ts.forEachKey
andts.forEachValue
. Be aware of their slightly different semantics. - Try to use
ts.forEach
,ts.map
, andts.filter
instead of loops when it is not strongly inconvenient.
- Use prettier and tslint/eslint.
- Use arrow functions over anonymous function expressions.
- Only surround arrow function parameters when necessary.
For example,(x) => x + x
is wrong but the following are correct:x => x + x
(x,y) => x + y
<T>(x: T, y: T) => x === y
- Always surround loop and conditional bodies with curly braces. Statements on the same line are allowed to omit braces.
- Open curly braces always go on the same line as whatever necessitates them.
- Parenthesized constructs should have no surrounding whitespace.
A single space follows commas, colons, and semicolons in those constructs. For example:for (var i = 0, n = str.length; i < 10; i++) { }
if (x < 10) { }
function f(x: number, y: string): void { }
- Use a single declaration per variable statement
(i.e. usevar x = 1; var y = 2;
overvar x = 1, y = 2;
). - Use 2 spaces per indentation.
[System UI Theme Specification](https://system-ui.com]
The theme object is intended to be a general purpose format for storing design system style values, scales, and/or design tokens. The object itself is not coupled to any particular library's implementation and can be used in places where sharing common style values in multiple parts of a code base is desirable.
Many CSS style properties accept open-ended values like lengths, colors, and font names. In order to create a consistent styling system, the theme object is centered around the idea of scales, such as a typographic (font-size) scale, a spacing scale for margin and padding, and a color object. These scales can be defined in multiple ways depending on needs, but tend to use arrays for ordinal values like font sizes, or plain objects for named values like colors, with the option of nesting objects for more complex systems.
// example fontSizes scale as an array
fontSizes: [
12, 14, 16, 20, 24, 32
]
// example colors object
colors: {
blue: '#07c',
green: '#0fa',
}
// example nested colors object
colors: {
blue: '#07c',
blues: [
'#004170',
'#006fbe',
'#2d8fd5',
'#5aa7de',
]
}
For typically ordinal values like font sizes that are stored in arrays, it can be helpful to create aliases by adding named properties to the object.
// example fontSizes aliases
fontSizes: [
12, 14, 16, 20, 24, 32
]
// aliases
fontSizes.body = fontSizes[2]
fontSizes.display = fontSizes[5]
Some CSS properties accept only a small, finite number of valid CSS values and should not be included as a scale object.
For example, the text-align
property accepts the following values:
left
, right
, center
, justify
, justify-all
, start
, end
, or match-parent
.
Other properties that are intentionally excluded from this specification include: float
, clear
, display
, overflow
, position
, vertical-align
, align-items
, justify-content
, and flex-direction
.
The keys in the theme object should typically correspond with the CSS properties they are used for, and follow a plural naming convention.
For example, the CSS property font-size
is expected to use values from the fontSizes
scale, and the color
property uses values from the colors
scale.
Some keys can be used for multiple CSS properties, where the data type is the same.
The color
object is intended to be used with any property that accepts a CSS color value, such as background-color
or border-color
.
The space
key is a specially-named scale intended for use with margin, padding, and other layout-related CSS properties.
A space scale can be defined as either a plain object or an array, but by convention an array is preferred.
This is an intentional constraint that makes it difficult to add "one-off" or "in-between" sizes that could lead to unwanted and rippling affects to layout.
Note: other names under consideration include spacing, spaces, and lengths.
When defining space scales as an array, it is conventional to use the value 0
as the first value so that space[0] === 0
.
// example space scale
space: [
0, 4, 8, 16, 32, 64
]
// example space scale object
space: {
small: 4,
medium: 8,
large: 16,
}
// example space scale with aliases
space: [
0, 4, 8, 16, 32
]
space.small = space[1]
space.medium = space[2]
space.large = space[3]
Breakpoints are CSS lengths intended for use in media queries. Commonly, the breakpoints scale is used to create mobile-first responsive media queries based on array values.
For convenience and for use with other styling approaches, a mediaQueries
scale derived from the breakpoints
scale can be added to the theme object.
breakpoints: [ '40em', '52em', '64em' ]
mediaQueries: {
small: `@media screen and (min-width: ${breakpoints[0]})`,
medium: `@media screen and (min-width: ${breakpoints[1]})`,
large: `@media screen and (min-width: ${breakpoints[2]})`,
}
The following is a list of theme object keys and their corresponding CSS properties. This list may be non-exhaustive.
Theme Key | CSS Properties |
---|---|
space |
margin , margin-top , margin-right , margin-bottom , margin-left , padding , padding-top , padding-right , padding-bottom , padding-left , grid-gap , grid-column-gap , grid-row-gap |
fontSizes |
font-size |
colors |
color , background-color , border-color |
fonts |
font-family |
fontWeights |
font-weight |
lineHeights |
line-height |
letterSpacings |
letter-spacing |
sizes |
width , height , min-width , max-width , min-height , max-height |
borders |
border , border-top , border-right , border-bottom , border-left |
borderWidths |
border-width |
borderStyles |
border-style |
radii |
border-radius |
shadows |
box-shadow , text-shadow |
zIndices |
z-index |
transitions |
transition |
Prior art includes, but is not limited to the following. Please open an issue or pull request to help ensure this list is inclusive.