From e6e02ece89e9e2af2ae0aa4c57cb9fcdb3d5b890 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 06:29:01 +0900
Subject: [PATCH 01/11] wip

---
 package.json                                  |   1 -
 src/client/app/app.styl                       |   5 -
 .../app/common/views/components/index.ts      |   2 +
 .../views/components/material/input.vue       | 129 ++++++++++++++++++
 src/client/app/mobile/script.ts               |  15 --
 src/client/app/mobile/style.styl              |   3 -
 src/client/app/mobile/views/pages/welcome.vue |  18 ++-
 .../app/mobile/views/widgets/profile.vue      |   2 +-
 src/client/md.scss                            |  13 --
 9 files changed, 145 insertions(+), 43 deletions(-)
 create mode 100644 src/client/app/common/views/components/material/input.vue
 delete mode 100644 src/client/md.scss

diff --git a/package.json b/package.json
index 4b207d5e5a..b1b8888b5c 100644
--- a/package.json
+++ b/package.json
@@ -211,7 +211,6 @@
 		"vue-js-modal": "1.3.13",
 		"vue-json-tree-view": "2.1.4",
 		"vue-loader": "15.2.1",
-		"vue-material": "^1.0.0-beta-10.2",
 		"vue-router": "3.0.1",
 		"vue-template-compiler": "2.5.16",
 		"vuedraggable": "2.16.0",
diff --git a/src/client/app/app.styl b/src/client/app/app.styl
index ba694b73ae..431b9daa65 100644
--- a/src/client/app/app.styl
+++ b/src/client/app/app.styl
@@ -7,11 +7,6 @@ html
 			cursor progress !important
 
 body
-	// for md
-	font-size 16px !important
-	line-height initial !important
-	letter-spacing initial !important
-
 	overflow-wrap break-word
 
 #error
diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 803854468e..659c2aca2a 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -29,6 +29,7 @@ import fileTypeIcon from './file-type-icon.vue';
 import Switch from './switch.vue';
 import Othello from './othello.vue';
 import welcomeTimeline from './welcome-timeline.vue';
+import uiInput from './material/input.vue';
 
 Vue.component('mk-analog-clock', analogClock);
 Vue.component('mk-menu', menu);
@@ -59,3 +60,4 @@ Vue.component('mk-file-type-icon', fileTypeIcon);
 Vue.component('mk-switch', Switch);
 Vue.component('mk-othello', Othello);
 Vue.component('mk-welcome-timeline', welcomeTimeline);
+Vue.component('ui-input', uiInput);
diff --git a/src/client/app/common/views/components/material/input.vue b/src/client/app/common/views/components/material/input.vue
new file mode 100644
index 0000000000..42ff0bb4b8
--- /dev/null
+++ b/src/client/app/common/views/components/material/input.vue
@@ -0,0 +1,129 @@
+<template>
+<div class="ui-input" :class="{ focused, filled }">
+	<div class="input">
+		<span class="label" ref="label"><slot></slot></span>
+		<div class="prefix" ref="prefix" @click="focus"><slot name="prefix"></slot></div>
+		<input ref="input" :value="value" @input="$emit('input', $event.target.value)" @focus="focused = true" @blur="focused = false">
+		<div class="suffix" @click="focus"><slot name="suffix"></slot></div>
+	</div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	props: ['value'],
+	data() {
+		return {
+			focused: false
+		}
+	},
+	computed: {
+		filled(): boolean {
+			return this.value != '' && this.value != null;
+		}
+	},
+	mounted() {
+		this.$refs.label.style.left = this.$refs.prefix.offsetWidth + 'px';
+	},
+	methods: {
+		focus() {
+			this.$refs.input.focus();
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-input
+	padding-bottom 16px
+
+	> .input
+		display flex
+		margin-top 16px
+
+		&:before
+			content ''
+			display block
+			position absolute
+			bottom 0
+			left 0
+			right 0
+			height 1px
+			background rgba(#000, 0.42)
+
+		&:after
+			content ''
+			display block
+			position absolute
+			bottom 0
+			left 0
+			right 0
+			height 2px
+			background $theme-color
+			opacity 0
+			transform scaleX(0.12)
+			transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)
+			will-change border opacity transform
+
+		> .label
+			position absolute
+			top 0
+			left 0
+			pointer-events none
+			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
+			transition-duration 0.3s
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+			pointer-events none
+
+		> input
+			display block
+			flex 1
+			width 100%
+			padding 0
+			font-size 16px
+			line-height 32px
+			background transparent
+			border none
+			border-radius 0
+			outline none
+			box-shadow none
+
+		> .prefix
+		> .suffix
+			display block
+			align-self center
+			justify-self center
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+
+		> .prefix
+			padding-right 4px
+
+		> .suffix
+			padding-left 4px
+
+	&.focused
+		> .input
+			&:after
+				opacity 1
+				transform scaleX(1)
+
+			> .label
+				color $theme-color
+
+	&.focused
+	&.filled
+		> .input
+			> .label
+				top -16px
+				left 0 !important
+				font-size 12px
+				line-height 20px
+
+</style>
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index d505b38dcc..a6f4359432 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -5,10 +5,6 @@
 import Vue from 'vue';
 import VueRouter from 'vue-router';
 
-import { MdCard, MdButton, MdField, MdMenu, MdList, MdSwitch, MdSubheader, MdDialog, MdDialogAlert, MdRadio } from 'vue-material/dist/components';
-import 'vue-material/dist/vue-material.min.css';
-import 'vue-material/dist/theme/default.css';
-
 // Style
 import './style.styl';
 import '../../element.scss';
@@ -44,17 +40,6 @@ import MkSettings from './views/pages/settings.vue';
 import MkOthello from './views/pages/othello.vue';
 import MkTag from './views/pages/tag.vue';
 
-Vue.use(MdCard);
-Vue.use(MdButton);
-Vue.use(MdField);
-Vue.use(MdMenu);
-Vue.use(MdList);
-Vue.use(MdSwitch);
-Vue.use(MdSubheader);
-Vue.use(MdDialog);
-Vue.use(MdDialogAlert);
-Vue.use(MdRadio);
-
 /**
  * init
  */
diff --git a/src/client/app/mobile/style.styl b/src/client/app/mobile/style.styl
index d1ab044eaf..df8f4a8fae 100644
--- a/src/client/app/mobile/style.styl
+++ b/src/client/app/mobile/style.styl
@@ -10,9 +10,6 @@ html
 	height 100%
 	background #ececed !important
 
-	// for md
-	transition none !important
-
 	&[data-darkmode]
 		background #191B22 !important
 
diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue
index 64cfa5a46c..ceb1abb9a0 100644
--- a/src/client/app/mobile/views/pages/welcome.vue
+++ b/src/client/app/mobile/views/pages/welcome.vue
@@ -7,9 +7,16 @@
 			<p>%fa:lock% ログイン</p>
 			<div>
 				<form @submit.prevent="onSubmit">
-					<input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange"/>
-					<input v-model="password" type="password" placeholder="パスワード" required/>
-					<input v-if="user && user.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
+					<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange">
+						<span>ユーザー名</span>
+						<span slot="prefix">@</span>
+						<span slot="suffix">@{{ host }}</span>
+					</ui-input>
+					<ui-input v-model="password" type="password" placeholder="パスワード" required>
+						<span>パスワード</span>
+						<span slot="prefix">%fa:lock%</span>
+					</ui-input>
+					<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
 					<button type="submit" :disabled="signing">{{ signing ? 'ログインしています' : 'ログイン' }}</button>
 				</form>
 				<div>
@@ -33,7 +40,7 @@
 
 <script lang="ts">
 import Vue from 'vue';
-import { apiUrl, copyright } from '../../../config';
+import { apiUrl, copyright, host } from '../../../config';
 
 export default Vue.extend({
 	data() {
@@ -45,7 +52,8 @@ export default Vue.extend({
 			token: '',
 			apiUrl,
 			copyright,
-			users: []
+			users: [],
+			host
 		};
 	},
 	mounted() {
diff --git a/src/client/app/mobile/views/widgets/profile.vue b/src/client/app/mobile/views/widgets/profile.vue
index beae1ffa36..a94f7e94b8 100644
--- a/src/client/app/mobile/views/widgets/profile.vue
+++ b/src/client/app/mobile/views/widgets/profile.vue
@@ -56,7 +56,7 @@ export default define({
 	left 92px
 	margin 0
 	line-height 100px
-	color #fff !important // !important is for md
+	color #fff
 	font-weight bold
 	text-shadow 0 0 8px rgba(#000, 0.5)
 
diff --git a/src/client/md.scss b/src/client/md.scss
deleted file mode 100644
index 8368365885..0000000000
--- a/src/client/md.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SEE: https://vuematerial.io/themes/configuration */
-
-@import '../const.json';
-
-@import "~vue-material/dist/theme/engine";
-
-@include md-register-theme("default", (
-	primary: $themeColor,
-	accent: $themeColor
-));
-
-@import "~vue-material/dist/components/MdButton/theme";
-@import "~vue-material/dist/components/MdField/theme";

From 8814fc9c9c0614215f7e5dbe3ec1316c49e2b68d Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 07:22:50 +0900
Subject: [PATCH 02/11] wip

---
 .../app/common/views/components/index.ts      |   2 +
 .../views/components/material/button.vue      |  39 ++++++
 .../views/components/material/input.vue       |  41 ++----
 src/client/app/mobile/script.ts               |   2 -
 src/client/app/mobile/views/pages/welcome.vue | 123 ++++++------------
 5 files changed, 92 insertions(+), 115 deletions(-)
 create mode 100644 src/client/app/common/views/components/material/button.vue

diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 659c2aca2a..5dc466857c 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -30,6 +30,7 @@ import Switch from './switch.vue';
 import Othello from './othello.vue';
 import welcomeTimeline from './welcome-timeline.vue';
 import uiInput from './material/input.vue';
+import uiButton from './material/button.vue';
 
 Vue.component('mk-analog-clock', analogClock);
 Vue.component('mk-menu', menu);
@@ -61,3 +62,4 @@ Vue.component('mk-switch', Switch);
 Vue.component('mk-othello', Othello);
 Vue.component('mk-welcome-timeline', welcomeTimeline);
 Vue.component('ui-input', uiInput);
+Vue.component('ui-button', uiButton);
diff --git a/src/client/app/common/views/components/material/button.vue b/src/client/app/common/views/components/material/button.vue
new file mode 100644
index 0000000000..0c768cf4cc
--- /dev/null
+++ b/src/client/app/common/views/components/material/button.vue
@@ -0,0 +1,39 @@
+<template>
+<div class="ui-button">
+	<button>
+		<slot></slot>
+	</button>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	props: {
+		type: {
+			type: String,
+			required: false
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-button
+	> button
+		display block
+		width 100%
+		padding 0
+		color $theme-color-foreground
+		font-weight bold
+		font-size 16px
+		line-height 44px
+		background $theme-color
+		border none
+		border-radius 6px
+		outline none
+		box-shadow none
+
+</style>
diff --git a/src/client/app/common/views/components/material/input.vue b/src/client/app/common/views/components/material/input.vue
index 42ff0bb4b8..fe64354908 100644
--- a/src/client/app/common/views/components/material/input.vue
+++ b/src/client/app/common/views/components/material/input.vue
@@ -24,7 +24,7 @@ export default Vue.extend({
 		}
 	},
 	mounted() {
-		this.$refs.label.style.left = this.$refs.prefix.offsetWidth + 'px';
+		this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
 	},
 	methods: {
 		focus() {
@@ -38,39 +38,18 @@ export default Vue.extend({
 @import '~const.styl'
 
 .ui-input
-	padding-bottom 16px
+	margin-bottom 32px
 
 	> .input
 		display flex
 		margin-top 16px
-
-		&:before
-			content ''
-			display block
-			position absolute
-			bottom 0
-			left 0
-			right 0
-			height 1px
-			background rgba(#000, 0.42)
-
-		&:after
-			content ''
-			display block
-			position absolute
-			bottom 0
-			left 0
-			right 0
-			height 2px
-			background $theme-color
-			opacity 0
-			transform scaleX(0.12)
-			transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)
-			will-change border opacity transform
+		padding 6px 12px
+		background #f5f5f5
+		border-radius 6px
 
 		> .label
 			position absolute
-			top 0
+			top 6px
 			left 0
 			pointer-events none
 			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
@@ -85,6 +64,8 @@ export default Vue.extend({
 			flex 1
 			width 100%
 			padding 0
+			font inherit
+			font-weight bold
 			font-size 16px
 			line-height 32px
 			background transparent
@@ -110,9 +91,7 @@ export default Vue.extend({
 
 	&.focused
 		> .input
-			&:after
-				opacity 1
-				transform scaleX(1)
+			background #eee
 
 			> .label
 				color $theme-color
@@ -121,7 +100,7 @@ export default Vue.extend({
 	&.filled
 		> .input
 			> .label
-				top -16px
+				top -20px
 				left 0 !important
 				font-size 12px
 				line-height 20px
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index a6f4359432..8ee078d621 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -2,13 +2,11 @@
  * Mobile Client
  */
 
-import Vue from 'vue';
 import VueRouter from 'vue-router';
 
 // Style
 import './style.styl';
 import '../../element.scss';
-import '../../md.scss';
 
 import init from '../init';
 
diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue
index ceb1abb9a0..07891fd56b 100644
--- a/src/client/app/mobile/views/pages/welcome.vue
+++ b/src/client/app/mobile/views/pages/welcome.vue
@@ -1,27 +1,23 @@
 <template>
 <div class="welcome">
 	<div>
-		<h1><b>Misskey</b>へようこそ</h1>
-		<p>Twitter風ミニブログSNS、Misskeyへようこそ。共有したいことを投稿したり、タイムラインでみんなの投稿を読むこともできます。<br><a href="/signup">アカウントを作成する</a></p>
+		<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" alt="Misskey">
 		<div class="form">
-			<p>%fa:lock% ログイン</p>
+			<form @submit.prevent="onSubmit">
+				<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange">
+					<span>ユーザー名</span>
+					<span slot="prefix">@</span>
+					<span slot="suffix">@{{ host }}</span>
+				</ui-input>
+				<ui-input v-model="password" type="password" placeholder="パスワード" required>
+					<span>パスワード</span>
+					<span slot="prefix">%fa:lock%</span>
+				</ui-input>
+				<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
+				<ui-button type="submit" :disabled="signing">{{ signing ? 'ログインしています' : 'ログイン' }}</ui-button>
+			</form>
 			<div>
-				<form @submit.prevent="onSubmit">
-					<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange">
-						<span>ユーザー名</span>
-						<span slot="prefix">@</span>
-						<span slot="suffix">@{{ host }}</span>
-					</ui-input>
-					<ui-input v-model="password" type="password" placeholder="パスワード" required>
-						<span>パスワード</span>
-						<span slot="prefix">%fa:lock%</span>
-					</ui-input>
-					<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
-					<button type="submit" :disabled="signing">{{ signing ? 'ログインしています' : 'ログイン' }}</button>
-				</form>
-				<div>
-					<a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a>
-				</div>
+				<a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a>
 			</div>
 		</div>
 		<div class="tl">
@@ -92,81 +88,44 @@ export default Vue.extend({
 
 <style lang="stylus" scoped>
 .welcome
-	background linear-gradient(to bottom, #1e1d65, #bd6659)
+	background #fff
 
 	> div
 		padding 16px
 		margin 0 auto
 		max-width 500px
 
-		h1
-			margin 0
-			padding 8px
-			font-size 1.5em
-			font-weight normal
-			color #cacac3
-
-			& + p
-				margin 0 0 16px 0
-				padding 0 8px 0 8px
-				color #949fa9
+		> img
+			display block
+			max-width 200px
+			margin 0 auto
 
 		.form
 			margin-bottom 16px
-			background #fff
-			border solid 1px rgba(#000, 0.2)
-			border-radius 8px
-			overflow hidden
 
-			> p
-				margin 0
-				padding 12px 20px
-				color #555
-				background #f5f5f5
-				border-bottom solid 1px #ddd
+			> form
+				padding 16px
 
-			> div
-
-				> form
-					padding 16px
-					border-bottom solid 1px #ddd
-
-					input
-						display block
-						padding 12px
-						margin 0 0 16px 0
-						width 100%
-						font-size 1em
-						color rgba(#000, 0.7)
-						background #fff
-						outline none
-						border solid 1px #ddd
-						border-radius 4px
-
-					button
-						display block
-						width 100%
-						padding 10px
-						margin 0
-						color #333
-						font-size 1em
-						text-align center
-						text-decoration none
-						text-shadow 0 1px 0 rgba(255, 255, 255, 0.9)
-						background-image linear-gradient(#fafafa, #eaeaea)
-						border 1px solid #ddd
-						border-bottom-color #cecece
-						border-radius 4px
-
-						&:active
-							background-color #767676
-							background-image none
-							border-color #444
-							box-shadow 0 1px 3px rgba(#000, 0.075), inset 0 0 5px rgba(#000, 0.2)
-
-				> div
-					padding 16px
+				button
+					display block
+					width 100%
+					padding 10px
+					margin 0
+					color #333
+					font-size 1em
 					text-align center
+					text-decoration none
+					text-shadow 0 1px 0 rgba(255, 255, 255, 0.9)
+					background-image linear-gradient(#fafafa, #eaeaea)
+					border 1px solid #ddd
+					border-bottom-color #cecece
+					border-radius 4px
+
+					&:active
+						background-color #767676
+						background-image none
+						border-color #444
+						box-shadow 0 1px 3px rgba(#000, 0.075), inset 0 0 5px rgba(#000, 0.2)
 
 		> .tl
 			background #fff

From 3a4833818f67aec56bc3570088a1ef34466f3905 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 09:51:55 +0900
Subject: [PATCH 03/11] wip

---
 .../views/components/material/button.vue      |  3 +-
 .../views/components/material/input.vue       | 43 ++++++++--
 .../app/common/views/components/signup.vue    | 54 ++++++-------
 src/client/app/mobile/views/pages/signup.vue  | 41 ++--------
 src/client/app/mobile/views/pages/welcome.vue | 80 +++++++++----------
 5 files changed, 109 insertions(+), 112 deletions(-)

diff --git a/src/client/app/common/views/components/material/button.vue b/src/client/app/common/views/components/material/button.vue
index 0c768cf4cc..8dacedbac6 100644
--- a/src/client/app/common/views/components/material/button.vue
+++ b/src/client/app/common/views/components/material/button.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="ui-button">
-	<button>
+	<button :type="type">
 		<slot></slot>
 	</button>
 </div>
@@ -25,6 +25,7 @@ export default Vue.extend({
 	> button
 		display block
 		width 100%
+		margin 32px 0 16px 0
 		padding 0
 		color $theme-color-foreground
 		font-weight bold
diff --git a/src/client/app/common/views/components/material/input.vue b/src/client/app/common/views/components/material/input.vue
index fe64354908..6564b3aa6c 100644
--- a/src/client/app/common/views/components/material/input.vue
+++ b/src/client/app/common/views/components/material/input.vue
@@ -3,16 +3,40 @@
 	<div class="input">
 		<span class="label" ref="label"><slot></slot></span>
 		<div class="prefix" ref="prefix" @click="focus"><slot name="prefix"></slot></div>
-		<input ref="input" :value="value" @input="$emit('input', $event.target.value)" @focus="focused = true" @blur="focused = false">
+		<input ref="input"
+				:type="type"
+				:value="value"
+				:required="required"
+				:readonly="readonly"
+				@input="$emit('input', $event.target.value)"
+				@focus="focused = true"
+				@blur="focused = false">
 		<div class="suffix" @click="focus"><slot name="suffix"></slot></div>
 	</div>
+	<div class="text"><slot name="text"></slot></div>
 </div>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
 export default Vue.extend({
-	props: ['value'],
+	props: {
+		value: {
+			required: false
+		},
+		type: {
+			type: String,
+			required: false
+		},
+		required: {
+			type: Boolean,
+			required: false
+		},
+		readonly: {
+			type: Boolean,
+			required: false
+		}
+	},
 	data() {
 		return {
 			focused: false
@@ -38,13 +62,13 @@ export default Vue.extend({
 @import '~const.styl'
 
 .ui-input
-	margin-bottom 32px
+	margin-bottom 16px
+	padding-top 16px
 
 	> .input
 		display flex
-		margin-top 16px
 		padding 6px 12px
-		background #f5f5f5
+		background rgba(#000, 0.035)
 		border-radius 6px
 
 		> .label
@@ -89,9 +113,16 @@ export default Vue.extend({
 		> .suffix
 			padding-left 4px
 
+	> .text
+		margin 8px 0
+		font-size 14px
+
+		> p
+			margin 0
+
 	&.focused
 		> .input
-			background #eee
+			background rgba(#000, 0.05)
 
 			> .label
 				color $theme-color
diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue
index f8bf7dd798..3adb10f2c3 100644
--- a/src/client/app/common/views/components/signup.vue
+++ b/src/client/app/common/views/components/signup.vue
@@ -1,20 +1,27 @@
 <template>
 <form class="mk-signup" @submit.prevent="onSubmit" autocomplete="off">
 	<label class="username">
-		<p class="caption">%fa:at%%i18n:@username%</p>
-		<input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required @input="onChangeUsername"/>
-		<p class="profile-page-url-preview" v-if="shouldShowProfileUrl">{{ `${url}/@${username}` }}</p>
-		<p class="info" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%%i18n:@checking%</p>
-		<p class="info" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw%%i18n:@available%</p>
-		<p class="info" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@unavailable%</p>
-		<p class="info" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@error%</p>
-		<p class="info" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@invalid-format%</p>
-		<p class="info" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-short%</p>
-		<p class="info" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-long%</p>
+		<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" autocomplete="off" required @input="onChangeUsername">
+			<span>%i18n:@username%</span>
+			<span slot="prefix">@</span>
+			<span slot="suffix">@{{ host }}</span>
+			<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%%i18n:@checking%</p>
+			<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw%%i18n:@available%</p>
+			<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@unavailable%</p>
+			<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@error%</p>
+			<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@invalid-format%</p>
+			<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-short%</p>
+			<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-long%</p>
+		</ui-input>
 	</label>
 	<label class="password">
-		<p class="caption">%fa:lock%%i18n:@password%</p>
-		<input v-model="password" type="password" placeholder="%i18n:@password-placeholder%" autocomplete="off" required @input="onChangePassword"/>
+		<ui-input v-model="password" type="password" autocomplete="off" required @input="onChangePassword">
+			<span>%i18n:@password%</span>
+			<span slot="prefix">%fa:lock%</span>
+			<div slot="text">
+
+			</div>
+		</ui-input>
 		<div class="meter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
 			<div class="value" ref="passwordMetar"></div>
 		</div>
@@ -23,14 +30,15 @@
 		<p class="info" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw%%i18n:@strong-password%</p>
 	</label>
 	<label class="retype-password">
-		<p class="caption">%fa:lock%%i18n:@password%(%i18n:@retype%)</p>
-		<input v-model="retypedPassword" type="password" placeholder="%i18n:@retype-placeholder%" autocomplete="off" required @input="onChangePasswordRetype"/>
+		<ui-input v-model="retypedPassword" type="password" autocomplete="off" required @input="onChangePasswordRetype">
+			<span>%i18n:@password% (%i18n:@retype%)</span>
+			<span slot="prefix">%fa:lock%</span>
+		</ui-input>
 		<p class="info" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw%%i18n:@password-matched%</p>
 		<p class="info" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@password-not-matched%</p>
 	</label>
 	<label class="recaptcha">
-		<p class="caption"><template v-if="recaptchaed">%fa:toggle-on%</template><template v-if="!recaptchaed">%fa:toggle-off%</template>%i18n:@recaptcha%</p>
-		<div class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" :data-sitekey="recaptchaSitekey"></div>
+		<div class="g-recaptcha" :data-sitekey="recaptchaSitekey"></div>
 	</label>
 	<label class="agree-tou">
 		<input name="agree-tou" type="checkbox" autocomplete="off" required/>
@@ -43,18 +51,18 @@
 <script lang="ts">
 import Vue from 'vue';
 const getPasswordStrength = require('syuilo-password-strength');
-import { url, docsUrl, lang, recaptchaSitekey } from '../../../config';
+import { host, url, docsUrl, lang, recaptchaSitekey } from '../../../config';
 
 export default Vue.extend({
 	data() {
 		return {
+			host,
 			username: '',
 			password: '',
 			retypedPassword: '',
 			url,
 			touUrl: `${docsUrl}/${lang}/tou`,
 			recaptchaSitekey,
-			recaptchaed: false,
 			usernameState: null,
 			passwordStrength: '',
 			passwordRetypeState: null
@@ -130,19 +138,9 @@ export default Vue.extend({
 				alert('%i18n:@some-error%');
 
 				(window as any).grecaptcha.reset();
-				this.recaptchaed = false;
 			});
 		}
 	},
-	created() {
-		(window as any).onRecaptchaed = () => {
-			this.recaptchaed = true;
-		};
-
-		(window as any).onRecaptchaExpired = () => {
-			this.recaptchaed = false;
-		};
-	},
 	mounted() {
 		const head = document.getElementsByTagName('head')[0];
 		const script = document.createElement('script');
diff --git a/src/client/app/mobile/views/pages/signup.vue b/src/client/app/mobile/views/pages/signup.vue
index b8245beb00..f2b29bca60 100644
--- a/src/client/app/mobile/views/pages/signup.vue
+++ b/src/client/app/mobile/views/pages/signup.vue
@@ -1,28 +1,18 @@
 <template>
 <div class="signup">
 	<h1>Misskeyをはじめる</h1>
-	<p>いつでも、どこからでもMisskeyを利用できます。もちろん、無料です。</p>
-	<div class="form">
-		<p>新規登録</p>
-		<div>
-			<mk-signup/>
-		</div>
-	</div>
+	<mk-signup/>
 </div>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
-export default Vue.extend({
-	mounted() {
-		document.documentElement.style.background = '#293946';
-	}
-});
+export default Vue.extend({});
 </script>
 
 <style lang="stylus" scoped>
 .signup
-	padding 16px
+	padding 32px
 	margin 0 auto
 	max-width 500px
 
@@ -30,28 +20,7 @@ export default Vue.extend({
 		margin 0
 		padding 8px
 		font-size 1.5em
-		font-weight normal
-		color #c3c6ca
-
-		& + p
-			margin 0 0 16px 0
-			padding 0 8px 0 8px
-			color #949fa9
-
-	.form
-		background #fff
-		border solid 1px rgba(#000, 0.2)
-		border-radius 8px
-		overflow hidden
-
-		> p
-			margin 0
-			padding 12px 20px
-			color #555
-			background #f5f5f5
-			border-bottom solid 1px #ddd
-
-		> div
-			padding 16px
+		font-weight bold
+		color #444
 
 </style>
diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue
index 07891fd56b..01b20aa472 100644
--- a/src/client/app/mobile/views/pages/welcome.vue
+++ b/src/client/app/mobile/views/pages/welcome.vue
@@ -2,7 +2,13 @@
 <div class="welcome">
 	<div>
 		<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" alt="Misskey">
-		<div class="form">
+		<p class="host">{{ host }}</p>
+		<div class="about">
+			<h2>{{ name || 'unidentified' }}</h2>
+			<p v-html="description || '%i18n:common.about%'"></p>
+			<router-link class="signup" to="/signup">新規登録</router-link>
+		</div>
+		<div class="login">
 			<form @submit.prevent="onSubmit">
 				<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange">
 					<span>ユーザー名</span>
@@ -20,13 +26,6 @@
 				<a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a>
 			</div>
 		</div>
-		<div class="tl">
-			<p>%fa:comments R% タイムラインを見てみる</p>
-			<mk-welcome-timeline/>
-		</div>
-		<div class="users">
-			<mk-avatar class="avatar" v-for="user in users" :key="user.id" :user="user"/>
-		</div>
 		<footer>
 			<small>{{ copyright }}</small>
 		</footer>
@@ -88,10 +87,11 @@ export default Vue.extend({
 
 <style lang="stylus" scoped>
 .welcome
-	background #fff
+	text-align center
+	//background #fff
 
 	> div
-		padding 16px
+		padding 32px
 		margin 0 auto
 		max-width 500px
 
@@ -100,11 +100,36 @@ export default Vue.extend({
 			max-width 200px
 			margin 0 auto
 
-		.form
-			margin-bottom 16px
+		> .host
+			display block
+			text-align center
+			padding 6px 12px
+			line-height 32px
+			font-weight bold
+			color #333
+			background rgba(#000, 0.035)
+			border-radius 6px
+
+		> .about
+			margin-top 16px
+			padding 16px
+			color #444
+			background #fff
+			border-radius 6px
+
+			> h2
+				margin 0
+
+			> p
+				margin 8px
+
+			> .signup
+				font-weight bold
+
+		> .login
+			margin 16px 0
 
 			> form
-				padding 16px
 
 				button
 					display block
@@ -127,36 +152,9 @@ export default Vue.extend({
 						border-color #444
 						box-shadow 0 1px 3px rgba(#000, 0.075), inset 0 0 5px rgba(#000, 0.2)
 
-		> .tl
-			background #fff
-			border solid 1px rgba(#000, 0.2)
-			border-radius 8px
-			overflow hidden
-
-			> p
-				margin 0
-				padding 12px 20px
-				color #555
-				background #f5f5f5
-				border-bottom solid 1px #ddd
-
-			> .mk-welcome-timeline
-				max-height 300px
-				overflow auto
-
-		> .users
-			margin 12px 0 0 0
-
-			> *
-				display inline-block
-				margin 4px
-				width 38px
-				height 38px
-				border-radius 6px
-
 		> footer
 			text-align center
-			color #fff
+			color #444
 
 			> small
 				display block

From a1ae832129e8bfe8c082064349e2b9973be5f0e5 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 14:52:37 +0900
Subject: [PATCH 04/11] wip

---
 .../app/common/views/components/index.ts      |  12 +-
 .../app/common/views/components/signup.vue    | 182 +++------------
 .../components/{material => ui}/button.vue    |   8 +-
 .../app/common/views/components/ui/form.vue   |  28 +++
 .../app/common/views/components/ui/group.vue  |  23 ++
 .../app/common/views/components/ui/input.vue  | 215 ++++++++++++++++++
 .../app/common/views/components/ui/switch.vue | 199 ++++++++++++++++
 .../{material/input.vue => ui/textarea.vue}   |  74 +++---
 .../app/mobile/views/pages/settings.vue       | 170 ++++++--------
 .../views/pages/settings/settings.profile.vue |  83 +++----
 src/client/app/mobile/views/pages/signup.vue  |   2 +-
 src/client/app/mobile/views/pages/welcome.vue |   8 +-
 12 files changed, 658 insertions(+), 346 deletions(-)
 rename src/client/app/common/views/components/{material => ui}/button.vue (83%)
 create mode 100644 src/client/app/common/views/components/ui/form.vue
 create mode 100644 src/client/app/common/views/components/ui/group.vue
 create mode 100644 src/client/app/common/views/components/ui/input.vue
 create mode 100644 src/client/app/common/views/components/ui/switch.vue
 rename src/client/app/common/views/components/{material/input.vue => ui/textarea.vue} (64%)

diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 5dc466857c..060af388b3 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -29,8 +29,12 @@ import fileTypeIcon from './file-type-icon.vue';
 import Switch from './switch.vue';
 import Othello from './othello.vue';
 import welcomeTimeline from './welcome-timeline.vue';
-import uiInput from './material/input.vue';
-import uiButton from './material/button.vue';
+import uiInput from './ui/input.vue';
+import uiButton from './ui/button.vue';
+import uiGroup from './ui/group.vue';
+import uiForm from './ui/form.vue';
+import uiTextarea from './ui/textarea.vue';
+import uiSwitch from './ui/switch.vue';
 
 Vue.component('mk-analog-clock', analogClock);
 Vue.component('mk-menu', menu);
@@ -63,3 +67,7 @@ Vue.component('mk-othello', Othello);
 Vue.component('mk-welcome-timeline', welcomeTimeline);
 Vue.component('ui-input', uiInput);
 Vue.component('ui-button', uiButton);
+Vue.component('ui-group', uiGroup);
+Vue.component('ui-form', uiForm);
+Vue.component('ui-textarea', uiTextarea);
+Vue.component('ui-switch', uiSwitch);
diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue
index 3adb10f2c3..d1621a4848 100644
--- a/src/client/app/common/views/components/signup.vue
+++ b/src/client/app/common/views/components/signup.vue
@@ -1,50 +1,40 @@
 <template>
 <form class="mk-signup" @submit.prevent="onSubmit" autocomplete="off">
-	<label class="username">
-		<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" autocomplete="off" required @input="onChangeUsername">
-			<span>%i18n:@username%</span>
-			<span slot="prefix">@</span>
-			<span slot="suffix">@{{ host }}</span>
-			<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%%i18n:@checking%</p>
-			<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw%%i18n:@available%</p>
-			<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@unavailable%</p>
-			<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@error%</p>
-			<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@invalid-format%</p>
-			<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-short%</p>
-			<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-long%</p>
-		</ui-input>
-	</label>
-	<label class="password">
-		<ui-input v-model="password" type="password" autocomplete="off" required @input="onChangePassword">
-			<span>%i18n:@password%</span>
-			<span slot="prefix">%fa:lock%</span>
-			<div slot="text">
-
-			</div>
-		</ui-input>
-		<div class="meter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
-			<div class="value" ref="passwordMetar"></div>
+	<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" autocomplete="off" required @input="onChangeUsername">
+		<span>%i18n:@username%</span>
+		<span slot="prefix">@</span>
+		<span slot="suffix">@{{ host }}</span>
+		<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
+		<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
+		<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
+		<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
+		<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
+		<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
+		<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
+	</ui-input>
+	<ui-input v-model="password" type="password" autocomplete="off" required @input="onChangePassword" :with-password-meter="true">
+		<span>%i18n:@password%</span>
+		<span slot="prefix">%fa:lock%</span>
+		<div slot="text">
+			<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
+			<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
+			<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
 		</div>
-		<p class="info" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@weak-password%</p>
-		<p class="info" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw%%i18n:@normal-password%</p>
-		<p class="info" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw%%i18n:@strong-password%</p>
-	</label>
-	<label class="retype-password">
-		<ui-input v-model="retypedPassword" type="password" autocomplete="off" required @input="onChangePasswordRetype">
-			<span>%i18n:@password% (%i18n:@retype%)</span>
-			<span slot="prefix">%fa:lock%</span>
-		</ui-input>
-		<p class="info" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw%%i18n:@password-matched%</p>
-		<p class="info" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@password-not-matched%</p>
-	</label>
-	<label class="recaptcha">
-		<div class="g-recaptcha" :data-sitekey="recaptchaSitekey"></div>
-	</label>
-	<label class="agree-tou">
+	</ui-input>
+	<ui-input v-model="retypedPassword" type="password" autocomplete="off" required @input="onChangePasswordRetype">
+		<span>%i18n:@password% (%i18n:@retype%)</span>
+		<span slot="prefix">%fa:lock%</span>
+		<div slot="text">
+			<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
+			<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
+		</div>
+	</ui-input>
+	<div class="g-recaptcha" :data-sitekey="recaptchaSitekey" style="margin: 16px 0;"></div>
+	<label class="agree-tou" style="display: block; margin: 16px 0;">
 		<input name="agree-tou" type="checkbox" autocomplete="off" required/>
 		<p><a :href="touUrl" target="_blank">利用規約</a>に同意する</p>
 	</label>
-	<button type="submit">%i18n:@create%</button>
+	<ui-button type="submit">%i18n:@create%</ui-button>
 </form>
 </template>
 
@@ -112,7 +102,6 @@ export default Vue.extend({
 
 			const strength = getPasswordStrength(this.password);
 			this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
-			(this.$refs.passwordMetar as any).style.width = `${strength * 100}%`;
 		},
 		onChangePasswordRetype() {
 			if (this.retypedPassword == '') {
@@ -156,100 +145,6 @@ export default Vue.extend({
 .mk-signup
 	min-width 302px
 
-	label
-		display block
-		margin 0 0 16px 0
-
-		> .caption
-			margin 0 0 4px 0
-			color #828888
-			font-size 0.95em
-
-			> [data-fa]
-				margin-right 0.25em
-				color #96adac
-
-		> .info
-			display block
-			margin 4px 0
-			font-size 0.8em
-
-			> [data-fa]
-				margin-right 0.3em
-
-		&.username
-			.profile-page-url-preview
-				display block
-				margin 4px 8px 0 4px
-				font-size 0.8em
-				color #888
-
-				&:empty
-					display none
-
-				&:not(:empty) + .info
-					margin-top 0
-
-		&.password
-			.meter
-				display block
-				margin-top 8px
-				width 100%
-				height 8px
-
-				&[data-strength='']
-					display none
-
-				&[data-strength='low']
-					> .value
-						background #d73612
-
-				&[data-strength='medium']
-					> .value
-						background #d7ca12
-
-				&[data-strength='high']
-					> .value
-						background #61bb22
-
-				> .value
-					display block
-					width 0%
-					height 100%
-					background transparent
-					border-radius 4px
-					transition all 0.1s ease
-
-	[type=text], [type=password]
-		user-select text
-		display inline-block
-		cursor auto
-		padding 0 12px
-		margin 0
-		width 100%
-		line-height 44px
-		font-size 1em
-		color #333 !important
-		background #fff !important
-		outline none
-		border solid 1px rgba(#000, 0.1)
-		border-radius 4px
-		box-shadow 0 0 0 114514px #fff inset
-		transition all .3s ease
-
-		&:hover
-			border-color rgba(#000, 0.2)
-			transition all .1s ease
-
-		&:focus
-			color $theme-color !important
-			border-color $theme-color
-			box-shadow 0 0 0 1024px #fff inset, 0 0 0 4px rgba($theme-color, 10%)
-			transition all 0s ease
-
-		&:disabled
-			opacity 0.5
-
 	.agree-tou
 		padding 4px
 		border-radius 4px
@@ -267,19 +162,4 @@ export default Vue.extend({
 			display inline
 			color #555
 
-	button
-		margin 0
-		padding 16px
-		width 100%
-		font-size 1em
-		color #fff
-		background $theme-color
-		border-radius 3px
-
-		&:hover
-			background lighten($theme-color, 5%)
-
-		&:active
-			background darken($theme-color, 5%)
-
 </style>
diff --git a/src/client/app/common/views/components/material/button.vue b/src/client/app/common/views/components/ui/button.vue
similarity index 83%
rename from src/client/app/common/views/components/material/button.vue
rename to src/client/app/common/views/components/ui/button.vue
index 8dacedbac6..57747fd469 100644
--- a/src/client/app/common/views/components/material/button.vue
+++ b/src/client/app/common/views/components/ui/button.vue
@@ -25,7 +25,7 @@ export default Vue.extend({
 	> button
 		display block
 		width 100%
-		margin 32px 0 16px 0
+		margin 0
 		padding 0
 		color $theme-color-foreground
 		font-weight bold
@@ -37,4 +37,10 @@ export default Vue.extend({
 		outline none
 		box-shadow none
 
+		&:hover
+			background lighten($theme-color, 5%)
+
+		&:active
+			background darken($theme-color, 5%)
+
 </style>
diff --git a/src/client/app/common/views/components/ui/form.vue b/src/client/app/common/views/components/ui/form.vue
new file mode 100644
index 0000000000..0893af1bce
--- /dev/null
+++ b/src/client/app/common/views/components/ui/form.vue
@@ -0,0 +1,28 @@
+<template>
+<div class="ui-form">
+	<fieldset :disabled="disabled">
+		<slot></slot>
+	</fieldset>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	props: {
+		disabled: {
+			type: String,
+			required: false
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-form
+	> fieldset
+		border none
+
+</style>
diff --git a/src/client/app/common/views/components/ui/group.vue b/src/client/app/common/views/components/ui/group.vue
new file mode 100644
index 0000000000..fb29458ce8
--- /dev/null
+++ b/src/client/app/common/views/components/ui/group.vue
@@ -0,0 +1,23 @@
+<template>
+<div class="ui-group">
+	<header>
+		<slot name="title"></slot>
+	</header>
+
+	<slot></slot>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-group
+	> header
+		font-weight bold
+
+</style>
diff --git a/src/client/app/common/views/components/ui/input.vue b/src/client/app/common/views/components/ui/input.vue
new file mode 100644
index 0000000000..7461aac7fe
--- /dev/null
+++ b/src/client/app/common/views/components/ui/input.vue
@@ -0,0 +1,215 @@
+<template>
+<div class="ui-input" :class="{ focused, filled }">
+	<div class="input" @click="focus">
+		<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
+			<div class="value" ref="passwordMetar"></div>
+		</div>
+		<span class="label" ref="label"><slot></slot></span>
+		<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
+		<input ref="input"
+				:type="type"
+				:value="value"
+				:required="required"
+				:readonly="readonly"
+				:pattern="pattern"
+				:autocomplete="autocomplete"
+				@input="$emit('input', $event.target.value)"
+				@focus="focused = true"
+				@blur="focused = false">
+		<div class="suffix"><slot name="suffix"></slot></div>
+	</div>
+	<div class="text"><slot name="text"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+const getPasswordStrength = require('syuilo-password-strength');
+
+export default Vue.extend({
+	props: {
+		value: {
+			required: false
+		},
+		type: {
+			type: String,
+			required: false
+		},
+		required: {
+			type: Boolean,
+			required: false
+		},
+		readonly: {
+			type: Boolean,
+			required: false
+		},
+		pattern: {
+			type: String,
+			required: false
+		},
+		autocomplete: {
+			type: String,
+			required: false
+		},
+		withPasswordMeter: {
+			type: Boolean,
+			required: false,
+			default: false
+		}
+	},
+	data() {
+		return {
+			focused: false,
+			passwordStrength: ''
+		}
+	},
+	computed: {
+		filled(): boolean {
+			return this.value != '' && this.value != null;
+		}
+	},
+	watch: {
+		value(v) {
+			if (this.withPasswordMeter) {
+				if (v == '') {
+					this.passwordStrength = '';
+					return;
+				}
+
+				const strength = getPasswordStrength(v);
+				this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
+				(this.$refs.passwordMetar as any).style.width = `${strength * 100}%`;
+			}
+		}
+	},
+	mounted() {
+		if (this.$refs.prefix) {
+			this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
+		}
+	},
+	methods: {
+		focus() {
+			this.$refs.input.focus();
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-input
+	margin 32px 0
+
+	> .input
+		display flex
+		padding 6px 12px
+		background rgba(#000, 0.035)
+		border-radius 6px
+
+		> .password-meter
+			position absolute
+			top 0
+			left 0
+			width 100%
+			height 100%
+			border-radius 6px
+			overflow hidden
+			opacity 0.3
+
+			&[data-strength='']
+				display none
+
+			&[data-strength='low']
+				> .value
+					background #d73612
+
+			&[data-strength='medium']
+				> .value
+					background #d7ca12
+
+			&[data-strength='high']
+				> .value
+					background #61bb22
+
+			> .value
+				display block
+				width 0%
+				height 100%
+				background transparent
+				border-radius 6px
+				transition all 0.1s ease
+
+		> .label
+			position absolute
+			top 6px
+			left 0
+			pointer-events none
+			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
+			transition-duration 0.3s
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+			pointer-events none
+			//will-change transform
+			transform-origin top left
+			transform scale(1)
+
+		> input
+			display block
+			flex 1
+			width 100%
+			padding 0
+			font inherit
+			font-weight bold
+			font-size 16px
+			line-height 32px
+			background transparent
+			border none
+			border-radius 0
+			outline none
+			box-shadow none
+
+		> .prefix
+		> .suffix
+			display block
+			align-self center
+			justify-self center
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+			pointer-events none
+
+			> *
+				display block
+				min-width 16px
+
+		> .prefix
+			padding-right 4px
+
+		> .suffix
+			padding-left 4px
+
+	> .text
+		margin 6px 0
+		font-size 13px
+
+		*
+			margin 0
+
+	&.focused
+		> .input
+			background rgba(#000, 0.05)
+
+			> .label
+				color $theme-color
+
+	&.focused
+	&.filled
+		> .input
+			> .label
+				top -24px
+				left 0 !important
+				transform scale(0.8)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
new file mode 100644
index 0000000000..2cac6262f1
--- /dev/null
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -0,0 +1,199 @@
+<template>
+<div
+	class="ui-switch"
+	:class="{ disabled, checked }"
+	role="switch"
+	:aria-checked="checked"
+	:aria-disabled="disabled"
+	@click="switchValue"
+	@mouseover="mouseenter"
+>
+	<input
+		type="checkbox"
+		@change="handleChange"
+		ref="input"
+		:disabled="disabled"
+		@keydown.enter="switchValue"
+	>
+	<span class="button">
+		<span :style="{ transform }"></span>
+	</span>
+	<span class="label">
+		<span :aria-hidden="!checked"><slot></slot></span>
+		<p :aria-hidden="!checked">
+			<slot name="text"></slot>
+		</p>
+	</span>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	props: {
+		value: {
+			type: Boolean,
+			default: false
+		},
+		disabled: {
+			type: Boolean,
+			default: false
+		}
+	},/*
+	created() {
+		if (!~[true, false].indexOf(this.value)) {
+			this.$emit('input', false);
+		}
+	},*/
+	computed: {
+		checked(): boolean {
+			return this.value;
+		},
+		transform(): string {
+			return this.checked ? 'translate3d(14px, 0, 0)' : '';
+		}
+	},
+	watch: {
+		value() {
+			(this.$el).style.transition = 'all 0.3s';
+			(this.$refs.input as any).checked = this.checked;
+		}
+	},
+	mounted() {
+		(this.$refs.input as any).checked = this.checked;
+	},
+	methods: {
+		mouseenter() {
+			(this.$el).style.transition = 'all 0s';
+		},
+		handleChange() {
+			(this.$el).style.transition = 'all 0.3s';
+			this.$emit('input', !this.checked);
+			this.$emit('change', !this.checked);
+			this.$nextTick(() => {
+				// set input's checked property
+				// in case parent refuses to change component's value
+				(this.$refs.input as any).checked = this.checked;
+			});
+		},
+		switchValue() {
+			!this.disabled && this.handleChange();
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark)
+	display flex
+	margin 16px 0
+	cursor pointer
+	transition all 0.3s
+
+	> *
+		user-select none
+
+	&.disabled
+		opacity 0.6
+		cursor not-allowed
+
+	&.checked
+		> .button
+			background-color $theme-color
+			border-color $theme-color
+
+		> .label
+			> span
+				color $theme-color
+
+		&:hover
+			> .label
+				> span
+					color darken($theme-color, 10%)
+
+			> .button
+				background darken($theme-color, 10%)
+				border-color darken($theme-color, 10%)
+
+	&:hover
+		> .label
+			> span
+				color isDark ? #fff : #2e3338
+
+		> .button
+			$color = isDark ? #15181d : #ced2da
+			background $color
+			border-color $color
+
+	> input
+		position absolute
+		width 0
+		height 0
+		opacity 0
+		margin 0
+
+		&:focus + .button
+			&:after
+				content ""
+				pointer-events none
+				position absolute
+				top -5px
+				right -5px
+				bottom -5px
+				left -5px
+				border 2px solid rgba($theme-color, 0.3)
+				border-radius 14px
+
+	> .button
+		$color = isDark ? #1c1f25 : #dcdfe6
+
+		display inline-block
+		margin 0
+		width 46px
+		min-width 46px
+		height 32px
+		min-height 32px
+		background $color
+		border 1px solid $color
+		outline none
+		border-radius 6px
+		transition inherit
+
+		> *
+			position absolute
+			top 1px
+			left 1px
+			border-radius 6px
+			transition transform 0.3s
+			width 28px
+			height 28px
+			background-color #fff
+
+	> .label
+		margin-left 8px
+		display block
+		font-size 16px
+		cursor pointer
+		transition inherit
+
+		> span
+			display block
+			line-height 32px
+			font-weight bold
+			color isDark ? #c4ccd2 : #4a535a
+			transition inherit
+
+		> p
+			margin 0
+			//font-size 90%
+			color isDark ? #78858e : #9daab3
+
+.ui-switch[data-darkmode]
+	root(true)
+
+.ui-switch:not([data-darkmode])
+	root(false)
+
+</style>
diff --git a/src/client/app/common/views/components/material/input.vue b/src/client/app/common/views/components/ui/textarea.vue
similarity index 64%
rename from src/client/app/common/views/components/material/input.vue
rename to src/client/app/common/views/components/ui/textarea.vue
index 6564b3aa6c..0a9f60f1bc 100644
--- a/src/client/app/common/views/components/material/input.vue
+++ b/src/client/app/common/views/components/ui/textarea.vue
@@ -1,17 +1,17 @@
 <template>
-<div class="ui-input" :class="{ focused, filled }">
+<div class="ui-textarea" :class="{ focused, filled }">
 	<div class="input">
 		<span class="label" ref="label"><slot></slot></span>
-		<div class="prefix" ref="prefix" @click="focus"><slot name="prefix"></slot></div>
-		<input ref="input"
-				:type="type"
+		<textarea ref="input"
 				:value="value"
 				:required="required"
 				:readonly="readonly"
+				:pattern="pattern"
+				:autocomplete="autocomplete"
 				@input="$emit('input', $event.target.value)"
 				@focus="focused = true"
 				@blur="focused = false">
-		<div class="suffix" @click="focus"><slot name="suffix"></slot></div>
+		</textarea>
 	</div>
 	<div class="text"><slot name="text"></slot></div>
 </div>
@@ -19,15 +19,13 @@
 
 <script lang="ts">
 import Vue from 'vue';
+const getPasswordStrength = require('syuilo-password-strength');
+
 export default Vue.extend({
 	props: {
 		value: {
 			required: false
 		},
-		type: {
-			type: String,
-			required: false
-		},
 		required: {
 			type: Boolean,
 			required: false
@@ -35,11 +33,20 @@ export default Vue.extend({
 		readonly: {
 			type: Boolean,
 			required: false
+		},
+		pattern: {
+			type: String,
+			required: false
+		},
+		autocomplete: {
+			type: String,
+			required: false
 		}
 	},
 	data() {
 		return {
-			focused: false
+			focused: false,
+			passwordStrength: ''
 		}
 	},
 	computed: {
@@ -47,9 +54,6 @@ export default Vue.extend({
 			return this.value != '' && this.value != null;
 		}
 	},
-	mounted() {
-		this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
-	},
 	methods: {
 		focus() {
 			this.$refs.input.focus();
@@ -61,20 +65,18 @@ export default Vue.extend({
 <style lang="stylus" scoped>
 @import '~const.styl'
 
-.ui-input
-	margin-bottom 16px
-	padding-top 16px
+.ui-textarea
+	margin 32px 0
 
 	> .input
-		display flex
-		padding 6px 12px
+		padding 12px
 		background rgba(#000, 0.035)
 		border-radius 6px
 
 		> .label
 			position absolute
 			top 6px
-			left 0
+			left 12px
 			pointer-events none
 			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
 			transition-duration 0.3s
@@ -82,42 +84,29 @@ export default Vue.extend({
 			line-height 32px
 			color rgba(#000, 0.54)
 			pointer-events none
+			//will-change transform
+			transform-origin top left
+			transform scale(1)
 
-		> input
+		> textarea
 			display block
-			flex 1
 			width 100%
+			min-height 100px
 			padding 0
 			font inherit
 			font-weight bold
 			font-size 16px
-			line-height 32px
 			background transparent
 			border none
 			border-radius 0
 			outline none
 			box-shadow none
 
-		> .prefix
-		> .suffix
-			display block
-			align-self center
-			justify-self center
-			font-size 16px
-			line-height 32px
-			color rgba(#000, 0.54)
-
-		> .prefix
-			padding-right 4px
-
-		> .suffix
-			padding-left 4px
-
 	> .text
-		margin 8px 0
-		font-size 14px
+		margin 6px 0
+		font-size 13px
 
-		> p
+		*
 			margin 0
 
 	&.focused
@@ -131,9 +120,8 @@ export default Vue.extend({
 	&.filled
 		> .input
 			> .label
-				top -20px
+				top -24px
 				left 0 !important
-				font-size 12px
-				line-height 20px
+				transform scale(0.8)
 
 </style>
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 8da7a76633..716f7afc0c 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -6,125 +6,105 @@
 		<div>
 			<x-profile/>
 
-			<md-card>
-				<md-card-header>
-					<div class="md-title">%fa:palette% %i18n:@design%</div>
-				</md-card-header>
+			<ui-group>
+				<div slot="title">%fa:palette% %i18n:@design%</div>
+
+				<div>
+					<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
+				</div>
+
+				<div>
+					<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
+				</div>
+
+				<div>
+					<div class="md-body-2">%i18n:@timeline%</div>
 
-				<md-card-content>
 					<div>
-						<md-switch v-model="darkmode">%i18n:@dark-mode%</md-switch>
+						<ui-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</ui-switch>
 					</div>
 
 					<div>
-						<md-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</md-switch>
+						<ui-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</ui-switch>
 					</div>
 
 					<div>
-						<div class="md-body-2">%i18n:@timeline%</div>
-
-						<div>
-							<md-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</md-switch>
-						</div>
-
-						<div>
-							<md-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</md-switch>
-						</div>
-
-						<div>
-							<md-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</md-switch>
-						</div>
+						<ui-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
 					</div>
+				</div>
 
-					<div>
-						<div class="md-body-2">%i18n:@post-style%</div>
+				<div>
+					<div class="md-body-2">%i18n:@post-style%</div>
 
-						<md-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</md-radio>
-						<md-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</md-radio>
-					</div>
-				</md-card-content>
-			</md-card>
+					<md-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</md-radio>
+					<md-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</md-radio>
+				</div>
+			</ui-group>
 
-			<md-card>
-				<md-card-header>
-					<div class="md-title">%fa:cog% %i18n:@behavior%</div>
-				</md-card-header>
+			<ui-group>
+				<div slot="title">%fa:cog% %i18n:@behavior%</div>
 
-				<md-card-content>
-					<div>
-						<md-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll">%i18n:@fetch-on-scroll%</md-switch>
-					</div>
+				<div>
+					<ui-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch>
+				</div>
 
-					<div>
-						<md-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile">%i18n:@disable-via-mobile%</md-switch>
-					</div>
+				<div>
+					<ui-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile">%i18n:@disable-via-mobile%</ui-switch>
+				</div>
 
-					<div>
-						<md-switch v-model="loadRawImages">%i18n:@load-raw-images%</md-switch>
-					</div>
+				<div>
+					<ui-switch v-model="loadRawImages">%i18n:@load-raw-images%</ui-switch>
+				</div>
 
-					<div>
-						<md-switch v-model="$store.state.settings.loadRemoteMedia" @change="onChangeLoadRemoteMedia">%i18n:@load-remote-media%</md-switch>
-					</div>
+				<div>
+					<ui-switch v-model="$store.state.settings.loadRemoteMedia" @change="onChangeLoadRemoteMedia">%i18n:@load-remote-media%</ui-switch>
+				</div>
 
-					<div>
-						<md-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</md-switch>
-					</div>
-				</md-card-content>
-			</md-card>
+				<div>
+					<ui-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</ui-switch>
+				</div>
+			</ui-group>
 
-			<md-card>
-				<md-card-header>
-					<div class="md-title">%fa:language% %i18n:@lang%</div>
-				</md-card-header>
+			<ui-group>
+				<div slot="title">%fa:language% %i18n:@lang%</div>
 
-				<md-card-content>
-					<md-field>
-						<md-select v-model="lang" placeholder="%i18n:@auto%">
-							<md-optgroup label="%i18n:@recommended%">
-								<md-option value="">%i18n:@auto%</md-option>
-							</md-optgroup>
+				<md-field>
+					<md-select v-model="lang" placeholder="%i18n:@auto%">
+						<md-optgroup label="%i18n:@recommended%">
+							<md-option value="">%i18n:@auto%</md-option>
+						</md-optgroup>
 
-							<md-optgroup label="%i18n:@specify-language%">
-								<md-option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</md-option>
-							</md-optgroup>
-						</md-select>
-					</md-field>
-					<span class="md-helper-text">%fa:info-circle% %i18n:@lang-tip%</span>
-				</md-card-content>
-			</md-card>
+						<md-optgroup label="%i18n:@specify-language%">
+							<md-option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</md-option>
+						</md-optgroup>
+					</md-select>
+				</md-field>
+				<span class="md-helper-text">%fa:info-circle% %i18n:@lang-tip%</span>
+			</ui-group>
 
-			<md-card>
-				<md-card-header>
-					<div class="md-title">%fa:B twitter% %i18n:@twitter%</div>
-				</md-card-header>
+			<ui-group>
+				<div slot="title">%fa:B twitter% %i18n:@twitter%</div>
 
-				<md-card-content>
-					<p class="account" v-if="$store.state.i.twitter"><a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
-					<p>
-						<a :href="`${apiUrl}/connect/twitter`" target="_blank">{{ $store.state.i.twitter ? '%i18n:@twitter-reconnect%' : '%i18n:@twitter-connect%' }}</a>
-						<span v-if="$store.state.i.twitter"> or </span>
-						<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="$store.state.i.twitter">%i18n:@twitter-disconnect%</a>
-					</p>
-				</md-card-content>
-			</md-card>
+				<p class="account" v-if="$store.state.i.twitter"><a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
+				<p>
+					<a :href="`${apiUrl}/connect/twitter`" target="_blank">{{ $store.state.i.twitter ? '%i18n:@twitter-reconnect%' : '%i18n:@twitter-connect%' }}</a>
+					<span v-if="$store.state.i.twitter"> or </span>
+					<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="$store.state.i.twitter">%i18n:@twitter-disconnect%</a>
+				</p>
+			</ui-group>
 
-			<md-card>
-				<md-card-header>
-					<div class="md-title">%fa:sync-alt% %i18n:@update%</div>
-				</md-card-header>
+			<ui-group>
+				<div slot="title">%fa:sync-alt% %i18n:@update%</div>
 
-				<md-card-content>
-					<div>%i18n:@version% <i>{{ version }}</i></div>
-					<template v-if="latestVersion !== undefined">
-						<div>%i18n:@latest-version% <i>{{ latestVersion ? latestVersion : version }}</i></div>
-					</template>
-					<md-button class="md-raised md-primary" @click="checkForUpdate" :disabled="checkingForUpdate">
-						<template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
-						<template v-else>%i18n:@check-for-updates%</template>
-					</md-button>
-				</md-card-content>
-			</md-card>
+				<div>%i18n:@version% <i>{{ version }}</i></div>
+				<template v-if="latestVersion !== undefined">
+					<div>%i18n:@latest-version% <i>{{ latestVersion ? latestVersion : version }}</i></div>
+				</template>
+				<md-button class="md-raised md-primary" @click="checkForUpdate" :disabled="checkingForUpdate">
+					<template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
+					<template v-else>%i18n:@check-for-updates%</template>
+				</md-button>
+			</ui-group>
 		</div>
 		<p><small>ver {{ version }} ({{ codename }})</small></p>
 	</main>
diff --git a/src/client/app/mobile/views/pages/settings/settings.profile.vue b/src/client/app/mobile/views/pages/settings/settings.profile.vue
index f3444eb1f0..73d876d14e 100644
--- a/src/client/app/mobile/views/pages/settings/settings.profile.vue
+++ b/src/client/app/mobile/views/pages/settings/settings.profile.vue
@@ -1,62 +1,47 @@
 <template>
-	<md-card>
-		<md-card-header>
-			<div class="md-title">%fa:pencil-alt% %i18n:@title%</div>
-		</md-card-header>
+<ui-group>
+	<div slot="title">%fa:pencil-alt% %i18n:@title%</div>
 
-		<md-card-content>
-			<md-field>
-				<label>%i18n:@name%</label>
-				<md-input v-model="name" :disabled="saving" md-counter="30"/>
-			</md-field>
+	<ui-form :disabled="saving">
+		<ui-input v-model="name" :max="30">
+			<span>%i18n:@name%</span>
+		</ui-input>
 
-			<md-field>
-				<label>%i18n:@account%</label>
-				<span class="md-prefix">@</span>
-				<md-input v-model="username" readonly></md-input>
-				<span class="md-suffix">@{{ host }}</span>
-			</md-field>
+		<ui-input v-model="username" readonly>
+			<span>%i18n:@account%</span>
+			<span slot="prefix">@</span>
+			<span slot="suffix">@{{ host }}</span>
+		</ui-input>
 
-			<md-field>
-				<md-icon>%fa:map-marker-alt%</md-icon>
-				<label>%i18n:@location%</label>
-				<md-input v-model="location" :disabled="saving"/>
-			</md-field>
+		<ui-input v-model="location">
+			<span>%i18n:@location%</span>
+			<span slot="prefix">%fa:map-marker-alt%</span>
+		</ui-input>
 
-			<md-field>
-				<md-icon>%fa:birthday-cake%</md-icon>
-				<label>%i18n:@birthday%</label>
-				<md-input type="date" v-model="birthday" :disabled="saving"/>
-			</md-field>
+		<ui-input v-model="birthday" type="date">
+			<span>%i18n:@birthday%</span>
+			<span slot="prefix">%fa:birthday-cake%</span>
+		</ui-input>
 
-			<md-field>
-				<label>%i18n:@description%</label>
-				<md-textarea v-model="description" :disabled="saving" md-counter="500"/>
-			</md-field>
+		<ui-textarea v-model="description" :max="500">
+			<span>%i18n:@description%</span>
+		</ui-textarea>
 
-			<md-field>
-				<label>%i18n:@avatar%</label>
-				<md-file @md-change="onAvatarChange"/>
-			</md-field>
+		<ui-input type="file" @change="onAvatarChange">
+			<span>%i18n:@avatar%</span>
+			<span slot="prefix">%fa:picture-o%</span>
+		</ui-input>
 
-			<md-field>
-				<label>%i18n:@banner%</label>
-				<md-file @md-change="onBannerChange"/>
-			</md-field>
+		<ui-input type="file" @change="onBannerChange">
+			<span>%i18n:@banner%</span>
+			<span slot="prefix">%fa:picture-o%</span>
+		</ui-input>
 
-			<md-dialog-alert
-					:md-active.sync="uploading"
-					md-content="%18n:!@uploading%"/>
+		<ui-switch v-model="isCat">%i18n:@is-cat%</ui-switch>
 
-			<div>
-				<md-switch v-model="isCat">%i18n:@is-cat%</md-switch>
-			</div>
-		</md-card-content>
-
-		<md-card-actions>
-			<md-button class="md-primary" :disabled="saving" @click="save">%i18n:@save%</md-button>
-		</md-card-actions>
-	</md-card>
+		<ui-button @click="save">%i18n:@save%</ui-button>
+	</ui-form>
+</ui-group>
 </template>
 
 <script lang="ts">
diff --git a/src/client/app/mobile/views/pages/signup.vue b/src/client/app/mobile/views/pages/signup.vue
index f2b29bca60..47384e2b3c 100644
--- a/src/client/app/mobile/views/pages/signup.vue
+++ b/src/client/app/mobile/views/pages/signup.vue
@@ -18,7 +18,7 @@ export default Vue.extend({});
 
 	h1
 		margin 0
-		padding 8px
+		padding 8px 0 0 0
 		font-size 1.5em
 		font-weight bold
 		color #444
diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue
index 01b20aa472..3b37a185bd 100644
--- a/src/client/app/mobile/views/pages/welcome.vue
+++ b/src/client/app/mobile/views/pages/welcome.vue
@@ -10,16 +10,16 @@
 		</div>
 		<div class="login">
 			<form @submit.prevent="onSubmit">
-				<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange">
+				<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" autofocus required @change="onUsernameChange">
 					<span>ユーザー名</span>
 					<span slot="prefix">@</span>
 					<span slot="suffix">@{{ host }}</span>
 				</ui-input>
-				<ui-input v-model="password" type="password" placeholder="パスワード" required>
+				<ui-input v-model="password" type="password" required>
 					<span>パスワード</span>
 					<span slot="prefix">%fa:lock%</span>
 				</ui-input>
-				<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
+				<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
 				<ui-button type="submit" :disabled="signing">{{ signing ? 'ログインしています' : 'ログイン' }}</ui-button>
 			</form>
 			<div>
@@ -113,7 +113,7 @@ export default Vue.extend({
 		> .about
 			margin-top 16px
 			padding 16px
-			color #444
+			color #555
 			background #fff
 			border-radius 6px
 

From 120c11b1817429c762d54df865b8cf1a35bf4e08 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 16:48:49 +0900
Subject: [PATCH 05/11] wip

---
 .../app/common/views/components/index.ts      |  4 +-
 .../components/ui/{group.vue => card.vue}     | 11 ++-
 .../app/common/views/components/ui/form.vue   |  2 +
 .../app/common/views/components/ui/switch.vue | 80 +++++--------------
 .../app/mobile/views/pages/settings.vue       | 31 ++++---
 .../views/pages/settings/settings.profile.vue |  6 +-
 6 files changed, 51 insertions(+), 83 deletions(-)
 rename src/client/app/common/views/components/ui/{group.vue => card.vue} (56%)

diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 060af388b3..0e20e66f5a 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -31,7 +31,7 @@ import Othello from './othello.vue';
 import welcomeTimeline from './welcome-timeline.vue';
 import uiInput from './ui/input.vue';
 import uiButton from './ui/button.vue';
-import uiGroup from './ui/group.vue';
+import uiCard from './ui/card.vue';
 import uiForm from './ui/form.vue';
 import uiTextarea from './ui/textarea.vue';
 import uiSwitch from './ui/switch.vue';
@@ -67,7 +67,7 @@ Vue.component('mk-othello', Othello);
 Vue.component('mk-welcome-timeline', welcomeTimeline);
 Vue.component('ui-input', uiInput);
 Vue.component('ui-button', uiButton);
-Vue.component('ui-group', uiGroup);
+Vue.component('ui-card', uiCard);
 Vue.component('ui-form', uiForm);
 Vue.component('ui-textarea', uiTextarea);
 Vue.component('ui-switch', uiSwitch);
diff --git a/src/client/app/common/views/components/ui/group.vue b/src/client/app/common/views/components/ui/card.vue
similarity index 56%
rename from src/client/app/common/views/components/ui/group.vue
rename to src/client/app/common/views/components/ui/card.vue
index fb29458ce8..5ba15dad7d 100644
--- a/src/client/app/common/views/components/ui/group.vue
+++ b/src/client/app/common/views/components/ui/card.vue
@@ -1,5 +1,5 @@
 <template>
-<div class="ui-group">
+<div class="ui-card">
 	<header>
 		<slot name="title"></slot>
 	</header>
@@ -16,8 +16,15 @@ export default Vue.extend({});
 <style lang="stylus" scoped>
 @import '~const.styl'
 
-.ui-group
+.ui-card
+	margin 16px 0
+	padding 32px
+	background #fff
+	//box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
+
 	> header
 		font-weight bold
+		font-size 28px
+		color #444
 
 </style>
diff --git a/src/client/app/common/views/components/ui/form.vue b/src/client/app/common/views/components/ui/form.vue
index 0893af1bce..b6b4a76d2a 100644
--- a/src/client/app/common/views/components/ui/form.vue
+++ b/src/client/app/common/views/components/ui/form.vue
@@ -23,6 +23,8 @@ export default Vue.extend({
 
 .ui-form
 	> fieldset
+		margin 0
+		padding 0
 		border none
 
 </style>
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
index 2cac6262f1..e78951a352 100644
--- a/src/client/app/common/views/components/ui/switch.vue
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -16,7 +16,7 @@
 		@keydown.enter="switchValue"
 	>
 	<span class="button">
-		<span :style="{ transform }"></span>
+		<span></span>
 	</span>
 	<span class="label">
 		<span :aria-hidden="!checked"><slot></slot></span>
@@ -48,9 +48,6 @@ export default Vue.extend({
 	computed: {
 		checked(): boolean {
 			return this.value;
-		},
-		transform(): string {
-			return this.checked ? 'translate3d(14px, 0, 0)' : '';
 		}
 	},
 	watch: {
@@ -88,7 +85,7 @@ export default Vue.extend({
 
 root(isDark)
 	display flex
-	margin 16px 0
+	margin 32px 0
 	cursor pointer
 	transition all 0.3s
 
@@ -101,31 +98,12 @@ root(isDark)
 
 	&.checked
 		> .button
-			background-color $theme-color
-			border-color $theme-color
+			background-color rgba($theme-color, 0.4)
+			border-color rgba($theme-color, 0.4)
 
-		> .label
-			> span
-				color $theme-color
-
-		&:hover
-			> .label
-				> span
-					color darken($theme-color, 10%)
-
-			> .button
-				background darken($theme-color, 10%)
-				border-color darken($theme-color, 10%)
-
-	&:hover
-		> .label
-			> span
-				color isDark ? #fff : #2e3338
-
-		> .button
-			$color = isDark ? #15181d : #ced2da
-			background $color
-			border-color $color
+			> *
+				background-color $theme-color
+				transform translateX(14px)
 
 	> input
 		position absolute
@@ -134,42 +112,26 @@ root(isDark)
 		opacity 0
 		margin 0
 
-		&:focus + .button
-			&:after
-				content ""
-				pointer-events none
-				position absolute
-				top -5px
-				right -5px
-				bottom -5px
-				left -5px
-				border 2px solid rgba($theme-color, 0.3)
-				border-radius 14px
-
 	> .button
-		$color = isDark ? #1c1f25 : #dcdfe6
-
 		display inline-block
-		margin 0
-		width 46px
-		min-width 46px
-		height 32px
-		min-height 32px
-		background $color
-		border 1px solid $color
+		margin 3px 0 0 0
+		width 34px
+		height 14px
+		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.05)
 		outline none
-		border-radius 6px
+		border-radius 14px
 		transition inherit
 
 		> *
 			position absolute
-			top 1px
-			left 1px
-			border-radius 6px
-			transition transform 0.3s
-			width 28px
-			height 28px
+			top -3px
+			left 0
+			border-radius 100%
+			transition background-color 0.3s, transform 0.3s
+			width 20px
+			height 20px
 			background-color #fff
+			box-shadow 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12)
 
 	> .label
 		margin-left 8px
@@ -180,9 +142,9 @@ root(isDark)
 
 		> span
 			display block
-			line-height 32px
+			line-height 20px
 			font-weight bold
-			color isDark ? #c4ccd2 : #4a535a
+			color isDark ? #c4ccd2 : rgba(#000, 0.75)
 			transition inherit
 
 		> p
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 716f7afc0c..25cfbf732b 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -6,7 +6,7 @@
 		<div>
 			<x-profile/>
 
-			<ui-group>
+			<ui-card>
 				<div slot="title">%fa:palette% %i18n:@design%</div>
 
 				<div>
@@ -39,9 +39,9 @@
 					<md-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</md-radio>
 					<md-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</md-radio>
 				</div>
-			</ui-group>
+			</ui-card>
 
-			<ui-group>
+			<ui-card>
 				<div slot="title">%fa:cog% %i18n:@behavior%</div>
 
 				<div>
@@ -63,9 +63,9 @@
 				<div>
 					<ui-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</ui-switch>
 				</div>
-			</ui-group>
+			</ui-card>
 
-			<ui-group>
+			<ui-card>
 				<div slot="title">%fa:language% %i18n:@lang%</div>
 
 				<md-field>
@@ -80,9 +80,9 @@
 					</md-select>
 				</md-field>
 				<span class="md-helper-text">%fa:info-circle% %i18n:@lang-tip%</span>
-			</ui-group>
+			</ui-card>
 
-			<ui-group>
+			<ui-card>
 				<div slot="title">%fa:B twitter% %i18n:@twitter%</div>
 
 				<p class="account" v-if="$store.state.i.twitter"><a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
@@ -91,9 +91,9 @@
 					<span v-if="$store.state.i.twitter"> or </span>
 					<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="$store.state.i.twitter">%i18n:@twitter-disconnect%</a>
 				</p>
-			</ui-group>
+			</ui-card>
 
-			<ui-group>
+			<ui-card>
 				<div slot="title">%fa:sync-alt% %i18n:@update%</div>
 
 				<div>%i18n:@version% <i>{{ version }}</i></div>
@@ -104,7 +104,7 @@
 					<template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
 					<template v-else>%i18n:@check-for-updates%</template>
 				</md-button>
-			</ui-group>
+			</ui-card>
 		</div>
 		<p><small>ver {{ version }} ({{ codename }})</small></p>
 	</main>
@@ -247,20 +247,17 @@ export default Vue.extend({
 
 <style lang="stylus" scoped>
 root(isDark)
-	padding 0 16px
 	margin 0 auto
 	max-width 500px
 	width 100%
 
-	> div
-		> *
-			margin-bottom 16px
-
 	> p
 		display block
-		margin 24px
+		margin 16px 0
+		padding 16px
 		text-align center
-		color isDark ? #cad2da : #a2a9b1
+		color isDark ? #cad2da : #2c662d
+		background #fcfff5
 
 main[data-darkmode]
 	root(true)
diff --git a/src/client/app/mobile/views/pages/settings/settings.profile.vue b/src/client/app/mobile/views/pages/settings/settings.profile.vue
index 73d876d14e..de891b5736 100644
--- a/src/client/app/mobile/views/pages/settings/settings.profile.vue
+++ b/src/client/app/mobile/views/pages/settings/settings.profile.vue
@@ -1,6 +1,6 @@
 <template>
-<ui-group>
-	<div slot="title">%fa:pencil-alt% %i18n:@title%</div>
+<ui-card>
+	<div slot="title">%fa:user% %i18n:@title%</div>
 
 	<ui-form :disabled="saving">
 		<ui-input v-model="name" :max="30">
@@ -41,7 +41,7 @@
 
 		<ui-button @click="save">%i18n:@save%</ui-button>
 	</ui-form>
-</ui-group>
+</ui-card>
 </template>
 
 <script lang="ts">

From c5e9b69eb3b235e9828371affe0d8d88a780d096 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 18:53:02 +0900
Subject: [PATCH 06/11] wip

---
 .../app/common/views/components/ui/card.vue   |  14 +-
 .../app/common/views/components/ui/input.vue  | 147 +++++++++++++++---
 .../app/common/views/components/ui/switch.vue |   1 -
 .../common/views/components/ui/textarea.vue   |  56 ++++++-
 .../views/pages/settings/settings.profile.vue |   4 +-
 5 files changed, 188 insertions(+), 34 deletions(-)

diff --git a/src/client/app/common/views/components/ui/card.vue b/src/client/app/common/views/components/ui/card.vue
index 5ba15dad7d..97f06ca655 100644
--- a/src/client/app/common/views/components/ui/card.vue
+++ b/src/client/app/common/views/components/ui/card.vue
@@ -10,17 +10,23 @@
 
 <script lang="ts">
 import Vue from 'vue';
-export default Vue.extend({});
+export default Vue.extend({
+	provide() {
+		return {
+			isCardChild: true
+		};
+	}
+});
 </script>
 
 <style lang="stylus" scoped>
 @import '~const.styl'
 
 .ui-card
-	margin 16px 0
-	padding 32px
+	margin 16px
+	padding 16px
 	background #fff
-	//box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
+	box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
 
 	> header
 		font-weight bold
diff --git a/src/client/app/common/views/components/ui/input.vue b/src/client/app/common/views/components/ui/input.vue
index 7461aac7fe..3a474f0243 100644
--- a/src/client/app/common/views/components/ui/input.vue
+++ b/src/client/app/common/views/components/ui/input.vue
@@ -1,21 +1,35 @@
 <template>
-<div class="ui-input" :class="{ focused, filled }">
+<div class="ui-input" :class="[{ focused, filled }, styl]">
+	<div class="icon" ref="icon"><slot name="icon"></slot></div>
 	<div class="input" @click="focus">
 		<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
 			<div class="value" ref="passwordMetar"></div>
 		</div>
 		<span class="label" ref="label"><slot></slot></span>
 		<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
-		<input ref="input"
-				:type="type"
-				:value="value"
-				:required="required"
-				:readonly="readonly"
-				:pattern="pattern"
-				:autocomplete="autocomplete"
-				@input="$emit('input', $event.target.value)"
-				@focus="focused = true"
-				@blur="focused = false">
+		<template v-if="type != 'file'">
+			<input ref="input"
+					:type="type"
+					:value="v"
+					:required="required"
+					:readonly="readonly"
+					:pattern="pattern"
+					:autocomplete="autocomplete"
+					@input="$emit('input', $event.target.value)"
+					@focus="focused = true"
+					@blur="focused = false">
+		</template>
+		<template v-else>
+			<input ref="input"
+					type="text"
+					:value="placeholder"
+					readonly
+					@click="chooseFile">
+			<input ref="file"
+					type="file"
+					:value="value"
+					@change="onChangeFile">
+		</template>
 		<div class="suffix"><slot name="suffix"></slot></div>
 	</div>
 	<div class="text"><slot name="text"></slot></div>
@@ -59,17 +73,34 @@ export default Vue.extend({
 	},
 	data() {
 		return {
+			v: this.value,
 			focused: false,
-			passwordStrength: ''
-		}
+			passwordStrength: '',
+			styl: 'fill'
+		};
 	},
 	computed: {
 		filled(): boolean {
-			return this.value != '' && this.value != null;
+			return this.v != '' && this.v != null;
+		},
+		placeholder(): string {
+			if (this.type != 'file') return null;
+			if (this.v == null) return null;
+
+			if (typeof this.v == 'string') return this.v;
+
+			if (Array.isArray(this.v)) {
+				return this.v.map(file => file.name).join(', ');
+			} else {
+				return this.v.name;
+			}
 		}
 	},
 	watch: {
 		value(v) {
+			this.v = v;
+		},
+		v(v) {
 			if (this.withPasswordMeter) {
 				if (v == '') {
 					this.passwordStrength = '';
@@ -82,6 +113,12 @@ export default Vue.extend({
 			}
 		}
 	},
+	inject: ['isCardChild'],
+	created() {
+		if (this.isCardChild) {
+			this.styl = 'line';
+		}
+	},
 	mounted() {
 		if (this.$refs.prefix) {
 			this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
@@ -90,6 +127,14 @@ export default Vue.extend({
 	methods: {
 		focus() {
 			this.$refs.input.focus();
+		},
+		chooseFile() {
+			this.$refs.file.click();
+		},
+		onChangeFile() {
+			this.v = Array.from((this.$refs.file as any).files);
+			this.$emit('input', this.v);
+			this.$emit('change', this.v);
 		}
 	}
 });
@@ -98,14 +143,52 @@ export default Vue.extend({
 <style lang="stylus" scoped>
 @import '~const.styl'
 
-.ui-input
+root(isDark, fill)
 	margin 32px 0
 
+	> .icon
+		position absolute
+		top 0
+		left 0
+		width 24px
+		text-align center
+		line-height 32px
+		color rgba(#000, 0.54)
+
+		&:not(:empty) + .input
+			margin-left 28px
+
 	> .input
 		display flex
-		padding 6px 12px
-		background rgba(#000, 0.035)
-		border-radius 6px
+
+		if fill
+			padding 6px 12px
+			background rgba(#000, 0.035)
+			border-radius 6px
+		else
+			&:before
+				content ''
+				display block
+				position absolute
+				bottom 0
+				left 0
+				right 0
+				height 1px
+				background rgba(#000, 0.42)
+
+			&:after
+				content ''
+				display block
+				position absolute
+				bottom 0
+				left 0
+				right 0
+				height 2px
+				background $theme-color
+				opacity 0
+				transform scaleX(0.12)
+				transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)
+				will-change border opacity transform
 
 		> .password-meter
 			position absolute
@@ -142,7 +225,7 @@ export default Vue.extend({
 
 		> .label
 			position absolute
-			top 6px
+			top fill ? 6px : 0
 			left 0
 			pointer-events none
 			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
@@ -161,7 +244,7 @@ export default Vue.extend({
 			width 100%
 			padding 0
 			font inherit
-			font-weight bold
+			font-weight fill ? bold : normal
 			font-size 16px
 			line-height 32px
 			background transparent
@@ -170,6 +253,9 @@ export default Vue.extend({
 			outline none
 			box-shadow none
 
+			&[type='file']
+				display none
+
 		> .prefix
 		> .suffix
 			display block
@@ -199,7 +285,12 @@ export default Vue.extend({
 
 	&.focused
 		> .input
-			background rgba(#000, 0.05)
+			if fill
+				background rgba(#000, 0.05)
+			else
+				&:after
+					opacity 1
+					transform scaleX(1)
 
 			> .label
 				color $theme-color
@@ -208,8 +299,20 @@ export default Vue.extend({
 	&.filled
 		> .input
 			> .label
-				top -24px
+				top fill ? -24px : -16px
 				left 0 !important
 				transform scale(0.8)
 
+.ui-input[data-darkmode]
+	&.fill
+		root(true, true)
+	&:not(.fill)
+		root(true, false)
+
+.ui-input:not([data-darkmode])
+	&.fill
+		root(false, true)
+	&:not(.fill)
+		root(false, false)
+
 </style>
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
index e78951a352..f860309867 100644
--- a/src/client/app/common/views/components/ui/switch.vue
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -143,7 +143,6 @@ root(isDark)
 		> span
 			display block
 			line-height 20px
-			font-weight bold
 			color isDark ? #c4ccd2 : rgba(#000, 0.75)
 			transition inherit
 
diff --git a/src/client/app/common/views/components/ui/textarea.vue b/src/client/app/common/views/components/ui/textarea.vue
index 0a9f60f1bc..d5e2b15628 100644
--- a/src/client/app/common/views/components/ui/textarea.vue
+++ b/src/client/app/common/views/components/ui/textarea.vue
@@ -65,13 +65,43 @@ export default Vue.extend({
 <style lang="stylus" scoped>
 @import '~const.styl'
 
-.ui-textarea
+root(isDark, fill)
 	margin 32px 0
 
 	> .input
 		padding 12px
-		background rgba(#000, 0.035)
-		border-radius 6px
+
+		if fill
+			background rgba(#000, 0.035)
+			border-radius 6px
+		else
+			&:before
+				content ''
+				display block
+				position absolute
+				top 0
+				bottom 0
+				left 0
+				right 0
+				background none
+				border solid 1px rgba(#000, 0.42)
+				border-radius 3px
+				pointer-events none
+
+			&:after
+				content ''
+				display block
+				position absolute
+				top 0
+				bottom 0
+				left 0
+				right 0
+				background none
+				border solid 2px $theme-color
+				border-radius 3px
+				opacity 0
+				transition opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)
+				pointer-events none
 
 		> .label
 			position absolute
@@ -94,7 +124,7 @@ export default Vue.extend({
 			min-height 100px
 			padding 0
 			font inherit
-			font-weight bold
+			font-weight fill ? bold : normal
 			font-size 16px
 			background transparent
 			border none
@@ -111,7 +141,11 @@ export default Vue.extend({
 
 	&.focused
 		> .input
-			background rgba(#000, 0.05)
+			if fill
+				background rgba(#000, 0.05)
+			else
+				&:after
+					opacity 1
 
 			> .label
 				color $theme-color
@@ -124,4 +158,16 @@ export default Vue.extend({
 				left 0 !important
 				transform scale(0.8)
 
+.ui-textarea[data-darkmode]
+	&.fill
+		root(true, true)
+	&:not(.fill)
+		root(true, false)
+
+.ui-textarea:not([data-darkmode])
+	&.fill
+		root(false, true)
+	&:not(.fill)
+		root(false, false)
+
 </style>
diff --git a/src/client/app/mobile/views/pages/settings/settings.profile.vue b/src/client/app/mobile/views/pages/settings/settings.profile.vue
index de891b5736..64adac01e6 100644
--- a/src/client/app/mobile/views/pages/settings/settings.profile.vue
+++ b/src/client/app/mobile/views/pages/settings/settings.profile.vue
@@ -29,12 +29,12 @@
 
 		<ui-input type="file" @change="onAvatarChange">
 			<span>%i18n:@avatar%</span>
-			<span slot="prefix">%fa:picture-o%</span>
+			<span slot="icon">%fa:image%</span>
 		</ui-input>
 
 		<ui-input type="file" @change="onBannerChange">
 			<span>%i18n:@banner%</span>
-			<span slot="prefix">%fa:picture-o%</span>
+			<span slot="icon">%fa:image%</span>
 		</ui-input>
 
 		<ui-switch v-model="isCat">%i18n:@is-cat%</ui-switch>

From 11e95ea092e2c1e5d8ee3defba3a8a211cc65864 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 18:57:54 +0900
Subject: [PATCH 07/11] wip

---
 .../app/common/views/components/ui/button.vue | 48 ++++++++++++++++---
 1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/src/client/app/common/views/components/ui/button.vue b/src/client/app/common/views/components/ui/button.vue
index 57747fd469..7723c83f57 100644
--- a/src/client/app/common/views/components/ui/button.vue
+++ b/src/client/app/common/views/components/ui/button.vue
@@ -14,6 +14,17 @@ export default Vue.extend({
 			type: String,
 			required: false
 		}
+	},
+	data() {
+		return {
+			styl: 'fill'
+		};
+	},
+	inject: ['isCardChild'],
+	created() {
+		if (this.isCardChild) {
+			this.styl = 'line';
+		}
 	}
 });
 </script>
@@ -21,26 +32,49 @@ export default Vue.extend({
 <style lang="stylus" scoped>
 @import '~const.styl'
 
-.ui-button
+root(isDark, fill)
 	> button
 		display block
 		width 100%
 		margin 0
 		padding 0
-		color $theme-color-foreground
 		font-weight bold
 		font-size 16px
 		line-height 44px
-		background $theme-color
 		border none
 		border-radius 6px
 		outline none
 		box-shadow none
 
-		&:hover
-			background lighten($theme-color, 5%)
+		if fill
+			color $theme-color-foreground
+			background $theme-color
 
-		&:active
-			background darken($theme-color, 5%)
+			&:hover
+				background lighten($theme-color, 5%)
+
+			&:active
+				background darken($theme-color, 5%)
+		else
+			color $theme-color
+			background none
+
+			&:hover
+				color darken($theme-color, 5%)
+
+			&:active
+				background rgba($theme-color, 0.3)
+
+.ui-button[data-darkmode]
+	&.fill
+		root(true, true)
+	&:not(.fill)
+		root(true, false)
+
+.ui-button:not([data-darkmode])
+	&.fill
+		root(false, true)
+	&:not(.fill)
+		root(false, false)
 
 </style>

From dec7d537dca22f60ddc595c7fc34eabaca2c13f1 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 20:23:50 +0900
Subject: [PATCH 08/11] wip

---
 .../app/common/views/components/index.ts      |   4 +
 .../app/common/views/components/ui/radio.vue  | 120 ++++++++++
 .../app/common/views/components/ui/select.vue | 212 ++++++++++++++++++
 .../app/common/views/components/ui/switch.vue |   2 +-
 .../app/mobile/views/pages/settings.vue       |  22 +-
 5 files changed, 347 insertions(+), 13 deletions(-)
 create mode 100644 src/client/app/common/views/components/ui/radio.vue
 create mode 100644 src/client/app/common/views/components/ui/select.vue

diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 0e20e66f5a..b91008f718 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -35,6 +35,8 @@ import uiCard from './ui/card.vue';
 import uiForm from './ui/form.vue';
 import uiTextarea from './ui/textarea.vue';
 import uiSwitch from './ui/switch.vue';
+import uiRadio from './ui/radio.vue';
+import uiSelect from './ui/select.vue';
 
 Vue.component('mk-analog-clock', analogClock);
 Vue.component('mk-menu', menu);
@@ -71,3 +73,5 @@ Vue.component('ui-card', uiCard);
 Vue.component('ui-form', uiForm);
 Vue.component('ui-textarea', uiTextarea);
 Vue.component('ui-switch', uiSwitch);
+Vue.component('ui-radio', uiRadio);
+Vue.component('ui-select', uiSelect);
diff --git a/src/client/app/common/views/components/ui/radio.vue b/src/client/app/common/views/components/ui/radio.vue
new file mode 100644
index 0000000000..2b7f1d9dd4
--- /dev/null
+++ b/src/client/app/common/views/components/ui/radio.vue
@@ -0,0 +1,120 @@
+<template>
+<div
+	class="ui-radio"
+	:class="{ disabled, checked }"
+	:aria-checked="checked"
+	:aria-disabled="disabled"
+	@click="toggle"
+>
+	<input type="radio"
+		:disabled="disabled"
+	>
+	<span class="button">
+		<span></span>
+	</span>
+	<span class="label"><slot></slot></span>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	model: {
+		prop: 'model',
+		event: 'change'
+	},
+	props: {
+		model: {
+			type: String,
+			required: false
+		},
+		value: {
+			type: String,
+			required: false
+		},
+		disabled: {
+			type: Boolean,
+			default: false
+		}
+	},
+	computed: {
+		checked(): boolean {
+			return this.model === this.value;
+		}
+	},
+	methods: {
+		toggle() {
+			this.$emit('change', this.value);
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark)
+	display inline-block
+	margin 32px 32px 32px 0
+	cursor pointer
+	transition all 0.3s
+
+	> *
+		user-select none
+
+	&.disabled
+		opacity 0.6
+		cursor not-allowed
+
+	&.checked
+		> .button
+			border-color $theme-color
+
+			&:after
+				background-color $theme-color
+				transform scale(1)
+				opacity 1
+
+	> input
+		position absolute
+		width 0
+		height 0
+		opacity 0
+		margin 0
+
+	> .button
+		position absolute
+		width 20px
+		height 20px
+		background none
+		border solid 2px rgba(#000, 0.54)
+		border-radius 100%
+		transition inherit
+
+		&:after
+			content ''
+			display block
+			position absolute
+			top 3px
+			right 3px
+			bottom 3px
+			left 3px
+			border-radius 100%
+			opacity 0
+			transform scale(0)
+			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
+
+	> .label
+		margin-left 28px
+		display block
+		font-size 16px
+		line-height 20px
+		cursor pointer
+
+.ui-radio[data-darkmode]
+	root(true)
+
+.ui-radio:not([data-darkmode])
+	root(false)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/select.vue b/src/client/app/common/views/components/ui/select.vue
new file mode 100644
index 0000000000..c56ae86159
--- /dev/null
+++ b/src/client/app/common/views/components/ui/select.vue
@@ -0,0 +1,212 @@
+<template>
+<div class="ui-select" :class="[{ focused, filled }, styl]">
+	<div class="icon" ref="icon"><slot name="icon"></slot></div>
+	<div class="input" @click="focus">
+		<span class="label" ref="label"><slot name="label"></slot></span>
+		<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
+		<select ref="input"
+				:value="v"
+				:required="required"
+				@input="$emit('input', $event.target.value)"
+				@focus="focused = true"
+				@blur="focused = false">
+			<slot></slot>
+		</select>
+		<div class="suffix"><slot name="suffix"></slot></div>
+	</div>
+	<div class="text"><slot name="text"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+	props: {
+		value: {
+			required: false
+		},
+		required: {
+			type: Boolean,
+			required: false
+		}
+	},
+	data() {
+		return {
+			v: this.value,
+			focused: false,
+			styl: 'fill'
+		};
+	},
+	computed: {
+		filled(): boolean {
+			return this.v != '' && this.v != null;
+		}
+	},
+	watch: {
+		value(v) {
+			this.v = v;
+		}
+	},
+	inject: ['isCardChild'],
+	created() {
+		if (this.isCardChild) {
+			this.styl = 'line';
+		}
+	},
+	mounted() {
+		if (this.$refs.prefix) {
+			this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
+		}
+	},
+	methods: {
+		focus() {
+			this.$refs.input.focus();
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark, fill)
+	margin 32px 0
+
+	> .icon
+		position absolute
+		top 0
+		left 0
+		width 24px
+		text-align center
+		line-height 32px
+		color rgba(#000, 0.54)
+
+		&:not(:empty) + .input
+			margin-left 28px
+
+	> .input
+		display flex
+
+		if fill
+			padding 6px 12px
+			background rgba(#000, 0.035)
+			border-radius 6px
+		else
+			&:before
+				content ''
+				display block
+				position absolute
+				bottom 0
+				left 0
+				right 0
+				height 1px
+				background rgba(#000, 0.42)
+
+			&:after
+				content ''
+				display block
+				position absolute
+				bottom 0
+				left 0
+				right 0
+				height 2px
+				background $theme-color
+				opacity 0
+				transform scaleX(0.12)
+				transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)
+				will-change border opacity transform
+
+		> .label
+			position absolute
+			top fill ? 6px : 0
+			left 0
+			pointer-events none
+			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
+			transition-duration 0.3s
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+			pointer-events none
+			//will-change transform
+			transform-origin top left
+			transform scale(1)
+
+		> select
+			display block
+			flex 1
+			width 100%
+			padding 0
+			font inherit
+			font-weight fill ? bold : normal
+			font-size 16px
+			height 32px
+			background transparent
+			border none
+			border-radius 0
+			outline none
+			box-shadow none
+
+			&[type='file']
+				display none
+
+		> .prefix
+		> .suffix
+			display block
+			align-self center
+			justify-self center
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+			pointer-events none
+
+			> *
+				display block
+				min-width 16px
+
+		> .prefix
+			padding-right 4px
+
+		> .suffix
+			padding-left 4px
+
+	> .text
+		margin 6px 0
+		font-size 13px
+
+		*
+			margin 0
+
+	&.focused
+		> .input
+			if fill
+				background rgba(#000, 0.05)
+			else
+				&:after
+					opacity 1
+					transform scaleX(1)
+
+			> .label
+				color $theme-color
+
+	&.focused
+	&.filled
+		> .input
+			> .label
+				top fill ? -24px : -16px
+				left 0 !important
+				transform scale(0.8)
+
+.ui-select[data-darkmode]
+	&.fill
+		root(true, true)
+	&:not(.fill)
+		root(true, false)
+
+.ui-select:not([data-darkmode])
+	&.fill
+		root(false, true)
+	&:not(.fill)
+		root(false, false)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
index f860309867..2431a76528 100644
--- a/src/client/app/common/views/components/ui/switch.vue
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -117,7 +117,7 @@ root(isDark)
 		margin 3px 0 0 0
 		width 34px
 		height 14px
-		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.05)
+		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.07)
 		outline none
 		border-radius 14px
 		transition inherit
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 25cfbf732b..beac788482 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -36,8 +36,8 @@
 				<div>
 					<div class="md-body-2">%i18n:@post-style%</div>
 
-					<md-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</md-radio>
-					<md-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</md-radio>
+					<ui-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</ui-radio>
+					<ui-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</ui-radio>
 				</div>
 			</ui-card>
 
@@ -68,17 +68,15 @@
 			<ui-card>
 				<div slot="title">%fa:language% %i18n:@lang%</div>
 
-				<md-field>
-					<md-select v-model="lang" placeholder="%i18n:@auto%">
-						<md-optgroup label="%i18n:@recommended%">
-							<md-option value="">%i18n:@auto%</md-option>
-						</md-optgroup>
+				<ui-select v-model="lang" placeholder="%i18n:@auto%">
+					<optgroup label="%i18n:@recommended%">
+						<option value="">%i18n:@auto%</option>
+					</optgroup>
 
-						<md-optgroup label="%i18n:@specify-language%">
-							<md-option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</md-option>
-						</md-optgroup>
-					</md-select>
-				</md-field>
+					<optgroup label="%i18n:@specify-language%">
+						<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
+					</optgroup>
+				</ui-select>
 				<span class="md-helper-text">%fa:info-circle% %i18n:@lang-tip%</span>
 			</ui-card>
 

From d90f75425f0449bcb04e462250a4495b63be39a0 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 21:38:39 +0900
Subject: [PATCH 09/11] wip

---
 .../app/common/views/components/ui/card.vue   | 16 ++++---
 .../app/common/views/components/ui/input.vue  | 13 +++---
 .../app/common/views/components/ui/select.vue |  4 +-
 .../app/common/views/components/ui/switch.vue | 43 ++++---------------
 .../common/views/components/ui/textarea.vue   |  7 +--
 5 files changed, 33 insertions(+), 50 deletions(-)

diff --git a/src/client/app/common/views/components/ui/card.vue b/src/client/app/common/views/components/ui/card.vue
index 97f06ca655..e2b15240e4 100644
--- a/src/client/app/common/views/components/ui/card.vue
+++ b/src/client/app/common/views/components/ui/card.vue
@@ -22,15 +22,21 @@ export default Vue.extend({
 <style lang="stylus" scoped>
 @import '~const.styl'
 
-.ui-card
+root(isDark)
 	margin 16px
 	padding 16px
-	background #fff
+	background isDark ? #282C37 : #fff
 	box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
 
 	> header
-		font-weight bold
-		font-size 28px
-		color #444
+		font-weight normal
+		font-size 24px
+		color isDark ? #fff : #444
+
+.ui-card[data-darkmode]
+	root(true)
+
+.ui-card:not([data-darkmode])
+	root(false)
 
 </style>
diff --git a/src/client/app/common/views/components/ui/input.vue b/src/client/app/common/views/components/ui/input.vue
index 3a474f0243..167dd4e2a9 100644
--- a/src/client/app/common/views/components/ui/input.vue
+++ b/src/client/app/common/views/components/ui/input.vue
@@ -153,7 +153,7 @@ root(isDark, fill)
 		width 24px
 		text-align center
 		line-height 32px
-		color rgba(#000, 0.54)
+		color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
 
 		&:not(:empty) + .input
 			margin-left 28px
@@ -174,7 +174,7 @@ root(isDark, fill)
 				left 0
 				right 0
 				height 1px
-				background rgba(#000, 0.42)
+				background isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42)
 
 			&:after
 				content ''
@@ -232,7 +232,7 @@ root(isDark, fill)
 			transition-duration 0.3s
 			font-size 16px
 			line-height 32px
-			color rgba(#000, 0.54)
+			color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
 			pointer-events none
 			//will-change transform
 			transform-origin top left
@@ -247,6 +247,7 @@ root(isDark, fill)
 			font-weight fill ? bold : normal
 			font-size 16px
 			line-height 32px
+			color isDark ? #fff : #000
 			background transparent
 			border none
 			border-radius 0
@@ -263,7 +264,7 @@ root(isDark, fill)
 			justify-self center
 			font-size 16px
 			line-height 32px
-			color rgba(#000, 0.54)
+			color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
 			pointer-events none
 
 			> *
@@ -299,9 +300,9 @@ root(isDark, fill)
 	&.filled
 		> .input
 			> .label
-				top fill ? -24px : -16px
+				top fill ? -24px : -17px
 				left 0 !important
-				transform scale(0.8)
+				transform scale(0.75)
 
 .ui-input[data-darkmode]
 	&.fill
diff --git a/src/client/app/common/views/components/ui/select.vue b/src/client/app/common/views/components/ui/select.vue
index c56ae86159..4577a15f68 100644
--- a/src/client/app/common/views/components/ui/select.vue
+++ b/src/client/app/common/views/components/ui/select.vue
@@ -193,9 +193,9 @@ root(isDark, fill)
 	&.filled
 		> .input
 			> .label
-				top fill ? -24px : -16px
+				top fill ? -24px : -17px
 				left 0 !important
-				transform scale(0.8)
+				transform scale(0.75)
 
 .ui-select[data-darkmode]
 	&.fill
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
index 2431a76528..24611b9aa0 100644
--- a/src/client/app/common/views/components/ui/switch.vue
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -5,15 +5,13 @@
 	role="switch"
 	:aria-checked="checked"
 	:aria-disabled="disabled"
-	@click="switchValue"
-	@mouseover="mouseenter"
+	@click="toggle"
 >
 	<input
 		type="checkbox"
-		@change="handleChange"
 		ref="input"
 		:disabled="disabled"
-		@keydown.enter="switchValue"
+		@keydown.enter="toggle"
 	>
 	<span class="button">
 		<span></span>
@@ -30,6 +28,10 @@
 <script lang="ts">
 import Vue from 'vue';
 export default Vue.extend({
+	model: {
+		prop: 'value',
+		event: 'change'
+	},
 	props: {
 		value: {
 			type: Boolean,
@@ -39,42 +41,15 @@ export default Vue.extend({
 			type: Boolean,
 			default: false
 		}
-	},/*
-	created() {
-		if (!~[true, false].indexOf(this.value)) {
-			this.$emit('input', false);
-		}
-	},*/
+	},
 	computed: {
 		checked(): boolean {
 			return this.value;
 		}
 	},
-	watch: {
-		value() {
-			(this.$el).style.transition = 'all 0.3s';
-			(this.$refs.input as any).checked = this.checked;
-		}
-	},
-	mounted() {
-		(this.$refs.input as any).checked = this.checked;
-	},
 	methods: {
-		mouseenter() {
-			(this.$el).style.transition = 'all 0s';
-		},
-		handleChange() {
-			(this.$el).style.transition = 'all 0.3s';
-			this.$emit('input', !this.checked);
+		toggle() {
 			this.$emit('change', !this.checked);
-			this.$nextTick(() => {
-				// set input's checked property
-				// in case parent refuses to change component's value
-				(this.$refs.input as any).checked = this.checked;
-			});
-		},
-		switchValue() {
-			!this.disabled && this.handleChange();
 		}
 	}
 });
@@ -117,7 +92,7 @@ root(isDark)
 		margin 3px 0 0 0
 		width 34px
 		height 14px
-		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.07)
+		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.25)
 		outline none
 		border-radius 14px
 		transition inherit
diff --git a/src/client/app/common/views/components/ui/textarea.vue b/src/client/app/common/views/components/ui/textarea.vue
index d5e2b15628..cc6b376ead 100644
--- a/src/client/app/common/views/components/ui/textarea.vue
+++ b/src/client/app/common/views/components/ui/textarea.vue
@@ -84,7 +84,7 @@ root(isDark, fill)
 				left 0
 				right 0
 				background none
-				border solid 1px rgba(#000, 0.42)
+				border solid 1px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42)
 				border-radius 3px
 				pointer-events none
 
@@ -112,7 +112,7 @@ root(isDark, fill)
 			transition-duration 0.3s
 			font-size 16px
 			line-height 32px
-			color rgba(#000, 0.54)
+			color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
 			pointer-events none
 			//will-change transform
 			transform-origin top left
@@ -126,6 +126,7 @@ root(isDark, fill)
 			font inherit
 			font-weight fill ? bold : normal
 			font-size 16px
+			color isDark ? #fff : #000
 			background transparent
 			border none
 			border-radius 0
@@ -156,7 +157,7 @@ root(isDark, fill)
 			> .label
 				top -24px
 				left 0 !important
-				transform scale(0.8)
+				transform scale(0.75)
 
 .ui-textarea[data-darkmode]
 	&.fill

From 0ac9120064b295a363b74a2c2afc487db2b50908 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 15 Jun 2018 07:56:56 +0900
Subject: [PATCH 10/11] wip

---
 .../app/common/views/components/signup.vue    | 10 +--
 .../app/common/views/components/ui/button.vue |  8 +-
 .../app/common/views/components/ui/card.vue   |  4 +
 .../app/common/views/components/ui/form.vue   |  2 +-
 .../app/common/views/components/ui/input.vue  |  8 +-
 .../app/common/views/components/ui/radio.vue  |  2 +-
 .../app/common/views/components/ui/select.vue | 11 ++-
 .../app/common/views/components/ui/switch.vue |  2 +-
 src/client/app/config.ts                      |  4 +
 .../app/mobile/views/pages/settings.vue       | 79 +++++++------------
 .../views/pages/settings/settings.profile.vue | 17 ++--
 src/client/app/mobile/views/pages/welcome.vue |  6 +-
 src/config/types.ts                           |  2 +
 webpack.config.ts                             |  2 +
 14 files changed, 80 insertions(+), 77 deletions(-)

diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue
index d1621a4848..987cc7e52d 100644
--- a/src/client/app/common/views/components/signup.vue
+++ b/src/client/app/common/views/components/signup.vue
@@ -1,6 +1,6 @@
 <template>
-<form class="mk-signup" @submit.prevent="onSubmit" autocomplete="off">
-	<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" autocomplete="off" required @input="onChangeUsername">
+<form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
+	<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" required @input="onChangeUsername">
 		<span>%i18n:@username%</span>
 		<span slot="prefix">@</span>
 		<span slot="suffix">@{{ host }}</span>
@@ -12,7 +12,7 @@
 		<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
 		<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
 	</ui-input>
-	<ui-input v-model="password" type="password" autocomplete="off" required @input="onChangePassword" :with-password-meter="true">
+	<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true">
 		<span>%i18n:@password%</span>
 		<span slot="prefix">%fa:lock%</span>
 		<div slot="text">
@@ -21,7 +21,7 @@
 			<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
 		</div>
 	</ui-input>
-	<ui-input v-model="retypedPassword" type="password" autocomplete="off" required @input="onChangePasswordRetype">
+	<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype">
 		<span>%i18n:@password% (%i18n:@retype%)</span>
 		<span slot="prefix">%fa:lock%</span>
 		<div slot="text">
@@ -31,7 +31,7 @@
 	</ui-input>
 	<div class="g-recaptcha" :data-sitekey="recaptchaSitekey" style="margin: 16px 0;"></div>
 	<label class="agree-tou" style="display: block; margin: 16px 0;">
-		<input name="agree-tou" type="checkbox" autocomplete="off" required/>
+		<input name="agree-tou" type="checkbox" required/>
 		<p><a :href="touUrl" target="_blank">利用規約</a>に同意する</p>
 	</label>
 	<ui-button type="submit">%i18n:@create%</ui-button>
diff --git a/src/client/app/common/views/components/ui/button.vue b/src/client/app/common/views/components/ui/button.vue
index 7723c83f57..e778750354 100644
--- a/src/client/app/common/views/components/ui/button.vue
+++ b/src/client/app/common/views/components/ui/button.vue
@@ -1,6 +1,6 @@
 <template>
-<div class="ui-button">
-	<button :type="type">
+<div class="ui-button" :class="[styl]">
+	<button :type="type" @click="$emit('click')">
 		<slot></slot>
 	</button>
 </div>
@@ -20,7 +20,9 @@ export default Vue.extend({
 			styl: 'fill'
 		};
 	},
-	inject: ['isCardChild'],
+	inject: {
+		isCardChild: { default: false }
+	},
 	created() {
 		if (this.isCardChild) {
 			this.styl = 'line';
diff --git a/src/client/app/common/views/components/ui/card.vue b/src/client/app/common/views/components/ui/card.vue
index e2b15240e4..05c51bca6b 100644
--- a/src/client/app/common/views/components/ui/card.vue
+++ b/src/client/app/common/views/components/ui/card.vue
@@ -25,9 +25,13 @@ export default Vue.extend({
 root(isDark)
 	margin 16px
 	padding 16px
+	color isDark ? #fff : #000
 	background isDark ? #282C37 : #fff
 	box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
 
+	@media (min-width 500px)
+		padding 32px
+
 	> header
 		font-weight normal
 		font-size 24px
diff --git a/src/client/app/common/views/components/ui/form.vue b/src/client/app/common/views/components/ui/form.vue
index b6b4a76d2a..fc8fdad9c4 100644
--- a/src/client/app/common/views/components/ui/form.vue
+++ b/src/client/app/common/views/components/ui/form.vue
@@ -11,7 +11,7 @@ import Vue from 'vue';
 export default Vue.extend({
 	props: {
 		disabled: {
-			type: String,
+			type: Boolean,
 			required: false
 		}
 	}
diff --git a/src/client/app/common/views/components/ui/input.vue b/src/client/app/common/views/components/ui/input.vue
index 167dd4e2a9..ec91ca364c 100644
--- a/src/client/app/common/views/components/ui/input.vue
+++ b/src/client/app/common/views/components/ui/input.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="ui-input" :class="[{ focused, filled }, styl]">
 	<div class="icon" ref="icon"><slot name="icon"></slot></div>
-	<div class="input" @click="focus">
+	<div class="input" @click="focus" @mousedown="focus">
 		<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
 			<div class="value" ref="passwordMetar"></div>
 		</div>
@@ -62,7 +62,6 @@ export default Vue.extend({
 			required: false
 		},
 		autocomplete: {
-			type: String,
 			required: false
 		},
 		withPasswordMeter: {
@@ -113,7 +112,9 @@ export default Vue.extend({
 			}
 		}
 	},
-	inject: ['isCardChild'],
+	inject: {
+		isCardChild: { default: false }
+	},
 	created() {
 		if (this.isCardChild) {
 			this.styl = 'line';
@@ -160,6 +161,7 @@ root(isDark, fill)
 
 	> .input
 		display flex
+		cursor text
 
 		if fill
 			padding 6px 12px
diff --git a/src/client/app/common/views/components/ui/radio.vue b/src/client/app/common/views/components/ui/radio.vue
index 2b7f1d9dd4..04a46c5a96 100644
--- a/src/client/app/common/views/components/ui/radio.vue
+++ b/src/client/app/common/views/components/ui/radio.vue
@@ -87,7 +87,7 @@ root(isDark)
 		width 20px
 		height 20px
 		background none
-		border solid 2px rgba(#000, 0.54)
+		border solid 2px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
 		border-radius 100%
 		transition inherit
 
diff --git a/src/client/app/common/views/components/ui/select.vue b/src/client/app/common/views/components/ui/select.vue
index 4577a15f68..4273a4a0de 100644
--- a/src/client/app/common/views/components/ui/select.vue
+++ b/src/client/app/common/views/components/ui/select.vue
@@ -48,7 +48,9 @@ export default Vue.extend({
 			this.v = v;
 		}
 	},
-	inject: ['isCardChild'],
+	inject: {
+		isCardChild: { default: false }
+	},
 	created() {
 		if (this.isCardChild) {
 			this.styl = 'line';
@@ -101,7 +103,7 @@ root(isDark, fill)
 				left 0
 				right 0
 				height 1px
-				background rgba(#000, 0.42)
+				background isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42)
 
 			&:after
 				content ''
@@ -141,14 +143,15 @@ root(isDark, fill)
 			font-weight fill ? bold : normal
 			font-size 16px
 			height 32px
+			color isDark ? #fff : #000
 			background transparent
 			border none
 			border-radius 0
 			outline none
 			box-shadow none
 
-			&[type='file']
-				display none
+			*
+				color #000
 
 		> .prefix
 		> .suffix
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
index 24611b9aa0..a9e00d73d2 100644
--- a/src/client/app/common/views/components/ui/switch.vue
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -92,7 +92,7 @@ root(isDark)
 		margin 3px 0 0 0
 		width 34px
 		height 14px
-		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.25)
+		background isDark ? rgba(#fff, 0.15) : rgba(#000, 0.25)
 		outline none
 		border-radius 14px
 		transition inherit
diff --git a/src/client/app/config.ts b/src/client/app/config.ts
index 70c085de1c..e4a7ff6d38 100644
--- a/src/client/app/config.ts
+++ b/src/client/app/config.ts
@@ -1,6 +1,8 @@
 declare const _HOST_: string;
 declare const _HOSTNAME_: string;
 declare const _URL_: string;
+declare const _NAME_: string;
+declare const _DESCRIPTION_: string;
 declare const _API_URL_: string;
 declare const _WS_URL_: string;
 declare const _DOCS_URL_: string;
@@ -21,6 +23,8 @@ declare const _GOOGLE_MAPS_API_KEY_: string;
 export const host = _HOST_;
 export const hostname = _HOSTNAME_;
 export const url = _URL_;
+export const name = _NAME_;
+export const description = _DESCRIPTION_;
 export const apiUrl = _API_URL_;
 export const wsUrl = _WS_URL_;
 export const docsUrl = _DOCS_URL_;
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index beac788482..2a32e982ba 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -1,41 +1,27 @@
 <template>
 <mk-ui>
 	<span slot="header">%fa:cog%%i18n:@settings%</span>
-	<main>
-		<p v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></p>
+	<main :data-darkmode="$store.state.device.darkmode">
+		<div class="signin-as" v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></div>
+
 		<div>
 			<x-profile/>
 
 			<ui-card>
 				<div slot="title">%fa:palette% %i18n:@design%</div>
 
-				<div>
-					<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
-				</div>
-
-				<div>
-					<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
-				</div>
+				<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
+				<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
 
 				<div>
 					<div class="md-body-2">%i18n:@timeline%</div>
-
-					<div>
-						<ui-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</ui-switch>
-					</div>
-
-					<div>
-						<ui-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</ui-switch>
-					</div>
-
-					<div>
-						<ui-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
-					</div>
+					<ui-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</ui-switch>
+					<ui-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</ui-switch>
+					<ui-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
 				</div>
 
 				<div>
 					<div class="md-body-2">%i18n:@post-style%</div>
-
 					<ui-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</ui-radio>
 					<ui-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</ui-radio>
 				</div>
@@ -43,26 +29,11 @@
 
 			<ui-card>
 				<div slot="title">%fa:cog% %i18n:@behavior%</div>
-
-				<div>
-					<ui-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch>
-				</div>
-
-				<div>
-					<ui-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile">%i18n:@disable-via-mobile%</ui-switch>
-				</div>
-
-				<div>
-					<ui-switch v-model="loadRawImages">%i18n:@load-raw-images%</ui-switch>
-				</div>
-
-				<div>
-					<ui-switch v-model="$store.state.settings.loadRemoteMedia" @change="onChangeLoadRemoteMedia">%i18n:@load-remote-media%</ui-switch>
-				</div>
-
-				<div>
-					<ui-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</ui-switch>
-				</div>
+				<ui-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch>
+				<ui-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile">%i18n:@disable-via-mobile%</ui-switch>
+				<ui-switch v-model="loadRawImages">%i18n:@load-raw-images%</ui-switch>
+				<ui-switch v-model="$store.state.settings.loadRemoteMedia" @change="onChangeLoadRemoteMedia">%i18n:@load-remote-media%</ui-switch>
+				<ui-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</ui-switch>
 			</ui-card>
 
 			<ui-card>
@@ -98,13 +69,16 @@
 				<template v-if="latestVersion !== undefined">
 					<div>%i18n:@latest-version% <i>{{ latestVersion ? latestVersion : version }}</i></div>
 				</template>
-				<md-button class="md-raised md-primary" @click="checkForUpdate" :disabled="checkingForUpdate">
+				<ui-button class="md-raised md-primary" @click="checkForUpdate" :disabled="checkingForUpdate">
 					<template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
 					<template v-else>%i18n:@check-for-updates%</template>
-				</md-button>
+				</ui-button>
 			</ui-card>
 		</div>
-		<p><small>ver {{ version }} ({{ codename }})</small></p>
+
+		<footer>
+			<small>ver {{ version }} ({{ codename }})</small>
+		</footer>
 	</main>
 </mk-ui>
 </template>
@@ -249,13 +223,18 @@ root(isDark)
 	max-width 500px
 	width 100%
 
-	> p
-		display block
-		margin 16px 0
+	> .signin-as
+		margin 16px
 		padding 16px
 		text-align center
-		color isDark ? #cad2da : #2c662d
-		background #fcfff5
+		color isDark ? #49ab63 : #2c662d
+		background isDark ? #273c34 : #fcfff5
+		box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
+
+	> footer
+		margin 16px
+		text-align center
+		color isDark ? #c9d2e0 : #888
 
 main[data-darkmode]
 	root(true)
diff --git a/src/client/app/mobile/views/pages/settings/settings.profile.vue b/src/client/app/mobile/views/pages/settings/settings.profile.vue
index 64adac01e6..da97cbebd7 100644
--- a/src/client/app/mobile/views/pages/settings/settings.profile.vue
+++ b/src/client/app/mobile/views/pages/settings/settings.profile.vue
@@ -30,11 +30,13 @@
 		<ui-input type="file" @change="onAvatarChange">
 			<span>%i18n:@avatar%</span>
 			<span slot="icon">%fa:image%</span>
+			<span slot="text" v-if="avatarUploading">%i18n:@uploading%<mk-ellipsis/></span>
 		</ui-input>
 
 		<ui-input type="file" @change="onBannerChange">
 			<span>%i18n:@banner%</span>
 			<span slot="icon">%fa:image%</span>
+			<span slot="text" v-if="bannerUploading">%i18n:@uploading%<mk-ellipsis/></span>
 		</ui-input>
 
 		<ui-switch v-model="isCat">%i18n:@is-cat%</ui-switch>
@@ -62,7 +64,8 @@ export default Vue.extend({
 			isBot: false,
 			isCat: false,
 			saving: false,
-			uploading: false
+			avatarUploading: false,
+			bannerUploading: false
 		};
 	},
 
@@ -80,7 +83,7 @@ export default Vue.extend({
 
 	methods: {
 		onAvatarChange([file]) {
-			this.uploading = true;
+			this.avatarUploading = true;
 
 			const data = new FormData();
 			data.append('file', file);
@@ -93,16 +96,16 @@ export default Vue.extend({
 			.then(response => response.json())
 			.then(f => {
 				this.avatarId = f.id;
-				this.uploading = false;
+				this.avatarUploading = false;
 			})
 			.catch(e => {
-				this.uploading = false;
+				this.avatarUploading = false;
 				alert('%18n:!@upload-failed%');
 			});
 		},
 
 		onBannerChange([file]) {
-			this.uploading = true;
+			this.bannerUploading = true;
 
 			const data = new FormData();
 			data.append('file', file);
@@ -115,10 +118,10 @@ export default Vue.extend({
 			.then(response => response.json())
 			.then(f => {
 				this.bannerId = f.id;
-				this.uploading = false;
+				this.bannerUploading = false;
 			})
 			.catch(e => {
-				this.uploading = false;
+				this.bannerUploading = false;
 				alert('%18n:!@upload-failed%');
 			});
 		},
diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue
index 3b37a185bd..3bf2a0af9f 100644
--- a/src/client/app/mobile/views/pages/welcome.vue
+++ b/src/client/app/mobile/views/pages/welcome.vue
@@ -35,7 +35,7 @@
 
 <script lang="ts">
 import Vue from 'vue';
-import { apiUrl, copyright, host } from '../../../config';
+import { apiUrl, copyright, host, name, description } from '../../../config';
 
 export default Vue.extend({
 	data() {
@@ -48,7 +48,9 @@ export default Vue.extend({
 			apiUrl,
 			copyright,
 			users: [],
-			host
+			host,
+			name,
+			description
 		};
 	},
 	mounted() {
diff --git a/src/config/types.ts b/src/config/types.ts
index 910c03c2c1..d557f2afde 100644
--- a/src/config/types.ts
+++ b/src/config/types.ts
@@ -15,6 +15,8 @@ export type Source = {
 		 */
 		url: string;
 	};
+	name?: string;
+	description?: string;
 	url: string;
 	port: number;
 	https?: { [x: string]: string };
diff --git a/webpack.config.ts b/webpack.config.ts
index 67fb929449..8376cd9c40 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -79,6 +79,8 @@ const consts = {
 	_DEV_URL_: config.dev_url,
 	_LANG_: '%lang%',
 	_LANGS_: Object.keys(locales).map(l => [l, locales[l].meta.lang]),
+	_NAME_: config.name,
+	_DESCRIPTION_: config.description,
 	_HOST_: config.host,
 	_HOSTNAME_: config.hostname,
 	_URL_: config.url,

From bddff17e5e22104a799d0147301570239a895fbc Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 15 Jun 2018 07:58:27 +0900
Subject: [PATCH 11/11] wip

---
 src/client/app/mobile/views/pages/settings.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 2a32e982ba..1c5a43ede4 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -14,14 +14,14 @@
 				<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
 
 				<div>
-					<div class="md-body-2">%i18n:@timeline%</div>
+					<div>%i18n:@timeline%</div>
 					<ui-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</ui-switch>
 					<ui-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</ui-switch>
 					<ui-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
 				</div>
 
 				<div>
-					<div class="md-body-2">%i18n:@post-style%</div>
+					<div>%i18n:@post-style%</div>
 					<ui-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</ui-radio>
 					<ui-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</ui-radio>
 				</div>
@@ -48,7 +48,7 @@
 						<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
 					</optgroup>
 				</ui-select>
-				<span class="md-helper-text">%fa:info-circle% %i18n:@lang-tip%</span>
+				<span>%fa:info-circle% %i18n:@lang-tip%</span>
 			</ui-card>
 
 			<ui-card>
@@ -69,7 +69,7 @@
 				<template v-if="latestVersion !== undefined">
 					<div>%i18n:@latest-version% <i>{{ latestVersion ? latestVersion : version }}</i></div>
 				</template>
-				<ui-button class="md-raised md-primary" @click="checkForUpdate" :disabled="checkingForUpdate">
+				<ui-button @click="checkForUpdate" :disabled="checkingForUpdate">
 					<template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
 					<template v-else>%i18n:@check-for-updates%</template>
 				</ui-button>