The name of a variable, function, or class, should answer all the big questions. It should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.
Use meaningful variable names.
Distinguish names in such a way that the reader knows what the differences offer.
❌ Bad:
function isBetween(a1: number, a2: number, a3: number): boolean {
return a2 <= a1 && a1 <= a3;
}
✅ Good:
function isBetween(value: number, left: number, right: number): boolean {
return left <= value && value <= right;
}
Use pronounceable variable names
If you can't pronounce it, you can't discuss it without sounding weird.
❌ Bad:
class Subs {
public ccId: number;
public billingAddrId: number;
public shippingAddrId: number;
}
✅ Good:
class Subscription {
public creditCardId: number;
public billingAddressId: number;
public shippingAddressId: number;
}
Avoid mental mapping
Explicit is better than implicit.
Clarity is king.
❌ Bad:
const u = getUser();
const s = getSubscription();
const t = charge(u, s);
✅ Good:
const user = getUser();
const subscription = getSubscription();
const transaction = charge(user, subscription);
Don't add unneeded context
If your class/type/object name tells you something, don't repeat that in your variable name.
❌ Bad:
type Car = {
carMake: string;
carModel: string;
carColor: string;
};
function print(car: Car): void {
console.log(`${car.carMake} ${car.carModel} (${car.carColor})`);
}
✅ Good:
type Car = {
make: string;
model: string;
color: string;
};
function print(car: Car): void {
console.log(`${car.make} ${car.model} (${car.color})`);
}
- Use camelCase for variable and function names
❌ Bad:
var FooVar;
function BarFunc() {}
✅ Good:
var fooVar;
function barFunc() {}
- Use camelCase of class members, interface members, methods and methods parameters
❌ Bad:
class Foo {
Bar: number;
Baz() {}
}
✅ Good:
class Foo {
bar: number;
baz() {}
}
- Use PascalCase for class names and interface names.
❌ Bad:
class foo {}
✅ Good:
class Foo {}
- Use PascalCase for enums and camelCase for enum members
❌ Bad:
enum notificationTypes {
Default = 0,
Info = 1,
Success = 2,
Error = 3,
Warning = 4
}
✅ Good:
enum NotificationTypes {
default = 0,
info = 1,
success = 2,
error = 3,
warning = 4
}
- Don't use negative names for boolean variables.
❌ Bad:
const isNotEnabled = true;
✅ Good:
const isEnabled = false;
- Keep it simple. If the action can be understood with the least amount of words. If the method has many action and needs to be described in multiple words, then you should refactor the method to have a single responsibility
❌ Bad:
handleCloseClick() {}
✅ Good:
close();
- If your class/type/object name tells you something, don't repeat that in your variable name.
❌ Bad:
class Alert extends LitElement {
closeAlert() {}
}
✅ Good:
class Alert extends LitElement {
close() {}
}
- Events should be named with an action word , prefix with
sgds-
❌ Bad: hide
, show
✅ Good: sgds-hide, sgds-show, sgds-after-show, sgds-toggle
-
Before creating a new event name, check if there are existing names for the same purposed.
-
Use the
emit
method of SgdsElement class to emit a custom event
- Follow the naming convention from Bootstrap 5.1
--componentname-subcomponentname-element-state-properties
.
Example with kebab case for properties with 2 or more words
--sidenav-item-btn-border-left-width;
Example with element state
--sidenav-item-btn-hover-color;
--sidenav-item-btn-active-bg;
- use of shortform for long words
- background --> bg
- button --> btn
All variables, properties and functions should be typed. The Tscompiler can infer the type of properties when a default value is assigned but not when none is defined. If a variable does not need to have a default variable, assign a type to it.
❌ Bad:
@property({ type: Boolean, reflect: true }) isLight;
@property({ type: Boolean, reflect: true }) roundedPill;
✅ Good:
@property({ type: Boolean, reflect: true }) isLight: boolean;
@property({ type: Boolean, reflect: true }) roundedPill = false;
jsdoc annotations is used to generate the custom-element.json, the metadata of sgds-web-components. The metadata is then used in generating the react package and storybook documentations
- All main component should have a @summary description of the component that will be rendered in the storybook docs. Annotate them above the component code
/**
* @summary A dropdown mechanism that allow users to either show or hide related content.
*/
export class SgdsAccordion extends SgdsElement {}
- Include
@slots
,@event
,@csspart
,@cssproperty
and their respective name whenever present in the component.
/**
* @slot default - content of the accordion item
*
* @event sgds-show - Emitted on show.
* @event sgds-after-show - Emitted on show after animation has completed.
* @event sgds-hide - Emitted on hide.
* @event sgds-after-hide - Emitted on hide after animation has completed.
*
* @csspart base - The accordion-item base wrapper
* @csspart header - The accordion-item button header
* @csspart content - The accordion-item content
*
* @cssproperty --mainnav-item-theme-color - Hover and active color for mainnav items.
* @cssproperty --mainnav-item-color - Text color of nav item.
* @cssproperty --mainnav-item-borderBottom-width - border bottom width for hover and active state for nav item
*/
export class SgdsAccordionItem extends SgdsElement {
- Annotate Lit's property decorator, @property, with jsdocs comments. Custom-element Analyzer is able to parse Lit's @property decorator as params.
/** Controls whether accordion-item is open or close */
@property({ type: Boolean, reflect: true }) open = false;
- Annotate all @query with @internal tag
/** @internal */
@query(".accordion-body") body: HTMLElement;
- Annotate public methods with jsdocs comments. Any methods that is meant expose for users should be marked with the "public" access modifier
/** Shows the accordion. */
public async show() {
}
- Avoid sgds specific querying of children components to allow reusability and implementation of our Lit components by other users. Example
❌ Bad:
const children = this.querySelectorAll("sgds-sidenav-item");
✅ Good:
const children = this.shadowRoot.querySelector('slot').assignedElements({flatten: true});
// or use Lit decorator queryAssignedElements
@queryAssignedElements()
- Boolean properties should always be
false
by default. See reason
Use scoped elements mixin when the component has a dependency on another component. Example: In FileUpload case, SgdsButton is used within it.
import { SgdsButton } from "../Button";
import { ScopedElementsMixin } from "@open-wc/scoped-elements";
export class SgdsFileUpload extends ScopedElementsMixin(SgdsElement) {
static get scopedElements() {
return {
"sgds-button": SgdsButton
};
}
}