diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index e1c5d5d86..4e5c96cd0 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -256,31 +256,27 @@ func NewFuncMap() []template.FuncMap {
 		"DefaultTheme": func() string {
 			return setting.UI.DefaultTheme
 		},
+		// pass key-value pairs to a partial template which receives them as a dict
 		"dict": func(values ...interface{}) (map[string]interface{}, error) {
 			if len(values) == 0 {
 				return nil, errors.New("invalid dict call")
 			}
 
 			dict := make(map[string]interface{})
-
-			for i := 0; i < len(values); i++ {
-				switch key := values[i].(type) {
-				case string:
-					i++
-					if i == len(values) {
-						return nil, errors.New("specify the key for non array values")
-					}
-					dict[key] = values[i]
-				case map[string]interface{}:
-					m := values[i].(map[string]interface{})
-					for i, v := range m {
-						dict[i] = v
-					}
-				default:
-					return nil, errors.New("dict values must be maps")
-				}
+			return util.MergeInto(dict, values...)
+		},
+		/* like dict but merge key-value pairs into the first dict and return it */
+		"mergeinto": func(root map[string]interface{}, values ...interface{}) (map[string]interface{}, error) {
+			if len(values) == 0 {
+				return nil, errors.New("invalid mergeinto call")
 			}
-			return dict, nil
+
+			dict := make(map[string]interface{})
+			for key, value := range root {
+				dict[key] = value
+			}
+
+			return util.MergeInto(dict, values...)
 		},
 		"percentage": func(n int, values ...int) float32 {
 			var sum = 0
diff --git a/modules/util/util.go b/modules/util/util.go
index 6d02b5f52..9de1710ac 100644
--- a/modules/util/util.go
+++ b/modules/util/util.go
@@ -6,6 +6,7 @@ package util
 
 import (
 	"bytes"
+	"errors"
 	"strings"
 )
 
@@ -100,3 +101,26 @@ func NormalizeEOL(input []byte) []byte {
 	}
 	return tmp[:pos]
 }
+
+// MergeInto merges pairs of values into a "dict"
+func MergeInto(dict map[string]interface{}, values ...interface{}) (map[string]interface{}, error) {
+	for i := 0; i < len(values); i++ {
+		switch key := values[i].(type) {
+		case string:
+			i++
+			if i == len(values) {
+				return nil, errors.New("specify the key for non array values")
+			}
+			dict[key] = values[i]
+		case map[string]interface{}:
+			m := values[i].(map[string]interface{})
+			for i, v := range m {
+				dict[i] = v
+			}
+		default:
+			return nil, errors.New("dict values must be maps")
+		}
+	}
+
+	return dict, nil
+}
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl
index 78223ce56..49adcd08b 100644
--- a/templates/repo/issue/list.tmpl
+++ b/templates/repo/issue/list.tmpl
@@ -200,106 +200,7 @@
 				</div>
 			</div>
 		</div>
-
-		<div class="issue list">
-			{{ $approvalCounts := .ApprovalCounts}}
-			{{range .Issues}}
-				<li class="item">
-					{{if $.CanWriteIssuesOrPulls}}
-					<div class="ui checkbox issue-checkbox">
-						<input type="checkbox" data-issue-id={{.ID}}></input>
-					</div>
-					{{end}}
-					<div class="ui {{if .IsClosed}}{{if .IsPull}}{{if .PullRequest.HasMerged}}purple{{else}}red{{end}}{{else}}red{{end}}{{else}}{{if .IsRead}}white{{else}}green{{end}}{{end}} label">#{{.Index}}</div>
-					<a class="title" href="{{$.Link}}/{{.Index}}">{{RenderEmoji .Title}}</a>
-
-					{{if .IsPull }}
-						{{if (index $.CommitStatus .PullRequest.ID)}}
-							{{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}}
-						{{end}}
-					{{end}}
-
-					{{range .Labels}}
-						<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
-					{{end}}
-
-					{{if .NumComments}}
-						<span class="comment ui right">{{svg "octicon-comment"}} {{.NumComments}}</span>
-					{{end}}
-
-					{{if .TotalTrackedTime}}
-						<span class="comment ui right">{{svg "octicon-clock"}} {{.TotalTrackedTime | Sec2Time}}</span>
-					{{end}}
-
-					<p class="desc">
-						{{ $timeStr := TimeSinceUnix .GetLastEventTimestamp $.Lang }}
-						{{if .OriginalAuthor }}
-							{{$.i18n.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor | Safe}}
-						{{else if gt .Poster.ID 0}}
-							{{$.i18n.Tr .GetLastEventLabel $timeStr .Poster.HomeLink (.Poster.GetDisplayName | Escape) | Safe}}
-						{{else}}
-							{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}}
-						{{end}}
-
-						{{if .Milestone}}
-							<a class="milestone" href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}">
-								{{svg "octicon-milestone"}} {{.Milestone.Name}}
-							</a>
-						{{end}}
-						{{if .Ref}}
-							<a class="ref" href="{{index $.IssueRefURLs .ID}}">
-								{{svg "octicon-git-branch"}} {{index $.IssueRefEndNames .ID}}
-							</a>
-						{{end}}
-						{{$tasks := .GetTasks}}
-						{{if gt $tasks 0}}
-							{{$tasksDone := .GetTasksDone}}
-							<span class="checklist">
-								{{svg "octicon-checklist"}} {{$tasksDone}} / {{$tasks}} <span class="progress-bar"><span class="progress" style="width:calc(100% * {{$tasksDone}} / {{$tasks}});"></span></span>
-							</span>
-						{{end}}
-						{{if ne .DeadlineUnix 0}}
-							<span class="due-date poping up" data-content="{{$.i18n.Tr "repo.issues.due_date"}}" data-variation="tiny inverted" data-position="right center">
-								{{svg "octicon-calendar"}}<span{{if .IsOverdue}} class="overdue"{{end}}>{{.DeadlineUnix.FormatShort}}</span>
-							</span>
-						{{end}}
-						{{range .Assignees}}
-							<a class="ui right assignee poping up" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-variation="inverted" data-position="left center">
-								<img class="ui avatar image" src="{{.RelAvatarLink}}">
-							</a>
-						{{end}}
-						{{if .IsPull}}
-							{{$approveOfficial := call $approvalCounts .ID "approve"}}
-							{{$rejectOfficial := call $approvalCounts .ID "reject"}}
-							{{$waitingOfficial := call $approvalCounts .ID "waiting"}}
-							{{if gt $approveOfficial 0}}
-								<span class="approvals">{{svg "octicon-check"}}
-									{{$.i18n.Tr (TrN $.i18n.Lang $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n") $approveOfficial}}
-								</span>
-							{{end}}
-
-							{{if gt $rejectOfficial 0}}
-								<span class="rejects">{{svg "octicon-diff"}}
-									{{$.i18n.Tr (TrN $.i18n.Lang $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n") $rejectOfficial}}
-								</span>
-							{{end}}
-
-							{{if gt $waitingOfficial 0}}
-								<span class="waiting">{{svg "octicon-eye"}}
-									{{$.i18n.Tr (TrN $.i18n.Lang $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n") $waitingOfficial}}
-								</span>
-							{{end}}
-
-							{{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}}
-								<span class="conflicting">{{svg "octicon-x"}} {{$.i18n.Tr (TrN $.i18n.Lang (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n") (len .PullRequest.ConflictedFiles)}}</span>
-							{{end}}
-						{{end}}
-					</p>
-				</li>
-			{{end}}
-
-			{{template "base/paginate" .}}
-		</div>
+		{{template "shared/issuelist" mergeinto . "listType" "repo"}}
 	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index ad75411d8..9958efc07 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -177,127 +177,7 @@
 				</div>
 			</div>
 		</div>
-
-		<div class="issue list">
-			{{ $approvalCounts := .ApprovalCounts}}
-			{{range .Issues}}
-				{{ $timeStr:= TimeSinceUnix .CreatedUnix $.Lang }}
-				<li class="item">
-					{{if or (and $.CanWriteIssues (not .IsPull)) (and $.CanWritePulls .IsPull)}}
-					<div class="ui checkbox issue-checkbox">
-						<input type="checkbox" data-issue-id={{.ID}}></input>
-					</div>
-					{{end}}
-
-					{{if .IsClosed}}
-						{{if .IsPull}}
-							{{if .PullRequest.HasMerged}}
-								<div class="ui purple label">#{{.Index}}</div>
-								<a class="ui purple text">{{svg "octicon-git-pull-request"}}</a>
-							{{else}}
-								<div class="ui red label">#{{.Index}}</div>
-								<a class="ui red text">{{svg "octicon-git-pull-request"}}</a>
-							{{end}}
-						{{else}}
-							<div class="ui red label">#{{.Index}}</div>
-							<a class="ui red text">{{svg "octicon-issue-closed"}}</a>
-						{{end}}
-					{{else}}
-						{{if .IsRead}}
-							<div class="ui white label">#{{.Index}}</div>
-						{{else}}
-							<div class="ui green label">#{{.Index}}</div>
-						{{end}}
-						{{if .IsPull}}
-							<a class="ui green text">{{svg "octicon-git-pull-request"}}</a>
-						{{else}}
-							<a class="ui green text">{{svg "octicon-issue-opened"}}</a>
-						{{end}}
-					{{end}}
-
-					<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji}}</a>
-
-					{{if .IsPull }}
-						{{if (index $.CommitStatus .PullRequest.ID)}}
-							{{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}}
-						{{end}}
-					{{end}}
-
-					{{range .Labels}}
-						<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description}}">{{.Name | RenderEmoji}}</a>
-					{{end}}
-
-					{{if .NumComments}}
-						<span class="comment ui right">{{svg "octicon-comment"}} {{.NumComments}}</span>
-					{{end}}
-
-					{{if .TotalTrackedTime}}
-						<span class="comment ui right">{{svg "octicon-clock"}} {{.TotalTrackedTime | Sec2Time}}</span>
-					{{end}}
-
-					<p class="desc">
-						{{ $timeStr := TimeSinceUnix .GetLastEventTimestamp $.Lang }}
-						{{if .OriginalAuthor }}
-							{{$.i18n.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor | Safe}}
-						{{else if gt .Poster.ID 0}}
-							{{$.i18n.Tr .GetLastEventLabel $timeStr .Poster.HomeLink (.Poster.GetDisplayName | Escape) | Safe}}
-						{{else}}
-							{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}}
-						{{end}}
-
-						{{if .Ref}}
-							<a class="ref" href="{{index $.IssueRefURLs .ID}}">
-								{{svg "octicon-git-branch"}} {{index $.IssueRefEndNames .ID}}
-							</a>
-						{{end}}
-						{{$tasks := .GetTasks}}
-						{{if gt $tasks 0}}
-							{{$tasksDone := .GetTasksDone}}
-							<span class="checklist">
-								{{svg "octicon-checklist"}} {{$tasksDone}} / {{$tasks}} <span class="progress-bar"><span class="progress" style="width:calc(100% * {{$tasksDone}} / {{$tasks}});"></span></span>
-							</span>
-						{{end}}
-						{{if ne .DeadlineUnix 0}}
-							{{svg "octicon-calendar"}}
-							<span{{if .IsOverdue}} class="overdue"{{end}}>{{.DeadlineUnix.FormatShort}}</span>
-						{{end}}
-						{{range .Assignees}}
-							<a class="ui right assignee poping up" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-variation="inverted" data-position="left center">
-								<img class="ui avatar image" src="{{.RelAvatarLink}}">
-							</a>
-						{{end}}
-						{{if .IsPull}}
-							{{$approveOfficial := call $approvalCounts .ID "approve"}}
-							{{$rejectOfficial := call $approvalCounts .ID "reject"}}
-							{{$waitingOfficial := call $approvalCounts .ID "waiting"}}
-							{{if gt $approveOfficial 0}}
-								<span class="approvals">{{svg "octicon-check"}}
-									{{$.i18n.Tr (TrN $.i18n.Lang $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n") $approveOfficial}}
-								</span>
-							{{end}}
-
-							{{if gt $rejectOfficial 0}}
-								<span class="rejects">{{svg "octicon-diff"}}
-									{{$.i18n.Tr (TrN $.i18n.Lang $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n") $rejectOfficial}}
-								</span>
-							{{end}}
-
-							{{if gt $waitingOfficial 0}}
-								<span class="waiting">{{svg "octicon-eye"}}
-									{{$.i18n.Tr (TrN $.i18n.Lang $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n") $waitingOfficial}}
-								</span>
-							{{end}}
-
-							{{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}}
-								<span class="conflicting">{{svg "octicon-x"}} {{$.i18n.Tr (TrN $.i18n.Lang (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n") (len .PullRequest.ConflictedFiles)}}</span>
-							{{end}}
-						{{end}}
-					</p>
-				</li>
-			{{end}}
-
-			{{template "base/paginate" .}}
-		</div>
+		{{template "shared/issuelist" mergeinto . "listType" "milestone"}}
 	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl
new file mode 100644
index 000000000..939358386
--- /dev/null
+++ b/templates/shared/issuelist.tmpl
@@ -0,0 +1,134 @@
+<div class="issue list">
+	{{ $approvalCounts := .ApprovalCounts}}
+	{{range .Issues}}
+		<li class="item df py-3">
+			<div class="issue-item-left df py-1">
+				{{if $.CanWriteIssuesOrPulls}}
+					<div class="ui checkbox issue-checkbox">
+						<input type="checkbox" data-issue-id={{.ID}}></input>
+						<label></label>
+					</div>
+				{{end}}
+				<div class="issue-item-icon">
+					{{if .IsPull}}
+						{{if .PullRequest.HasMerged}}
+							{{svg "octicon-git-merge" 16 "text purple"}}
+						{{else}}
+							{{if .IsClosed}}
+								{{svg "octicon-git-pull-request" 16 "text red"}}
+							{{else}}
+								{{svg "octicon-git-pull-request" 16 "text green"}}
+							{{end}}
+						{{end}}
+					{{else}}
+						{{if .IsClosed}}
+							{{svg "octicon-issue-opened" 16 "text red"}}
+						{{else}}
+							{{svg "octicon-issue-closed" 16 "text green"}}
+						{{end}}
+					{{end}}
+				</div>
+			</div>
+			<div class="issue-item-main f1 fc df">
+				<div class="issue-item-top-row df ac fw">
+					<a class="title mr-3" href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji .Title}}</a>
+					{{if .IsPull }}
+						{{if (index $.CommitStatus .PullRequest.ID)}}
+							{{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}}
+						{{end}}
+					{{end}}
+					{{range .Labels}}
+						<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
+					{{end}}
+				</div>
+				<div class="desc issue-item-bottom-row df ac fw my-1">
+					<a class="index ml-0 mr-2" href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
+						#{{.Index}}
+					</a>
+					{{ $timeStr := TimeSinceUnix .GetLastEventTimestamp $.Lang }}
+					{{if .OriginalAuthor }}
+						{{$.i18n.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor | Safe}}
+					{{else if gt .Poster.ID 0}}
+						{{$.i18n.Tr .GetLastEventLabel $timeStr .Poster.HomeLink (.Poster.GetDisplayName | Escape) | Safe}}
+					{{else}}
+						{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}}
+					{{end}}
+					{{if and .Milestone (ne $.listType "milestone")}}
+						<a class="milestone" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/milestone/{{.Milestone.ID}}"{{end}}>
+							{{svg "octicon-milestone" 14 "mr-2"}}{{.Milestone.Name}}
+						</a>
+					{{end}}
+					{{if .Ref}}
+						<a class="ref" {{if $.RepoLink}}href="{{$.RepoLink}}{{index $.IssueRefURLs .ID}}"{{else}}href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}{{index $.IssueRefURLs .ID}}"{{end}}>
+							{{svg "octicon-git-branch" 14 "mr-2"}}{{index $.IssueRefEndNames .ID}}
+						</a>
+					{{end}}
+					{{$tasks := .GetTasks}}
+					{{if gt $tasks 0}}
+						{{$tasksDone := .GetTasksDone}}
+						<span class="checklist">
+							{{svg "octicon-checklist" 14 "mr-2"}}{{$tasksDone}} / {{$tasks}} <span class="progress-bar"><span class="progress" style="width:calc(100% * {{$tasksDone}} / {{$tasks}});"></span></span>
+						</span>
+					{{end}}
+					{{if ne .DeadlineUnix 0}}
+						<span class="due-date poping up" data-content="{{$.i18n.Tr "repo.issues.due_date"}}" data-variation="tiny inverted" data-position="right center">
+							{{svg "octicon-calendar" 14 "mr-2"}}<span{{if .IsOverdue}} class="overdue"{{end}}>{{.DeadlineUnix.FormatShort}}</span>
+						</span>
+					{{end}}
+					{{if .IsPull}}
+						{{$approveOfficial := call $approvalCounts .ID "approve"}}
+						{{$rejectOfficial := call $approvalCounts .ID "reject"}}
+						{{$waitingOfficial := call $approvalCounts .ID "waiting"}}
+						{{if gt $approveOfficial 0}}
+							<span class="approvals df ac">
+								{{svg "octicon-check" 14 "mr-2"}}
+								{{$.i18n.Tr (TrN $.i18n.Lang $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n") $approveOfficial}}
+							</span>
+						{{end}}
+						{{if gt $rejectOfficial 0}}
+							<span class="rejects df ac">
+								{{svg "octicon-diff" 14 "mr-2"}}
+								{{$.i18n.Tr (TrN $.i18n.Lang $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n") $rejectOfficial}}
+							</span>
+						{{end}}
+						{{if gt $waitingOfficial 0}}
+							<span class="waiting df ac">
+								{{svg "octicon-eye" 14}}
+								{{$.i18n.Tr (TrN $.i18n.Lang $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n") $waitingOfficial}}
+							</span>
+						{{end}}
+						{{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}}
+							<span class="conflicting df ac">
+								{{svg "octicon-x" 14}}
+								{{$.i18n.Tr (TrN $.i18n.Lang (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n") (len .PullRequest.ConflictedFiles)}}
+							</span>
+						{{end}}
+					{{end}}
+				</div>
+			</div>
+			<div class="issue-item-icons-right df p-2">
+				<div class="issue-item-icon-right text grey">
+					{{if .TotalTrackedTime}}
+						{{svg "octicon-clock" 16 "mr-2"}}
+						{{.TotalTrackedTime | Sec2Time}}
+					{{end}}
+				</div>
+				<div class="issue-item-icon-right text grey">
+					{{range .Assignees}}
+						<a class="ui assignee poping up" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-variation="inverted" data-position="left center">
+							<img class="ui avatar image" src="{{.RelAvatarLink}}">
+						</a>
+					{{end}}
+				</div>
+				<div class="issue-item-icon-right text grey">
+					{{if .NumComments}}
+						<a href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
+							{{svg "octicon-comment" 16 "mr-2"}}{{.NumComments}}
+						</a>
+					{{end}}
+				</div>
+			</div>
+		</li>
+	{{end}}
+</div>
+{{template "base/paginate" .}}
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index f035aed77..93ec2ed00 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -97,110 +97,7 @@
 						</div>
 					</div>
 				</div>
-
-				<div class="issue list">
-					{{ $approvalCounts := .ApprovalCounts}}
-					{{range .Issues}}
-
-						{{ $timeStr:= TimeSinceUnix .CreatedUnix $.Lang }}
-						{{if .Repo}}
-						<li class="item">
-							<div class="ui label">{{.Repo.FullName}}#{{.Index}}</div>
-							<a class="title" href="{{.HTMLURL}}">{{RenderEmoji .Title}}</a>
-
-							{{if .IsPull}}
-									{{if (index $.CommitStatus .PullRequest.ID)}}
-											{{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}}
-									{{end}}
-							{{end}}
-
-							{{with .Labels}}
-								{{/* If we have any labels, we should show them
-								with a 2.5 line height, this way they don't look
-								awful and they don't stack on top of each other,
-								especially on mobile views. */}}
-								<span style="line-height: 2.5">
-									{{range .}}
-										<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
-									{{end}}
-								</span>
-							{{end}}
-
-							{{if .NumComments}}
-								<span class="comment ui right">{{svg "octicon-comment"}} {{.NumComments}}</span>
-							{{end}}
-							{{if .TotalTrackedTime}}
-								<span class="comment ui right">{{svg "octicon-clock"}} {{.TotalTrackedTime | Sec2Time}}</span>
-							{{end}}
-
-							<p class="desc">
-								{{if .OriginalAuthor}}
-									{{$.i18n.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor | Safe}}
-								{{else if gt .Poster.ID 0}}
-									{{$.i18n.Tr .GetLastEventLabel $timeStr .Poster.HomeLink (.Poster.GetDisplayName|Escape) | Safe}}
-								{{else}}
-									{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName|Escape) | Safe}}
-								{{end}}
-								{{if .Milestone}}
-									<a class="milestone" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/milestone/{{.Milestone.ID}}">
-										{{svg "octicon-milestone"}} {{.Milestone.Name}}
-									</a>
-								{{end}}
-								{{if .Ref}}
-									<a class="ref" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}{{index $.IssueRefURLs .ID}}">
-										{{svg "octicon-git-branch"}} {{index $.IssueRefEndNames .ID}}
-									</a>
-								{{end}}
-								{{range .Assignees}}
-									<a class="ui right assignee poping up" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-variation="inverted" data-position="left center">
-										<img class="ui avatar image" src="{{.RelAvatarLink}}">
-									</a>
-								{{end}}
-								{{$tasks := .GetTasks}}
-								{{if gt $tasks 0}}
-									{{$tasksDone := .GetTasksDone}}
-									<span class="checklist">
-										{{svg "octicon-checklist"}} {{$tasksDone}} / {{$tasks}} <span class="progress-bar"><span class="progress" style="width:calc(100% * {{$tasksDone}} / {{$tasks}});"></span></span>
-									</span>
-								{{end}}
-								{{if ne .DeadlineUnix 0}}
-									<span class="due-date poping up" data-content="{{$.i18n.Tr "repo.issues.due_date"}}" data-variation="tiny inverted" data-position="right center">
-										{{svg "octicon-calendar"}}<span{{if .IsOverdue}} class="overdue"{{end}}>{{.DeadlineUnix.FormatShort}}</span>
-									</span>
-								{{end}}
-								{{if .IsPull}}
-									{{$approveOfficial := call $approvalCounts .ID "approve"}}
-									{{$rejectOfficial := call $approvalCounts .ID "reject"}}
-									{{$waitingOfficial := call $approvalCounts .ID "waiting"}}
-									{{if gt $approveOfficial 0}}
-										<span class="approvals">{{svg "octicon-check"}}
-											{{$.i18n.Tr (TrN $.i18n.Lang $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n") $approveOfficial}}
-										</span>
-									{{end}}
-
-									{{if gt $rejectOfficial 0}}
-										<span class="rejects">{{svg "octicon-diff"}}
-											{{$.i18n.Tr (TrN $.i18n.Lang $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n") $rejectOfficial}}
-										</span>
-									{{end}}
-
-									{{if gt $waitingOfficial 0}}
-										<span class="waiting">{{svg "octicon-eye"}}
-											{{$.i18n.Tr (TrN $.i18n.Lang $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n") $waitingOfficial}}
-										</span>
-									{{end}}
-
-									{{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}}
-										<span class="conflicting">{{svg "octicon-x"}} {{$.i18n.Tr (TrN $.i18n.Lang (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n") (len .PullRequest.ConflictedFiles)}}</span>
-									{{end}}
-								{{end}}
-							</p>
-						</li>
-						{{end}}
-					{{end}}
-
-					{{template "base/paginate" .}}
-				</div>
+				{{template "shared/issuelist" mergeinto . "listType" "dashboard"}}
 			</div>
 		</div>
 	</div>
diff --git a/web_src/less/_base.less b/web_src/less/_base.less
index 406247179..9af770e40 100644
--- a/web_src/less/_base.less
+++ b/web_src/less/_base.less
@@ -76,6 +76,7 @@
   /* target-based colors */
   --color-body: #ffffff;
   --color-text: #212121;
+  --color-text-light: #444444;
   --color-box-header: #f7f7f7;
   --color-box-body: #ffffff;
   --color-timeline: #ececec;
@@ -141,6 +142,15 @@ strong {
   font-weight: 500;
 }
 
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-weight: 600;
+}
+
 body {
   background-color: var(--color-body);
   overflow-y: auto;
@@ -448,7 +458,7 @@ a:hover,
       color: var(--color-red) !important;
 
       a {
-        color: var(--color-red) !important;
+        color: inherit !important;
 
         &:hover {
           color: #e67777 !important;
@@ -457,19 +467,19 @@ a:hover,
     }
 
     &.blue {
-      color: var(--color-primary-dark-1) !important;
+      color: var(--color-primary) !important;
 
       a {
-        color: var(--color-primary) !important;
+        color: inherit !important;
 
         &:hover {
-          color: var(--color-primary-dark-2) !important;
+          color: var(--color-primary-dark-1) !important;
         }
       }
     }
 
     &.black {
-      color: #444444;
+      color: var(--color-body);
 
       &:hover {
         color: #000000;
@@ -477,13 +487,13 @@ a:hover,
     }
 
     &.grey {
-      color: var(--color-grey) !important;
+      color: var(--color-text-light) !important;
 
       a {
-        color: #444444 !important;
+        color: inherit !important;
 
         &:hover {
-          color: #000000 !important;
+          color: var(--color-primary) !important;
         }
       }
     }
diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less
index dc0c718c1..1dbd741ee 100644
--- a/web_src/less/_repository.less
+++ b/web_src/less/_repository.less
@@ -2586,111 +2586,10 @@
   display: none;
 }
 
-.ui.checkbox.issue-checkbox {
-  vertical-align: middle;
-}
-
 .ui.menu .item > img:not(.ui) {
   width: auto;
 }
 
-.issue.list {
-  list-style: none;
-
-  > .item {
-    padding-top: 15px;
-    padding-bottom: 10px;
-    border-bottom: 1px dashed #aaaaaa;
-
-    .title {
-      color: #444444;
-      font-size: 15px;
-      font-weight: 500;
-      margin: 0 6px;
-
-      &:hover {
-        color: #000000;
-      }
-    }
-
-    .comment {
-      padding-right: 10px;
-      color: #666666;
-    }
-
-    .desc {
-      padding-top: 5px;
-      color: #999999;
-
-      .waiting,
-      .approvals,
-      .rejects {
-        padding-left: 5px;
-      }
-
-      .checklist {
-        padding-left: 5px;
-
-        .progress-bar {
-          margin-left: 2px;
-          width: 80px;
-          height: 6px;
-          display: inline-block;
-          background-color: #eeeeee;
-          overflow: hidden;
-          border-radius: 3px;
-          vertical-align: 2px !important;
-
-          .progress {
-            background-color: #cccccc;
-            display: block;
-            height: 100%;
-          }
-        }
-      }
-
-      .conflicting {
-        padding-left: 5px;
-      }
-
-      .due-date {
-        padding-left: 5px;
-      }
-
-      a.milestone {
-        margin-left: 5px;
-        color: #999999 !important;
-
-        &:hover {
-          color: #000000 !important;
-        }
-      }
-
-      a.ref {
-        margin-left: 8px;
-        color: #999999 !important;
-
-        &:hover {
-          color: #000000 !important;
-        }
-
-        span {
-          margin-right: -4px;
-        }
-      }
-
-      .assignee {
-        margin-top: -5px;
-        margin-right: 5px;
-      }
-
-      .overdue {
-        color: var(--color-red);
-      }
-    }
-  }
-}
-
 .page.buttons {
   padding-top: 15px;
 }
@@ -2975,15 +2874,31 @@
   flex-wrap: wrap;
 }
 
-.labels.list .item {
+.labels.list .item,
+.timeline-item .label {
   padding: .3em .5em !important;
   margin-left: 0;
   margin-right: 0;
   margin-bottom: 3px;
 }
 
-.labels.list .item + .item {
-  margin-left: 3px;
+.issue-item-top-row .label {
+  margin-left: 0;
+  margin-right: 0;
+  margin-top: 1.5px;
+  margin-bottom: 1.5px;
+}
+
+.labels.list .item,
+.timeline-item .label,
+.issue-item-top-row .label {
+  margin-right: 3px;
+  display: inline !important;
+}
+
+.timeline-item .label:last-of-type,
+.issue-item-top-row .label:last-of-type {
+  margin-right: 0;
 }
 
 tbody.commit-list {
diff --git a/web_src/less/helpers.less b/web_src/less/helpers.less
index d50aea026..60d50883c 100644
--- a/web_src/less/helpers.less
+++ b/web_src/less/helpers.less
@@ -6,6 +6,7 @@
 .sb { justify-content: space-between !important; }
 .fc { flex-direction: column !important; }
 .f1 { flex: 1 !important; }
+.fw { flex-wrap: wrap !important; }
 
 .mono {
   font-family: var(--fonts-monospace) !important;
diff --git a/web_src/less/index.less b/web_src/less/index.less
index 1b0701ae8..0d5ef8872 100644
--- a/web_src/less/index.less
+++ b/web_src/less/index.less
@@ -1,6 +1,7 @@
 @import "~font-awesome/css/font-awesome.css";
 
 @import "./variables.less";
+@import "./shared/issuelist.less";
 @import "./features/gitgraph.less";
 @import "./features/animations.less";
 @import "./features/heatmap.less";
diff --git a/web_src/less/shared/issuelist.less b/web_src/less/shared/issuelist.less
new file mode 100644
index 000000000..dec99e135
--- /dev/null
+++ b/web_src/less/shared/issuelist.less
@@ -0,0 +1,127 @@
+.issue.list {
+  list-style: none;
+  margin-top: 1rem;
+
+  a:not(.label):hover {
+    color: var(--color-primary) !important;
+  }
+
+  > .item {
+    .issue-checkbox {
+      margin-top: 1px;
+    }
+
+    .issue-item-icon svg {
+      margin-right: .75rem;
+    }
+
+    .issue-item-icons-right > * + * {
+      margin-left: .5rem;
+    }
+
+    .issue-item-main {
+      width: 100%;
+    }
+
+    .issue-item-top-row {
+      max-width: 100%;
+      color: var(--color-text);
+      font-size: 16px;
+      min-width: 0;
+      font-weight: 600;
+    }
+
+    .issue-item-bottom-row {
+      font-size: 13px;
+    }
+
+    .title {
+      color: var(--color-text);
+      word-break: break-word;
+    }
+
+    .issue-item-icon-right {
+      min-width: 2rem;
+    }
+
+    .assignee {
+      position: relative;
+      top: -2px;
+    }
+
+    .assignee img {
+      width: 20px;
+      height: 20px;
+      margin-right: 2px;
+    }
+
+    .desc {
+      color: #999999;
+
+      a {
+        color: inherit;
+      }
+
+      .time-since,
+      a {
+        margin-left: .25rem;
+        margin-right: .25rem;
+      }
+
+      .waiting,
+      .approvals,
+      .rejects {
+        padding-left: 5px;
+      }
+
+      .checklist {
+        padding-left: 5px;
+
+        .progress-bar {
+          margin-left: 2px;
+          width: 80px;
+          height: 6px;
+          display: inline-block;
+          background-color: #eeeeee;
+          overflow: hidden;
+          border-radius: 3px;
+          vertical-align: 2px !important;
+
+          .progress {
+            background-color: #cccccc;
+            display: block;
+            height: 100%;
+          }
+        }
+      }
+
+      .conflicting {
+        padding-left: 5px;
+      }
+
+      .due-date {
+        padding-left: 5px;
+      }
+
+      a.milestone {
+        margin-left: 5px;
+      }
+
+      a.ref {
+        margin-left: 8px;
+
+        span {
+          margin-right: -4px;
+        }
+      }
+
+      .overdue {
+        color: var(--color-red);
+      }
+    }
+  }
+
+  > .item + .item {
+    border-top: 1px solid var(--color-secondary);
+  }
+}
diff --git a/web_src/less/themes/theme-arc-green.less b/web_src/less/themes/theme-arc-green.less
index 57b00efe3..8c36944ec 100644
--- a/web_src/less/themes/theme-arc-green.less
+++ b/web_src/less/themes/theme-arc-green.less
@@ -73,6 +73,7 @@
   --color-box-header: #454a57;
   --color-box-body: #353945;
   --color-text: #b6bac5;
+  --color-text-light: #969aa5;
   --color-timeline: #4a505c;
   --color-input-text: #dcdcdc;
   --color-input-background: #2e323e;
@@ -680,18 +681,6 @@ a.ui.basic.label:hover {
   background-color: #383c4a;
 }
 
-.issue.list > .item .title {
-  color: #87ab63;
-}
-
-.issue.list > .item .title:hover {
-  color: #a0cc75;
-}
-
-.issue.list > .item {
-  border-bottom: 1px dashed #475767;
-}
-
 .ui.green.label,
 .ui.green.labels .label,
 .ui.basic.green.label {
@@ -708,10 +697,6 @@ a.ui.basic.green.label:hover {
   color: #fff !important;
 }
 
-.issue.list > .item .comment {
-  color: var(--color-secondary-dark-6);
-}
-
 .ui.basic.red.active.button,
 .ui.basic.red.buttons .active.button {
   box-shadow: 0 0 0 1px #b75252 inset !important;
@@ -1019,10 +1004,6 @@ a.ui.basic.green.label:hover {
   text-shadow: -2px 0 #383c4a, 0 2px #383c4a, 2px 0 #383c4a, 0 -2px #383c4a;
 }
 
-.ui .text.grey a {
-  color: #dbdbdb !important;
-}
-
 .repository.view.issue .comment-list .comment .content .header {
   color: #dbdbdb;
   background-color: var(--color-secondary);
@@ -1045,10 +1026,6 @@ a.ui.basic.green.label:hover {
   background: #353945;
 }
 
-.ui .text.grey a:hover {
-  color: #dbdbdb !important;
-}
-
 .ui.basic.green.active.button,
 .ui.basic.green.buttons .active.button {
   color: #87ab63 !important;