diff --git a/src/client/app/desktop/views/components/mentions.vue b/src/client/app/desktop/views/components/mentions.vue
index fc3a7af75d..53d08a0eca 100644
--- a/src/client/app/desktop/views/components/mentions.vue
+++ b/src/client/app/desktop/views/components/mentions.vue
@@ -1,8 +1,8 @@
 <template>
 <div class="mk-mentions">
 	<header>
-		<span :data-is-active="mode == 'all'" @click="mode = 'all'">すべて</span>
-		<span :data-is-active="mode == 'following'" @click="mode = 'following'">フォロー中</span>
+		<span :data-active="mode == 'all'" @click="mode = 'all'">すべて</span>
+		<span :data-active="mode == 'following'" @click="mode = 'following'">フォロー中</span>
 	</header>
 	<div class="fetching" v-if="fetching">
 		<mk-ellipsis-icon/>
@@ -98,7 +98,7 @@ export default Vue.extend({
 			font-size 18px
 			color #555
 
-			&:not([data-is-active])
+			&:not([data-active])
 				color $theme-color
 				cursor pointer
 
diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index 01e1f5c2f0..fa7a782b7b 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -31,6 +31,7 @@
 <script lang="ts">
 import Vue from 'vue';
 import { url } from '../../../config';
+import getNoteSummary from '../../../../../renderers/get-note-summary';
 
 import XNote from './notes.note.vue';
 
@@ -53,6 +54,7 @@ export default Vue.extend({
 			requestInitPromise: null as () => Promise<any[]>,
 			notes: [],
 			queue: [],
+			unreadCount: 0,
 			fetching: true,
 			moreFetching: false
 		};
@@ -71,10 +73,12 @@ export default Vue.extend({
 	},
 
 	mounted() {
+		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 		window.addEventListener('scroll', this.onScroll);
 	},
 
 	beforeDestroy() {
+		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 		window.removeEventListener('scroll', this.onScroll);
 	},
 
@@ -130,6 +134,12 @@ export default Vue.extend({
 			}
 			//#endregion
 
+			// 投稿が自分のものではないかつ、タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
+			if ((document.hidden || !this.isScrollTop()) && note.userId !== (this as any).os.i.id) {
+				this.unreadCount++;
+				document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
+			}
+
 			if (this.isScrollTop()) {
 				// Prepend the note
 				this.notes.unshift(note);
@@ -172,9 +182,21 @@ export default Vue.extend({
 			this.moreFetching = false;
 		},
 
+		clearNotification() {
+			this.unreadCount = 0;
+			document.title = 'Misskey';
+		},
+
+		onVisibilitychange() {
+			if (!document.hidden) {
+				this.clearNotification();
+			}
+		},
+
 		onScroll() {
 			if (this.isScrollTop()) {
 				this.releaseQueue();
+				this.clearNotification();
 			}
 
 			if ((this as any).os.i.clientSettings.fetchOnScroll !== false) {
diff --git a/src/client/app/desktop/views/components/timeline.vue b/src/client/app/desktop/views/components/timeline.vue
index d50b41b846..f5f13cbd56 100644
--- a/src/client/app/desktop/views/components/timeline.vue
+++ b/src/client/app/desktop/views/components/timeline.vue
@@ -1,10 +1,10 @@
 <template>
 <div class="mk-timeline">
 	<header>
-		<span :data-is-active="src == 'home'" @click="src = 'home'">%fa:home% ホーム</span>
-		<span :data-is-active="src == 'local'" @click="src = 'local'">%fa:R comments% ローカル</span>
-		<span :data-is-active="src == 'global'" @click="src = 'global'">%fa:globe% グローバル</span>
-		<span :data-is-active="src == 'list'" @click="src = 'list'" v-if="list">%fa:list% {{ list.title }}</span>
+		<span :data-active="src == 'home'" @click="src = 'home'">%fa:home% ホーム</span>
+		<span :data-active="src == 'local'" @click="src = 'local'">%fa:R comments% ローカル</span>
+		<span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% グローバル</span>
+		<span :data-active="src == 'list'" @click="src = 'list'" v-if="list">%fa:list% {{ list.title }}</span>
 		<button @click="chooseList" title="リスト">%fa:list%</button>
 	</header>
 	<x-core v-if="src == 'home'" ref="tl" key="home" src="home"/>
@@ -93,7 +93,7 @@ root(isDark)
 			font-size 12px
 			user-select none
 
-			&[data-is-active]
+			&[data-active]
 				color $theme-color
 				cursor default
 				font-weight bold
@@ -108,7 +108,7 @@ root(isDark)
 					height 2px
 					background $theme-color
 
-			&:not([data-is-active])
+			&:not([data-active])
 				color isDark ? #9aa2a7 : #6f7477
 				cursor pointer
 
diff --git a/src/client/app/desktop/views/components/user-list-timeline.vue b/src/client/app/desktop/views/components/user-list-timeline.vue
index 8a1814f99c..ee983a969c 100644
--- a/src/client/app/desktop/views/components/user-list-timeline.vue
+++ b/src/client/app/desktop/views/components/user-list-timeline.vue
@@ -87,7 +87,7 @@ export default Vue.extend({
 		},
 		onUserRemoved() {
 			this.fetch();
-		},
+		}
 	}
 });
 </script>
diff --git a/src/client/app/desktop/views/components/users-list.vue b/src/client/app/desktop/views/components/users-list.vue
index a08e76f573..e8f4c94d42 100644
--- a/src/client/app/desktop/views/components/users-list.vue
+++ b/src/client/app/desktop/views/components/users-list.vue
@@ -2,8 +2,8 @@
 <div class="mk-users-list">
 	<nav>
 		<div>
-			<span :data-is-active="mode == 'all'" @click="mode = 'all'">すべて<span>{{ count }}</span></span>
-			<span v-if="os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">知り合い<span>{{ youKnowCount }}</span></span>
+			<span :data-active="mode == 'all'" @click="mode = 'all'">すべて<span>{{ count }}</span></span>
+			<span v-if="os.isSignedIn && youKnowCount" :data-active="mode == 'iknow'" @click="mode = 'iknow'">知り合い<span>{{ youKnowCount }}</span></span>
 		</div>
 	</nav>
 	<div class="users" v-if="!fetching && users.length != 0">
@@ -98,7 +98,7 @@ export default Vue.extend({
 				*
 					pointer-events none
 
-				&[data-is-active]
+				&[data-active]
 					font-weight bold
 					color $theme-color
 					border-color $theme-color
diff --git a/src/client/app/desktop/views/pages/user/user.timeline.vue b/src/client/app/desktop/views/pages/user/user.timeline.vue
index 754be8c04f..9c9840c190 100644
--- a/src/client/app/desktop/views/pages/user/user.timeline.vue
+++ b/src/client/app/desktop/views/pages/user/user.timeline.vue
@@ -1,9 +1,9 @@
 <template>
 <div class="timeline">
 	<header>
-		<span :data-is-active="mode == 'default'" @click="mode = 'default'">投稿</span>
-		<span :data-is-active="mode == 'with-replies'" @click="mode = 'with-replies'">投稿と返信</span>
-		<span :data-is-active="mode == 'with-media'" @click="mode = 'with-media'">メディア</span>
+		<span :data-active="mode == 'default'" @click="mode = 'default'">投稿</span>
+		<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">投稿と返信</span>
+		<span :data-active="mode == 'with-media'" @click="mode = 'with-media'">メディア</span>
 	</header>
 	<div class="loading" v-if="fetching">
 		<mk-ellipsis-icon/>
@@ -114,7 +114,7 @@ export default Vue.extend({
 			font-size 18px
 			color #555
 
-			&:not([data-is-active])
+			&:not([data-active])
 				color $theme-color
 				cursor pointer
 
diff --git a/src/client/app/mobile/views/components/index.ts b/src/client/app/mobile/views/components/index.ts
index 9a0a52d106..5ed8427b05 100644
--- a/src/client/app/mobile/views/components/index.ts
+++ b/src/client/app/mobile/views/components/index.ts
@@ -19,6 +19,7 @@ import notificationPreview from './notification-preview.vue';
 import usersList from './users-list.vue';
 import userPreview from './user-preview.vue';
 import userTimeline from './user-timeline.vue';
+import userListTimeline from './user-list-timeline.vue';
 import activity from './activity.vue';
 import widgetContainer from './widget-container.vue';
 
@@ -41,5 +42,6 @@ Vue.component('mk-notification-preview', notificationPreview);
 Vue.component('mk-users-list', usersList);
 Vue.component('mk-user-preview', userPreview);
 Vue.component('mk-user-timeline', userTimeline);
+Vue.component('mk-user-list-timeline', userListTimeline);
 Vue.component('mk-activity', activity);
 Vue.component('mk-widget-container', widgetContainer);
diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue
index 137e15c6de..703b51d678 100644
--- a/src/client/app/mobile/views/components/notes.vue
+++ b/src/client/app/mobile/views/components/notes.vue
@@ -36,6 +36,7 @@
 
 <script lang="ts">
 import Vue from 'vue';
+import getNoteSummary from '../../../../../renderers/get-note-summary';
 
 const displayLimit = 30;
 
@@ -52,6 +53,7 @@ export default Vue.extend({
 			requestInitPromise: null as () => Promise<any[]>,
 			notes: [],
 			queue: [],
+			unreadCount: 0,
 			fetching: true,
 			moreFetching: false
 		};
@@ -70,10 +72,12 @@ export default Vue.extend({
 	},
 
 	mounted() {
+		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 		window.addEventListener('scroll', this.onScroll);
 	},
 
 	beforeDestroy() {
+		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 		window.removeEventListener('scroll', this.onScroll);
 	},
 
@@ -125,6 +129,12 @@ export default Vue.extend({
 			}
 			//#endregion
 
+			// 投稿が自分のものではないかつ、タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
+			if ((document.hidden || !this.isScrollTop()) && note.userId !== (this as any).os.i.id) {
+				this.unreadCount++;
+				document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
+			}
+
 			if (this.isScrollTop()) {
 				// Prepend the note
 				this.notes.unshift(note);
@@ -160,9 +170,21 @@ export default Vue.extend({
 			this.moreFetching = false;
 		},
 
+		clearNotification() {
+			this.unreadCount = 0;
+			document.title = 'Misskey';
+		},
+
+		onVisibilitychange() {
+			if (!document.hidden) {
+				this.clearNotification();
+			}
+		},
+
 		onScroll() {
 			if (this.isScrollTop()) {
 				this.releaseQueue();
+				this.clearNotification();
 			}
 
 			if ((this as any).os.i.clientSettings.fetchOnScroll !== false) {
diff --git a/src/client/app/mobile/views/components/user-list-timeline.vue b/src/client/app/mobile/views/components/user-list-timeline.vue
new file mode 100644
index 0000000000..ee983a969c
--- /dev/null
+++ b/src/client/app/mobile/views/components/user-list-timeline.vue
@@ -0,0 +1,93 @@
+<template>
+<div>
+	<mk-notes ref="timeline" :more="existMore ? more : null"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { UserListStream } from '../../../common/scripts/streaming/user-list';
+
+const fetchLimit = 10;
+
+export default Vue.extend({
+	props: ['list'],
+	data() {
+		return {
+			fetching: true,
+			moreFetching: false,
+			existMore: false,
+			connection: null
+		};
+	},
+	watch: {
+		$route: 'init'
+	},
+	mounted() {
+		this.init();
+	},
+	beforeDestroy() {
+		this.connection.close();
+	},
+	methods: {
+		init() {
+			if (this.connection) this.connection.close();
+			this.connection = new UserListStream((this as any).os, (this as any).os.i, this.list.id);
+			this.connection.on('note', this.onNote);
+			this.connection.on('userAdded', this.onUserAdded);
+			this.connection.on('userRemoved', this.onUserRemoved);
+
+			this.fetch();
+		},
+		fetch() {
+			this.fetching = true;
+
+			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
+				(this as any).api('notes/user-list-timeline', {
+					listId: this.list.id,
+					limit: fetchLimit + 1,
+					includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
+					includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
+				}).then(notes => {
+					if (notes.length == fetchLimit + 1) {
+						notes.pop();
+						this.existMore = true;
+					}
+					res(notes);
+					this.fetching = false;
+					this.$emit('loaded');
+				}, rej);
+			}));
+		},
+		more() {
+			this.moreFetching = true;
+
+			(this as any).api('notes/list-timeline', {
+				listId: this.list.id,
+				limit: fetchLimit + 1,
+				untilId: (this.$refs.timeline as any).tail().id,
+				includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
+				includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
+			}).then(notes => {
+				if (notes.length == fetchLimit + 1) {
+					notes.pop();
+				} else {
+					this.existMore = false;
+				}
+				notes.forEach(n => (this.$refs.timeline as any).append(n));
+				this.moreFetching = false;
+			});
+		},
+		onNote(note) {
+			// Prepend a note
+			(this.$refs.timeline as any).prepend(note);
+		},
+		onUserAdded() {
+			this.fetch();
+		},
+		onUserRemoved() {
+			this.fetch();
+		}
+	}
+});
+</script>
diff --git a/src/client/app/mobile/views/components/users-list.vue b/src/client/app/mobile/views/components/users-list.vue
index 8fa7a9cbe6..67a38a8955 100644
--- a/src/client/app/mobile/views/components/users-list.vue
+++ b/src/client/app/mobile/views/components/users-list.vue
@@ -1,8 +1,8 @@
 <template>
 <div class="mk-users-list">
 	<nav>
-		<span :data-is-active="mode == 'all'" @click="mode = 'all'">%i18n:@all%<span>{{ count }}</span></span>
-		<span v-if="os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:@known%<span>{{ youKnowCount }}</span></span>
+		<span :data-active="mode == 'all'" @click="mode = 'all'">%i18n:@all%<span>{{ count }}</span></span>
+		<span v-if="os.isSignedIn && youKnowCount" :data-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:@known%<span>{{ youKnowCount }}</span></span>
 	</nav>
 	<div class="users" v-if="!fetching && users.length != 0">
 		<mk-user-preview v-for="u in users" :user="u" :key="u.id"/>
@@ -85,7 +85,7 @@ export default Vue.extend({
 			color #657786
 			border-bottom solid 2px transparent
 
-			&[data-is-active]
+			&[data-active]
 				font-weight bold
 				color $theme-color
 				border-color $theme-color
diff --git a/src/client/app/mobile/views/pages/home.timeline.vue b/src/client/app/mobile/views/pages/home.timeline.vue
index 42ed454d22..5f4bd6dcd8 100644
--- a/src/client/app/mobile/views/pages/home.timeline.vue
+++ b/src/client/app/mobile/views/pages/home.timeline.vue
@@ -13,7 +13,6 @@
 
 <script lang="ts">
 import Vue from 'vue';
-import getNoteSummary from '../../../../../renderers/get-note-summary';
 
 const fetchLimit = 10;
 
@@ -73,8 +72,6 @@ export default Vue.extend({
 			this.connection.on('unfollow', this.onChangeFollowing);
 		}
 
-		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
-
 		this.fetch();
 	},
 
@@ -85,8 +82,6 @@ export default Vue.extend({
 			this.connection.off('unfollow', this.onChangeFollowing);
 		}
 		this.stream.dispose(this.connectionId);
-
-		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 	},
 
 	methods: {
@@ -133,11 +128,6 @@ export default Vue.extend({
 		},
 
 		onNote(note) {
-			if (document.hidden && note.userId !== (this as any).os.i.id) {
-				this.unreadCount++;
-				document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
-			}
-
 			// Prepend a note
 			(this.$refs.timeline as any).prepend(note);
 		},
@@ -153,13 +143,6 @@ export default Vue.extend({
 		warp(date) {
 			this.date = date;
 			this.fetch();
-		},
-
-		onVisibilitychange() {
-			if (!document.hidden) {
-				this.unreadCount = 0;
-				document.title = 'Misskey';
-			}
 		}
 	}
 });
diff --git a/src/client/app/mobile/views/pages/home.vue b/src/client/app/mobile/views/pages/home.vue
index 3b152b3952..92d34fa83b 100644
--- a/src/client/app/mobile/views/pages/home.vue
+++ b/src/client/app/mobile/views/pages/home.vue
@@ -5,7 +5,7 @@
 			<span v-if="src == 'home'">%fa:home%ホーム</span>
 			<span v-if="src == 'local'">%fa:R comments%ローカル</span>
 			<span v-if="src == 'global'">%fa:globe%グローバル</span>
-			<span v-if="src == 'list'">%fa:list%{{ list.title }}</span>
+			<span v-if="src.startsWith('list')">%fa:list%{{ list.title }}</span>
 		</span>
 		<span style="margin-left:8px">
 			<template v-if="!showNav">%fa:angle-down%</template>
@@ -21,9 +21,14 @@
 		<div class="nav" v-if="showNav">
 			<div class="bg" @click="showNav = false"></div>
 			<div class="body">
-				<span :data-is-active="src == 'home'" @click="src = 'home'">%fa:home% ホーム</span>
-				<span :data-is-active="src == 'local'" @click="src = 'local'">%fa:R comments% ローカル</span>
-				<span :data-is-active="src == 'global'" @click="src = 'global'">%fa:globe% グローバル</span>
+				<div>
+					<span :data-active="src == 'home'" @click="src = 'home'">%fa:home% ホーム</span>
+					<span :data-active="src == 'local'" @click="src = 'local'">%fa:R comments% ローカル</span>
+					<span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% グローバル</span>
+					<template v-if="lists">
+						<span v-for="l in lists" :data-active="src == 'list:' + l.id" @click="src = 'list:' + l.id; list = l" :key="l.id">%fa:list% {{ l.title }}</span>
+					</template>
+				</div>
 			</div>
 		</div>
 
@@ -31,7 +36,7 @@
 			<x-tl v-if="src == 'home'" ref="tl" key="home" src="home" @loaded="onLoaded"/>
 			<x-tl v-if="src == 'local'" ref="tl" key="local" src="local"/>
 			<x-tl v-if="src == 'global'" ref="tl" key="global" src="global"/>
-			<mk-user-list-timeline v-if="src == 'list'" ref="tl" key="list" :list="list"/>
+			<mk-user-list-timeline v-if="src.startsWith('list:')" ref="tl" key="list" :list="list"/>
 		</div>
 	</main>
 </mk-ui>
@@ -51,10 +56,25 @@ export default Vue.extend({
 		return {
 			src: 'home',
 			list: null,
+			lists: null,
 			showNav: false
 		};
 	},
 
+	watch: {
+		src() {
+			this.showNav = false;
+		},
+
+		showNav(v) {
+			if (v && this.lists === null) {
+				(this as any).api('users/lists/list').then(lists => {
+					this.lists = lists;
+				});
+			}
+		}
+	},
+
 	mounted() {
 		document.title = 'Misskey';
 		document.documentElement.style.background = '#313a42';
@@ -79,6 +99,8 @@ export default Vue.extend({
 </script>
 
 <style lang="stylus" scoped>
+@import '~const.styl'
+
 main
 	> .nav
 		> .bg
@@ -93,10 +115,52 @@ main
 		> .body
 			position fixed
 			z-index 10001
-			top 48px
+			top 56px
 			left 0
+			right 0
+			width 300px
+			margin 0 auto
 			background #fff
 			border-radius 8px
+			box-shadow 0 0 16px rgba(0, 0, 0, 0.1)
+
+			$balloon-size = 16px
+
+			&:before
+				content ""
+				display block
+				position absolute
+				top -($balloon-size * 2)
+				left s('calc(50% - %s)', $balloon-size)
+				border-top solid $balloon-size transparent
+				border-left solid $balloon-size transparent
+				border-right solid $balloon-size transparent
+				border-bottom solid $balloon-size $border-color
+
+			&:after
+				content ""
+				display block
+				position absolute
+				top -($balloon-size * 2) + 1.5px
+				left s('calc(50% - %s)', $balloon-size)
+				border-top solid $balloon-size transparent
+				border-left solid $balloon-size transparent
+				border-right solid $balloon-size transparent
+				border-bottom solid $balloon-size #fff
+
+			> div
+				padding 8px 0
+
+				> *
+					display block
+					padding 8px 16px
+
+					&[data-active]
+						color $theme-color-foreground
+						background $theme-color
+
+					&:not([data-active]):hover
+						background #eee
 
 	> .tl
 		max-width 600px
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index 3ff9057f73..73b8e24315 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -45,9 +45,9 @@
 		</header>
 		<nav>
 			<div class="nav-container">
-				<a :data-is-active="page == 'home'" @click="page = 'home'">%i18n:@overview%</a>
-				<a :data-is-active="page == 'notes'" @click="page = 'notes'">%i18n:@timeline%</a>
-				<a :data-is-active="page == 'media'" @click="page = 'media'">%i18n:@media%</a>
+				<a :data-active="page == 'home'" @click="page = 'home'">%i18n:@overview%</a>
+				<a :data-active="page == 'notes'" @click="page = 'notes'">%i18n:@timeline%</a>
+				<a :data-active="page == 'media'" @click="page = 'media'">%i18n:@media%</a>
 			</div>
 		</nav>
 		<div class="body">
@@ -256,7 +256,7 @@ main
 				color #657786
 				border-bottom solid 2px transparent
 
-				&[data-is-active]
+				&[data-active]
 					font-weight bold
 					color $theme-color
 					border-color $theme-color