summaryrefslogtreecommitdiffstats
path: root/docs/content/doc/developers/guidelines-frontend.md
blob: 9fec5bd17e485b88c60f4ff7d99e17750d9fdb6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
---
date: "2021-10-13T16:00:00+02:00"
title: "Guidelines for Frontend Development"
slug: "guidelines-frontend"
weight: 20
toc: false
draft: false
menu:
  sidebar:
    parent: "developers"
    name: "Guidelines for Frontend"
    weight: 20
    identifier: "guidelines-frontend"
---

# Guidelines for Frontend Development

**Table of Contents**

{{< toc >}}

## Background

Gitea uses [Less CSS](https://lesscss.org), [Fomantic-UI](https://fomantic-ui.com/introduction/getting-started.html) (based on [jQuery](https://api.jquery.com)) and [Vue2](https://vuejs.org/v2/guide/) for its frontend.

The HTML pages are rendered by [Go HTML Template](https://pkg.go.dev/html/template)

## General Guidelines

We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html) and [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)

### Gitea specific guidelines:

1. Every feature (Fomantic-UI/jQuery module) should be put in separate files/directories.
2. HTML ids and classes should use kebab-case.
3. HTML ids and classes used in JavaScript should be unique for the whole project, and should contain 2-3 feature related keywords. We recommend to use the `js-` prefix for classes that are only used in JavaScript.
4. jQuery events across different features should use their own namespaces.
5. CSS styling for classes provided by frameworks should not be overwritten. Always use new class-names to overwrite framework styles. We recommend to use the `us-` prefix for user defined styles.  
6. The backend can pass complex data to the frontend by using `ctx.PageData["myModuleData"] = map[]{}`
7. Simple pages and SEO-related pages use Go HTML Template render to generate static Fomantic-UI HTML output. Complex pages can use Vue2 (or Vue3 in future).


### Framework Usage

Mixing different frameworks together is highly discouraged. A JavaScript module should follow one major framework and follow the framework's best practice.

Recommended implementations:
* Vue + Native
* Fomantic-UI (jQuery)
* Native only

Discouraged implementations:
* Vue + jQuery
* jQuery + Native

### `async` Functions

Only mark a function as `async` if and only if there are `await` calls 
or `Promise` returns inside the function.

It's not recommended to use `async` event listeners, which may lead to problems. 
The reason is that the code after await is executed outside the event dispatch. 
Reference: https://github.com/github/eslint-plugin-github/blob/main/docs/rules/async-preventdefault.md

If we want to call an `async` function in a non-async context,
it's recommended to use `const _promise = asyncFoo()` to tell readers
that this is done by purpose, we want to call the async function and ignore the Promise.
Some lint rules and IDEs also have warnings if the returned Promise is not handled.

#### DOM Event Listener

```js
el.addEventListener('click', (e) => {
  (async () => {
    await asyncFoo(); // recommended
    // then we shound't do e.preventDefault() after await, no effect
  })(); 
  
  const _promise = asyncFoo(); // recommended

  e.preventDefault(); // correct
});

el.addEventListener('async', async (e) => { // not recommended but acceptable
  e.preventDefault(); // acceptable
  await asyncFoo();   // skip out event dispatch
  e.preventDefault(); // WRONG
});
```

#### jQuery Event Listener

```js
$('#el').on('click', (e) => {
  (async () => {
    await asyncFoo(); // recommended
    // then we shound't do e.preventDefault() after await, no effect
  })();

  const _promise = asyncFoo(); // recommended

  e.preventDefault();  // correct
  return false;        // correct
});

$('#el').on('click', async (e) => {  // not recommended but acceptable
  e.preventDefault();  // acceptable
  return false;        // WRONG, jQuery expects the returned value is a boolean, not a Promise
  await asyncFoo();    // skip out event dispatch
  return false;        // WRONG
});
```

### HTML Attributes and `dataset`

We forbid `dataset` usage, its camel-casing behaviour makes it hard to grep for attributes. However there are still some special cases, so the current guideline is:

* For legacy code:
  * `$.data()` should be refactored to `$.attr()`.
  * `$.data()` can be used to bind some non-string data to elements in rare cases, but it is highly discouraged.

* For new code:
  * `node.dataset` should not be used, use `node.getAttribute` instead. 
  * never bind any user data to a DOM node, use a suitable design pattern to describe the relation between node and data.


### Vue2/Vue3 and JSX

Gitea is using Vue2 now, we plan to upgrade to Vue3. We decided not to introduce JSX to keep the HTML and the JavaScript code separated.