diff --git a/src/web/app/common/mios.ts b/src/web/app/common/mios.ts
index 4ff2333e8..e91def521 100644
--- a/src/web/app/common/mios.ts
+++ b/src/web/app/common/mios.ts
@@ -38,7 +38,7 @@ export default class MiOS extends EventEmitter {
 	/**
 	 * Whether signed in
 	 */
-	public get isSignedin() {
+	public get isSignedIn() {
 		return this.i != null;
 	}
 
@@ -251,7 +251,7 @@ export default class MiOS extends EventEmitter {
 		if (!isSwSupported) return;
 
 		// Reject when not signed in to Misskey
-		if (!this.isSignedin) return;
+		if (!this.isSignedIn) return;
 
 		// When service worker activated
 		navigator.serviceWorker.ready.then(registration => {
diff --git a/src/web/app/desktop/router.ts b/src/web/app/desktop/router.ts
index ce68c4f2d..6ba8bda12 100644
--- a/src/web/app/desktop/router.ts
+++ b/src/web/app/desktop/router.ts
@@ -23,7 +23,7 @@ export default (mios: MiOS) => {
 	route('*',                       notFound);
 
 	function index() {
-		mios.isSignedin ? home() : entrance();
+		mios.isSignedIn ? home() : entrance();
 	}
 
 	function home() {
diff --git a/src/web/app/desktop/script.ts b/src/web/app/desktop/script.ts
index 4aef69b07..e4e5f1914 100644
--- a/src/web/app/desktop/script.ts
+++ b/src/web/app/desktop/script.ts
@@ -5,19 +5,17 @@
 // Style
 import './style.styl';
 
-import Vue from 'vue';
 import init from '../init';
 import fuckAdBlock from './scripts/fuck-ad-block';
-import MiOS from '../common/mios';
 import HomeStreamManager from '../common/scripts/streaming/home-stream-manager';
 import composeNotification from '../common/scripts/compose-notification';
 
-import MkIndex from './tags/pages/index.vue';
+import MkIndex from './views/pages/index.vue';
 
 /**
  * init
  */
-init(async (mios: MiOS, app: Vue) => {
+init(async (os, launch) => {
 	/**
 	 * Fuck AD Block
 	 */
@@ -33,12 +31,17 @@ init(async (mios: MiOS, app: Vue) => {
 		}
 
 		if ((Notification as any).permission == 'granted') {
-			registerNotifications(mios.stream);
+			registerNotifications(os.stream);
 		}
 	}
 
+	// Register components
+	require('./views/components');
+
+	const app = launch();
+
 	app.$router.addRoutes([{
-		path: '/', component: MkIndex, props: { os: mios }
+		path: '/', component: MkIndex, props: { os }
 	}]);
 }, true);
 
diff --git a/src/web/app/desktop/style.styl b/src/web/app/desktop/style.styl
index c893e2ed6..4d295035f 100644
--- a/src/web/app/desktop/style.styl
+++ b/src/web/app/desktop/style.styl
@@ -42,10 +42,10 @@
 		background rgba(0, 0, 0, 0.2)
 
 html
+	height 100%
 	background #f7f7f7
 
-	// ↓ workaround of https://github.com/riot/riot/issues/2134
-	&[data-page='entrance']
-		#wait
-			right auto
-			left 15px
+body
+	display flex
+	flex-direction column
+	min-height 100%
diff --git a/src/web/app/desktop/tags/pages/index.vue b/src/web/app/desktop/tags/pages/index.vue
deleted file mode 100644
index 6bd036fc2..000000000
--- a/src/web/app/desktop/tags/pages/index.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-<template>
-	<h1>hi</h1>
-</template>
diff --git a/src/web/app/desktop/views/components/index.ts b/src/web/app/desktop/views/components/index.ts
new file mode 100644
index 000000000..f628dee88
--- /dev/null
+++ b/src/web/app/desktop/views/components/index.ts
@@ -0,0 +1,5 @@
+import Vue from 'vue';
+
+import ui from './ui.vue';
+
+Vue.component('mk-ui', ui);
diff --git a/src/web/app/desktop/views/components/ui.vue b/src/web/app/desktop/views/components/ui.vue
new file mode 100644
index 000000000..34ac86f70
--- /dev/null
+++ b/src/web/app/desktop/views/components/ui.vue
@@ -0,0 +1,6 @@
+<template>
+<div>
+	<header>misskey</header>
+	<slot></slot>
+</div>
+</template>
diff --git a/src/web/app/desktop/tags/home.vue b/src/web/app/desktop/views/home.vue
similarity index 99%
rename from src/web/app/desktop/tags/home.vue
rename to src/web/app/desktop/views/home.vue
index 981123c56..d054127da 100644
--- a/src/web/app/desktop/tags/home.vue
+++ b/src/web/app/desktop/views/home.vue
@@ -82,8 +82,6 @@
 <script lang="typescript">
 import uuid from 'uuid';
 import Sortable from 'sortablejs';
-import I from '../../common/i';
-import { resolveSrv } from 'dns';
 
 export default {
 	props: {
diff --git a/src/web/app/desktop/views/pages/home.vue b/src/web/app/desktop/views/pages/home.vue
new file mode 100644
index 000000000..8a380fad0
--- /dev/null
+++ b/src/web/app/desktop/views/pages/home.vue
@@ -0,0 +1,17 @@
+<template>
+	<mk-ui>
+		<home ref="home" :mode="mode"/>
+	</mk-ui>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	props: {
+		mode: {
+			type: String,
+			default: 'timeline'
+		}
+	},
+});
+</script>
diff --git a/src/web/app/desktop/views/pages/index.vue b/src/web/app/desktop/views/pages/index.vue
new file mode 100644
index 000000000..dbe77e081
--- /dev/null
+++ b/src/web/app/desktop/views/pages/index.vue
@@ -0,0 +1,17 @@
+<template>
+	<component v-bind:is="os.isSignedIn ? 'home' : 'welcome'"></component>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import HomeView from './home.vue';
+import WelcomeView from './welcome.vue';
+
+export default Vue.extend({
+	props: ['os'],
+	components: {
+		home: HomeView,
+		welcome: WelcomeView
+	}
+});
+</script>
diff --git a/src/web/app/desktop/views/pages/welcome.vue b/src/web/app/desktop/views/pages/welcome.vue
new file mode 100644
index 000000000..a99a31d6b
--- /dev/null
+++ b/src/web/app/desktop/views/pages/welcome.vue
@@ -0,0 +1,119 @@
+<template>
+<div class="root">
+	<main>
+		<div>
+			<h1>繋がるNotes</h1>
+			<p>ようこそ! <b>Misskey</b>はTwitter風ミニブログSNSです――思ったこと、共有したいことをシンプルに書き残せます。タイムラインを見れば、皆の反応や皆がどう思っているのかもすぐにわかります。<a>詳しく...</a></p>
+			<p><button class="signup" @click="signup">はじめる</button><button class="signin" @click="signin">ログイン</button></p>
+		</div>
+		<div>
+
+		</div>
+	</main>
+	<mk-forkit/>
+	<footer>
+		<div>
+			<mk-nav-links/>
+			<p class="c">{ _COPYRIGHT_ }</p>
+		</div>
+	</footer>
+</div>
+</template>
+
+<style>
+	#wait {
+		right: auto;
+		left: 15px;
+	}
+</style>
+
+<style lang="stylus" scoped>
+	.root
+		display flex
+		flex-direction column
+		flex 1
+		background #eee
+		$width = 1000px
+
+		> main
+			display flex
+			flex 1
+			max-width $width
+			margin 0 auto
+			padding 80px 0 0 0
+
+			> div:first-child
+				margin 0 auto 0 0
+				width calc(100% - 500px)
+				color #777
+
+				> h1
+					margin 0
+					font-weight normal
+
+				> p
+					margin 0.5em 0
+					line-height 2em
+
+				button
+					padding 8px 16px
+					font-size inherit
+
+				.signup
+					color $theme-color
+					border solid 2px $theme-color
+					border-radius 4px
+
+					&:focus
+						box-shadow 0 0 0 3px rgba($theme-color, 0.2)
+
+					&:hover
+						color $theme-color-foreground
+						background $theme-color
+
+					&:active
+						color $theme-color-foreground
+						background darken($theme-color, 10%)
+						border-color darken($theme-color, 10%)
+
+				.signin
+					&:focus
+						color #444
+
+					&:hover
+						color #444
+
+					&:active
+						color #333
+
+			> div:last-child
+				margin 0 0 0 auto
+
+		> footer
+			background #fff
+
+			*
+				color #fff !important
+				text-shadow 0 0 8px #000
+				font-weight bold
+
+			> div
+				max-width $width
+				margin 0 auto
+				padding 16px 0
+				text-align center
+				border-top solid 1px #fff
+
+				> .c
+					margin 0
+					line-height 64px
+					font-size 10px
+
+</style>
+
+<script lang="ts">
+import Vue from 'vue'
+export default Vue.extend({
+
+})
+</script>
diff --git a/src/web/app/init.ts b/src/web/app/init.ts
index 91797a95a..796a96694 100644
--- a/src/web/app/init.ts
+++ b/src/web/app/init.ts
@@ -61,22 +61,24 @@ if (localStorage.getItem('should-refresh') == 'true') {
 }
 
 // MiOSを初期化してコールバックする
-export default (callback, sw = false) => {
+export default (callback: (os: MiOS, launch: () => Vue) => void, sw = false) => {
 	const mios = new MiOS(sw);
 
 	mios.init(() => {
 		// アプリ基底要素マウント
 		document.body.innerHTML = '<div id="app"></div>';
 
-		const app = new Vue({
-			router: new VueRouter({
-				mode: 'history'
-			}),
-			render: createEl => createEl(App)
-		}).$mount('#app');
+		const launch = () => {
+			return new Vue({
+				router: new VueRouter({
+					mode: 'history'
+				}),
+				render: createEl => createEl(App)
+			}).$mount('#app');
+		};
 
 		try {
-			callback(mios, app);
+			callback(mios, launch);
 		} catch (e) {
 			panic(e);
 		}
diff --git a/src/web/app/mobile/router.ts b/src/web/app/mobile/router.ts
index afb9aa620..050fa7fc2 100644
--- a/src/web/app/mobile/router.ts
+++ b/src/web/app/mobile/router.ts
@@ -32,7 +32,7 @@ export default (mios: MiOS) => {
 	route('*',                           notFound);
 
 	function index() {
-		mios.isSignedin ? home() : entrance();
+		mios.isSignedIn ? home() : entrance();
 	}
 
 	function home() {
diff --git a/webpack/module/rules/theme-color.ts b/webpack/module/rules/theme-color.ts
index 7ee545191..a65338465 100644
--- a/webpack/module/rules/theme-color.ts
+++ b/webpack/module/rules/theme-color.ts
@@ -8,7 +8,7 @@ const constants = require('../../../src/const.json');
 
 export default () => ({
 	enforce: 'pre',
-	test: /\.tag$/,
+	test: /\.vue$/,
 	exclude: /node_modules/,
 	loader: StringReplacePlugin.replace({
 		replacements: [