diff --git a/cmd/admin.go b/cmd/admin.go
index ac911f184b..0934a7364b 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -340,6 +340,7 @@ func runCreateUser(c *cli.Context) error {
 		IsActive:           true,
 		IsAdmin:            c.Bool("admin"),
 		MustChangePassword: changePassword,
+		Theme:              setting.UI.DefaultTheme,
 	}); err != nil {
 		return fmt.Errorf("CreateUser: %v", err)
 	}
diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample
index 0b24bf3414..31048f7857 100644
--- a/custom/conf/app.ini.sample
+++ b/custom/conf/app.ini.sample
@@ -85,6 +85,8 @@ MAX_DISPLAY_FILE_SIZE = 8388608
 SHOW_USER_EMAIL = true
 ; Set the default theme for the Gitea install
 DEFAULT_THEME = gitea
+; All available themes
+THEMES = gitea,arc-green
 
 [ui.admin]
 ; Number of users that are displayed on one page
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 1fa94c42aa..4c6b6d64bc 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -18,7 +18,7 @@ import (
 	"github.com/Unknwon/com"
 	"github.com/go-xorm/xorm"
 	gouuid "github.com/satori/go.uuid"
-	"gopkg.in/ini.v1"
+	ini "gopkg.in/ini.v1"
 
 	"code.gitea.io/gitea/modules/generate"
 	"code.gitea.io/gitea/modules/log"
@@ -206,6 +206,8 @@ var migrations = []Migration{
 	NewMigration("clear nonused data which not deleted when user was deleted", clearNonusedData),
 	// v76 -> v77
 	NewMigration("add pull request rebase with merge commit", addPullRequestRebaseWithMerge),
+	// v77 -> v78
+	NewMigration("add theme to users", addUserDefaultTheme),
 }
 
 // Migrate database to current version
diff --git a/models/migrations/v77.go b/models/migrations/v77.go
new file mode 100644
index 0000000000..12e7456642
--- /dev/null
+++ b/models/migrations/v77.go
@@ -0,0 +1,17 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"github.com/go-xorm/xorm"
+)
+
+func addUserDefaultTheme(x *xorm.Engine) error {
+	type User struct {
+		Theme string `xorm:"VARCHAR(30)"`
+	}
+
+	return x.Sync2(new(User))
+}
diff --git a/models/user.go b/models/user.go
index 3a47346e34..764c2280d7 100644
--- a/models/user.go
+++ b/models/user.go
@@ -140,6 +140,7 @@ type User struct {
 
 	// Preferences
 	DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
+	Theme         string `xorm:"NOT NULL DEFAULT ''"`
 }
 
 // BeforeUpdate is invoked from XORM before updating this object.
@@ -165,6 +166,13 @@ func (u *User) BeforeUpdate() {
 	u.Description = base.TruncateString(u.Description, 255)
 }
 
+// AfterLoad is invoked from XORM after filling all the fields of this object.
+func (u *User) AfterLoad() {
+	if u.Theme == "" {
+		u.Theme = setting.UI.DefaultTheme
+	}
+}
+
 // SetLastLogin set time to last login
 func (u *User) SetLastLogin() {
 	u.LastLoginUnix = util.TimeStampNow()
@@ -176,6 +184,12 @@ func (u *User) UpdateDiffViewStyle(style string) error {
 	return UpdateUserCols(u, "diff_view_style")
 }
 
+// UpdateTheme updates a users' theme irrespective of the site wide theme
+func (u *User) UpdateTheme(themeName string) error {
+	u.Theme = themeName
+	return UpdateUserCols(u, "theme")
+}
+
 // getEmail returns an noreply email, if the user has set to keep his
 // email address private, otherwise the primary email address.
 func (u *User) getEmail() string {
@@ -777,6 +791,7 @@ func CreateUser(u *User) (err error) {
 	u.HashPassword(u.Passwd)
 	u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization
 	u.MaxRepoCreation = -1
+	u.Theme = setting.UI.DefaultTheme
 
 	if _, err = sess.Insert(u); err != nil {
 		return err
diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go
index c281672fe1..f59b795b4e 100644
--- a/modules/auth/user_form.go
+++ b/modules/auth/user_form.go
@@ -12,7 +12,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 
 	"github.com/go-macaron/binding"
-	"gopkg.in/macaron.v1"
+	macaron "gopkg.in/macaron.v1"
 )
 
 // InstallForm form for installation page
@@ -189,6 +189,30 @@ func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) bindi
 	return validate(errs, ctx.Data, f, ctx.Locale)
 }
 
+// UpdateThemeForm form for updating a users' theme
+type UpdateThemeForm struct {
+	Theme string `binding:"Required;MaxSize(30)"`
+}
+
+// Validate validates the field
+func (f *UpdateThemeForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
+	return validate(errs, ctx.Data, f, ctx.Locale)
+}
+
+// IsThemeExists checks if the theme is a theme available in the config.
+func (f UpdateThemeForm) IsThemeExists() bool {
+	var exists bool
+
+	for _, v := range setting.UI.Themes {
+		if strings.ToLower(v) == strings.ToLower(f.Theme) {
+			exists = true
+			break
+		}
+	}
+
+	return exists
+}
+
 // ChangePasswordForm form for changing password
 type ChangePasswordForm struct {
 	OldPassword string `form:"old_password" binding:"MaxSize(255)"`
diff --git a/modules/setting/defaults.go b/modules/setting/defaults.go
index 7630d10090..48257284c7 100644
--- a/modules/setting/defaults.go
+++ b/modules/setting/defaults.go
@@ -8,4 +8,5 @@ var (
 	defaultLangs                             = strings.Split("en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,uk-UA,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR", ",")
 	defaultLangNames                         = strings.Split("English,简体中文,繁體中文(香港),繁體中文(台灣),Deutsch,français,Nederlands,latviešu,русский,Українська,日本語,español,português do Brasil,polski,български,italiano,suomi,Türkçe,čeština,српски,svenska,한국어", ",")
 	defaultPullRequestWorkInProgressPrefixes = strings.Split("WIP:,[WIP]", ",")
+	defaultThemes                            = strings.Split("gitea", "arc-green")
 )
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 23304453d2..88101937e9 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -33,9 +33,9 @@ import (
 	"github.com/go-macaron/session"
 	_ "github.com/go-macaron/session/redis" // redis plugin for store session
 	"github.com/go-xorm/core"
-	"github.com/kballard/go-shellquote"
-	"github.com/mcuadros/go-version"
-	"gopkg.in/ini.v1"
+	shellquote "github.com/kballard/go-shellquote"
+	version "github.com/mcuadros/go-version"
+	ini "gopkg.in/ini.v1"
 	"strk.kbt.io/projects/go/libravatar"
 )
 
@@ -303,6 +303,7 @@ var (
 		MaxDisplayFileSize  int64
 		ShowUserEmail       bool
 		DefaultTheme        string
+		Themes              []string
 
 		Admin struct {
 			UserPagingNum   int
@@ -329,6 +330,7 @@ var (
 		ThemeColorMetaTag:   `#6cc644`,
 		MaxDisplayFileSize:  8388608,
 		DefaultTheme:        `gitea`,
+		Themes:              []string{`gitea`, `arc-green`},
 		Admin: struct {
 			UserPagingNum   int
 			RepoPagingNum   int
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 0835175f16..06fe12d10b 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -355,6 +355,7 @@ password_username_disabled = Non-local users are not allowed to change their use
 full_name = Full Name
 website = Website
 location = Location
+update_theme = Update Theme
 update_profile = Update Profile
 update_profile_success = Your profile has been updated.
 change_username = Your username has been changed.
@@ -362,6 +363,7 @@ change_username_prompt = Note: username changes also change your account URL.
 continue = Continue
 cancel = Cancel
 language = Language
+ui = Theme
 
 lookup_avatar_by_mail = Look Up Avatar by Email Address
 federated_avatar_lookup = Federated Avatar Lookup
@@ -382,14 +384,18 @@ password_change_disabled = Non-local users can not update their password through
 
 emails = Email Addresses
 manage_emails = Manage Email Addresses
+manage_themes = Select default theme
 manage_openid = Manage OpenID Addresses
 email_desc = Your primary email address will be used for notifications and other operations.
+theme_desc = This will be your default theme across the site.
 primary = Primary
 primary_email = Make Primary
 delete_email = Remove
 email_deletion = Remove Email Address
 email_deletion_desc = The email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue?
 email_deletion_success = The email address has been removed.
+theme_update_success = Your theme was updated.
+theme_update_error = The selected theme does not exist.
 openid_deletion = Remove OpenID Address
 openid_deletion_desc = Removing this OpenID address from your account will prevent you from signing in with it. Continue?
 openid_deletion_success = The OpenID address has been removed.
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index b776d0c201..6011427321 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -42,7 +42,7 @@ import (
 	"github.com/go-macaron/toolbox"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/tstranex/u2f"
-	"gopkg.in/macaron.v1"
+	macaron "gopkg.in/macaron.v1"
 )
 
 // NewMacaron initializes Macaron instance.
@@ -243,6 +243,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 			m.Post("/email", bindIgnErr(auth.AddEmailForm{}), userSetting.EmailPost)
 			m.Post("/email/delete", userSetting.DeleteEmail)
 			m.Post("/delete", userSetting.DeleteAccount)
+			m.Post("/theme", bindIgnErr(auth.UpdateThemeForm{}), userSetting.UpdateUIThemePost)
 		})
 		m.Group("/security", func() {
 			m.Get("", userSetting.Security)
@@ -292,6 +293,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 		})
 	}, reqSignIn, func(ctx *context.Context) {
 		ctx.Data["PageIsUserSettings"] = true
+		ctx.Data["AllThemes"] = setting.UI.Themes
 	})
 
 	m.Group("/user", func() {
diff --git a/routers/user/setting/account.go b/routers/user/setting/account.go
index bcf602c5ed..ff8631bf5b 100644
--- a/routers/user/setting/account.go
+++ b/routers/user/setting/account.go
@@ -168,6 +168,34 @@ func DeleteAccount(ctx *context.Context) {
 	}
 }
 
+// UpdateUIThemePost is used to update users' specific theme
+func UpdateUIThemePost(ctx *context.Context, form auth.UpdateThemeForm) {
+
+	ctx.Data["Title"] = ctx.Tr("settings")
+	ctx.Data["PageIsSettingsAccount"] = true
+
+	if ctx.HasError() {
+		ctx.Redirect(setting.AppSubURL + "/user/settings/account")
+		return
+	}
+
+	if !form.IsThemeExists() {
+		ctx.Flash.Error(ctx.Tr("settings.theme_update_error"))
+		ctx.Redirect(setting.AppSubURL + "/user/settings/account")
+		return
+	}
+
+	if err := ctx.User.UpdateTheme(form.Theme); err != nil {
+		ctx.Flash.Error(ctx.Tr("settings.theme_update_error"))
+		ctx.Redirect(setting.AppSubURL + "/user/settings/account")
+		return
+	}
+
+	log.Trace("Update user theme: %s", ctx.User.Name)
+	ctx.Flash.Success(ctx.Tr("settings.theme_update_success"))
+	ctx.Redirect(setting.AppSubURL + "/user/settings/account")
+}
+
 func loadAccountData(ctx *context.Context) {
 	emails, err := models.GetEmailAddresses(ctx.User.ID)
 	if err != nil {
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 47c0d6b473..b6afe94bb4 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -6,7 +6,7 @@
 	<meta http-equiv="x-ua-compatible" content="ie=edge">
 	<title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title>
 	<link rel="manifest" href="{{AppSubUrl}}/manifest.json">
-	
+
 	<script>
 		if ('serviceWorker' in navigator) {
   			window.addEventListener('load', function() {
@@ -147,7 +147,11 @@
 	<meta property="og:url" content="{{AppUrl}}" />
 	<meta property="og:description" content="{{MetaDescription}}">
 {{end}}
-{{if ne DefaultTheme "gitea"}}
+{{if .IsSigned }}
+	{{ if ne .SignedUser.Theme "gitea" }}
+		<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css">
+	{{end}}
+{{else if ne DefaultTheme "gitea"}}
 	<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css">
 {{end}}
 {{template "custom/header" .}}
diff --git a/templates/pwa/serviceworker_js.tmpl b/templates/pwa/serviceworker_js.tmpl
index 5e5860ebb0..f109d06300 100644
--- a/templates/pwa/serviceworker_js.tmpl
+++ b/templates/pwa/serviceworker_js.tmpl
@@ -32,10 +32,14 @@ var urlsToCache = [
   '{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css',
   '{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css',
   '{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.css',
-{{if ne DefaultTheme "gitea"}}
-  '{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css',
+{{if .IsSigned }}
+	{{ if ne .SignedUser.Theme "gitea" }}
+		'{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css'
+	{{end}}
+{{else if ne DefaultTheme "gitea"}}
+	'{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css'
 {{end}}
-  
+
   // img
   '{{AppSubUrl}}/img/gitea-sm.png',
   '{{AppSubUrl}}/img/gitea-lg.png',
diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl
index 56c3df7f84..778103e44d 100644
--- a/templates/user/settings/account.tmpl
+++ b/templates/user/settings/account.tmpl
@@ -85,6 +85,44 @@
 			</form>
 		</div>
 
+		<h4 class="ui top attached header">
+			{{.i18n.Tr "settings.manage_themes"}}
+		</h4>
+		<div class="ui attached segment">
+			<div class="ui email list">
+				<div class="item">
+					{{.i18n.Tr "settings.theme_desc"}}
+				</div>
+
+			<form class="ui form" action="{{.Link}}/theme" method="post">
+				{{.CsrfTokenHtml}}
+					<div class="field">
+						<label for="ui">{{.i18n.Tr "settings.ui"}}</label>
+						<div class="ui selection dropdown" id="ui">
+							<input name="theme" type="hidden" value="{{.SignedUser.Theme}}">
+							<i class="dropdown icon"></i>
+							<div class="text">
+								{{range $i,$a := .AllThemes}}
+								    {{if eq $.SignedUser.Theme $a}}{{$a}}{{end}}
+								{{end}}
+							</div>
+
+							<div class="menu">
+							{{range $i,$a := .AllThemes}}
+								<div class="item{{if eq $.SignedUser.Theme $a}} active selected{{end}}" data-value="{{$a}}">
+									{{$a}}
+								</div>
+							{{end}}
+							</div>
+						</div>
+					</div>
+
+				<div class="field">
+					<button class="ui green button">{{$.i18n.Tr "settings.update_theme"}}</button>
+				</div>
+			</form>
+			</div>
+		</div>
 		<h4 class="ui top attached warning header">
 			{{.i18n.Tr "settings.delete_account"}}
 		</h4>