diff --git a/README.md b/README.md
index a8fa474..2d6782a 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@
 - 🖥️ 支持 PWA,可在 Chrome/Edge 里点击地址栏右边的 ➕ 安装到电脑
 - 🙉 支持显示歌曲和专辑的 Explicit 标志
 - 📺 MV 播放
+- ✔️ 每日自动签到(手机端和电脑端同时签到)
+- 🌚 Light/Dark Mode 自动切换
 - 🚫🤝 无任何社交功能
 - 🛠 更多特性开发中
 
@@ -60,14 +62,9 @@ npm run build
 
 ## ☑️ Todo
 
-- 中文支持
-- Dark Mode
-- 歌词
-- 私人 FM
-- 播放记录
-- 无限播放模式(播放完列表后自动播放相似歌曲)
+查看 Todo 请访问本项目的 [Projects](https://github.com/qier222/YesPlayMusic/projects/1)
 
-欢迎提 issue 和 pull request。
+欢迎提 Issue 和 Pull request。
 
 ## 📜 开源许可
 
diff --git a/src/App.vue b/src/App.vue
index 8d1f41f..f177602 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -50,11 +50,36 @@ export default {
 <style lang="scss">
 @import url("https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,500;0,600;0,700;0,800;0,900;1,500;1,600;1,700;1,800;1,900&display=swap");
 
+:root {
+  --color-body-bg: #ffffff;
+  --color-text: #000;
+  --color-primary: #335eea;
+  --color-primary-bg: #eaeffd;
+  --color-secondary: #7a7a7b;
+  --color-secondary-bg: #f5f5f7;
+  --color-navbar-bg: rgba(255, 255, 255, 0.86);
+}
+
+[data-theme="dark"] {
+  --color-body-bg: #222222;
+  --color-text: #ffffff;
+  --color-primary: #335eea;
+  --color-primary-bg: #bbcdff;
+  --color-secondary: #7a7a7b;
+  --color-secondary-bg: #323232;
+  --color-navbar-bg: #335eea;
+  --color-navbar-bg: rgba(34, 34, 34, 0.86);
+}
+
 #app {
   font-family: "Barlow", -apple-system, BlinkMacSystemFont, Helvetica Neue,
     PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC,
     WenQuanYi Micro Hei, sans-serif;
   width: 100%;
+  transition: all 0.4s;
+}
+body {
+  background-color: var(--color-body-bg);
 }
 
 html {
@@ -102,12 +127,13 @@ a {
 
 ::-webkit-scrollbar-track {
   background: transparent;
+  border-left: 1px solid rgba(128, 128, 128, 0.18);
 }
 
 ::-webkit-scrollbar-thumb {
   -webkit-border-radius: 10px;
   border-radius: 10px;
-  background: rgb(216, 216, 216);
+  background: var(--color-secondary-bg);
 }
 
 .slide-up-enter-active,
diff --git a/src/api/album.js b/src/api/album.js
index 5149b03..214128e 100644
--- a/src/api/album.js
+++ b/src/api/album.js
@@ -1,6 +1,11 @@
 import request from "@/utils/request";
 import { mapTrackPlayableStatus } from "@/utils/common";
 
+/**
+ * 获取专辑内容
+ * 说明 : 调用此接口 , 传入专辑 id, 可获得专辑内容
+ * @param {number} id
+ */
 export function getAlbum(id) {
   return request({
     url: "/album",
@@ -14,10 +19,18 @@ export function getAlbum(id) {
   });
 }
 
+/**
+ * 全部新碟
+ * 说明 : 登录后调用此接口 ,可获取全部新碟
+ * - limit - 返回数量 , 默认为 30
+ * - offset - 偏移数量,用于分页 , 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
+ * - area - ALL:全部,ZH:华语,EA:欧美,KR:韩国,JP:日本
+ * @param {Object} params
+ * @param {number} params.limit
+ * @param {number=} params.offset
+ * @param {string} params.area
+ */
 export function newAlbums(params) {
-  // limit : 返回数量 , 默认为 30
-  // offset : 偏移数量,用于分页 , 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
-  // area : ALL:全部,ZH:华语,EA:欧美,KR:韩国,JP:日本
   return request({
     url: "/album/new",
     method: "get",
diff --git a/src/api/artist.js b/src/api/artist.js
index 259dadf..0a5ed88 100644
--- a/src/api/artist.js
+++ b/src/api/artist.js
@@ -1,6 +1,11 @@
 import request from "@/utils/request";
 import { mapTrackPlayableStatus } from "@/utils/common";
 
+/**
+ * 获取歌手单曲
+ * 说明 : 调用此接口 , 传入歌手 id, 可获得歌手部分信息和热门歌曲
+ * @param {number} id - 歌手 id, 可由搜索接口获得
+ */
 export function getArtist(id) {
   return request({
     url: "/artists",
@@ -14,10 +19,18 @@ export function getArtist(id) {
   });
 }
 
+/**
+ * 获取歌手专辑
+ * 说明 : 调用此接口 , 传入歌手 id, 可获得歌手专辑内容
+ * - id: 歌手 id
+ * - limit: 取出数量 , 默认为 50
+ * - offset: 偏移数量 , 用于分页 , 如 :( 页数 -1)*50, 其中 50 为 limit 的值 , 默认为 0
+ * @param {Object} params
+ * @param {number} params.id
+ * @param {number=} params.limit
+ * @param {number=} params.offset
+ */
 export function getArtistAlbum(params) {
-  // 必选参数 : id: 歌手 id
-  // 可选参数 : limit: 取出数量 , 默认为 50
-  // offset: 偏移数量 , 用于分页 , 如 :( 页数 -1)*50, 其中 50 为 limit 的值 , 默认 为 0
   return request({
     url: "/artist/album",
     method: "get",
@@ -25,12 +38,17 @@ export function getArtistAlbum(params) {
   });
 }
 
+/**
+ * 歌手榜
+ * 说明 : 调用此接口 , 可获取排行榜中的歌手榜
+ * - type : 地区
+ * 1: 华语
+ * 2: 欧美
+ * 3: 韩国
+ * 4: 日本
+ * @param {number=} type
+ */
 export function toplistOfArtists(type = null) {
-  // type : 地区
-  // 1: 华语
-  // 2: 欧美
-  // 3: 韩国
-  // 4: 日本
   return request({
     url: "/toplist/artist",
     method: "get",
@@ -39,7 +57,11 @@ export function toplistOfArtists(type = null) {
     },
   });
 }
-
+/**
+ * 获取歌手 mv
+ * 说明 : 调用此接口 , 传入歌手 id, 可获得歌手 mv 信息 , 具体 mv 播放地址可调 用/mv传入此接口获得的 mvid 来拿到 , 如 : /artist/mv?id=6452,/mv?mvid=5461064
+ * @param {number} id 歌手 id, 可由搜索接口获得
+ */
 export function artistMv(id) {
   return request({
     url: "/artist/mv",
diff --git a/src/api/auth.js b/src/api/auth.js
index da40e6a..461ac01 100644
--- a/src/api/auth.js
+++ b/src/api/auth.js
@@ -1,25 +1,35 @@
 import request from "@/utils/request";
 
+/**
+ * 手机登录
+ * - phone: 手机号码
+ * - password: 密码
+ * - countrycode: 国家码,用于国外手机号登录,例如美国传入:1
+ * - md5_password: md5加密后的密码,传入后 password 将失效
+ * @param {Object} params
+ * @param {string} params.phone
+ * @param {string} params.password
+ * @param {string=} params.countrycode
+ * @param {string=} params.md5_password
+ */
 export function loginWithPhone(params) {
-  //必选参数 :
-  // phone: 手机号码
-  // password: 密码
-  // 可选参数 :
-  // countrycode: 国家码,用于国外手机号登录,例如美国传入:1
-  // md5_password: md5加密后的密码,传入后 password 将失效
   return request({
     url: "/login/cellphone",
     method: "post",
     params,
   });
 }
-
+/**
+ * 邮箱登录
+ * - email: 163 网易邮箱
+ * - password: 密码
+ * - md5_password: md5加密后的密码,传入后 password 将失效
+ * @param {Object} params
+ * @param {string} params.email
+ * @param {string} params.password
+ * @param {string=} params.md5_password
+ */
 export function loginWithEmail(params) {
-  // 必选参数 :
-  // email: 163 网易邮箱
-  // password: 密码
-  // 可选参数 :
-  // md5_password: md5加密后的密码,传入后 password 将失效
   return request({
     url: "/login",
     method: "post",
@@ -27,13 +37,22 @@ export function loginWithEmail(params) {
   });
 }
 
-export function loginRefresh() {
+/**
+ * 刷新登录
+ * 说明 : 调用此接口 , 可刷新登录状态
+ * - 调用例子 : /login/refresh
+ */
+export function refreshCookie() {
   return request({
     url: "/login/refresh",
     method: "post",
   });
 }
 
+/**
+ * 退出登录
+ * 说明 : 调用此接口 , 可退出登录
+ */
 export function logout() {
   return request({
     url: "/logout",
diff --git a/src/api/mv.js b/src/api/mv.js
index b114afc..d00b095 100644
--- a/src/api/mv.js
+++ b/src/api/mv.js
@@ -1,5 +1,12 @@
 import request from "@/utils/request";
 
+/**
+ * 获取 mv 数据
+ * 说明 : 调用此接口 , 传入 mvid ( 在搜索音乐的时候传 type=1004 获得 ) , 可获取对应 MV 数据 , 数据包含 mv 名字 , 歌手 , 发布时间 , mv 视频地址等数据 ,
+ * 其中 mv 视频 网易做了防盗链处理 , 可能不能直接播放 , 需要播放的话需要调用 ' mv 地址' 接口
+ * - 调用例子 : /mv/detail?mvid=5436712
+ * @param {number} mvid mv 的 id
+ */
 export function mvDetail(mvid) {
   return request({
     url: "/mv/detail",
@@ -10,16 +17,29 @@ export function mvDetail(mvid) {
   });
 }
 
+/**
+ * mv 地址
+ * 说明 : 调用此接口 , 传入 mv id,可获取 mv 播放地址
+ * - id: mv id
+ * - r: 分辨率,默认1080,可从 /mv/detail 接口获取分辨率列表
+ * - 调用例子 : /mv/url?id=5436712 /mv/url?id=10896407&r=1080
+ * @param {Object} params
+ * @param {number} params.id
+ * @param {number=} params.r
+ */
+
 export function mvUrl(params) {
-  // 必选参数 : id: mv id
-  // 可选参数 : r: 分辨率,默认1080,可从 /mv/detail 接口获取分辨率列表
   return request({
     url: "/mv/url",
     method: "get",
     params,
   });
 }
-
+/**
+ * 相似 mv
+ * 说明 : 调用此接口 , 传入 mvid 可获取相似 mv
+ * @param {number} mvid
+ */
 export function simiMv(mvid) {
   return request({
     url: "/simi/mv",
diff --git a/src/api/others.js b/src/api/others.js
index 0582e9e..f63423a 100644
--- a/src/api/others.js
+++ b/src/api/others.js
@@ -1,6 +1,21 @@
 import request from "@/utils/request";
 import { mapTrackPlayableStatus } from "@/utils/common";
 
+/**
+ * 搜索
+ * 说明 : 调用此接口 , 传入搜索关键词可以搜索该音乐 / 专辑 / 歌手 / 歌单 / 用户 , 关键词可以多个 , 以空格隔开 ,
+ * 如 " 周杰伦 搁浅 "( 不需要登录 ), 搜索获取的 mp3url 不能直接用 , 可通过 /song/url 接口传入歌曲 id 获取具体的播放链接
+ * - keywords : 关键词
+ * - limit : 返回数量 , 默认为 30
+ * - offset : 偏移数量,用于分页 , 如 : 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
+ * - type: 搜索类型;默认为 1 即单曲 , 取值意义 : 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频, 1018:综合
+ * - 调用例子 : /search?keywords=海阔天空 /cloudsearch?keywords=海阔天空(更全)
+ * @param {Object} params
+ * @param {string} params.keywords
+ * @param {number=} params.limit
+ * @param {number=} params.offset
+ * @param {number=} params.type
+ */
 export function search(params) {
   return request({
     url: "/search",
diff --git a/src/api/playlist.js b/src/api/playlist.js
index 584675d..d643b50 100644
--- a/src/api/playlist.js
+++ b/src/api/playlist.js
@@ -1,23 +1,44 @@
 import request from "@/utils/request";
 import { mapTrackPlayableStatus } from "@/utils/common";
 
+/**
+ * 推荐歌单
+ * 说明 : 调用此接口 , 可获取推荐歌单
+ * - limit: 取出数量 , 默认为 30 (不支持 offset)
+ * - 调用例子 : /personalized?limit=1
+ * @param {Object} params
+ * @param {number=} params.limit
+ */
 export function recommendPlaylist(params) {
-  // limit: 取出数量 , 默认为 30
   return request({
     url: "/personalized",
     method: "get",
     params,
   });
 }
+/**
+ * 获取每日推荐歌单
+ * 说明 : 调用此接口 , 可获得每日推荐歌单 ( 需要登录 )
+ * @param {Object} params
+ * @param {number=} params.limit
+ */
 export function dailyRecommendPlaylist(params) {
-  // limit: 取出数量 , 默认为 30
   return request({
     url: "/recommend/resource",
     method: "get",
     params,
   });
 }
-
+/**
+ * 获取歌单详情
+ * 说明 : 歌单能看到歌单名字, 但看不到具体歌单内容 , 调用此接口 , 传入歌单 id, 可以获取对应歌单内的所有的音乐(未登录状态只能获取不完整的歌单,登录后是完整的),
+ * 但是返回的trackIds是完整的,tracks 则是不完整的,可拿全部 trackIds 请求一次 song/detail 接口
+ * 获取所有歌曲的详情 (https://github.com/Binaryify/NeteaseCloudMusicApi/issues/452)
+ * - id : 歌单 id
+ * - s : 歌单最近的 s 个收藏者, 默认为8
+ * @param {number} id
+ * @param {boolean=} noCache
+ */
 export function getPlaylistDetail(id, noCache = false) {
   let params = { id };
   if (noCache) params.timestamp = new Date().getTime();
@@ -30,11 +51,18 @@ export function getPlaylistDetail(id, noCache = false) {
     return data;
   });
 }
-
+/**
+ * 获取精品歌单
+ * 说明 : 调用此接口 , 可获取精品歌单
+ * - cat: tag, 比如 " 华语 "、" 古风 " 、" 欧美 "、" 流行 ", 默认为 "全部", 可从精品歌单标签列表接口获取(/playlist/highquality/tags)
+ * - limit: 取出歌单数量 , 默认为 20
+ * - before: 分页参数,取上一页最后一个歌单的 updateTime 获取下一页数据
+ * @param {Object} params
+ * @param {string} params.cat
+ * @param {number=} params.limit
+ * @param {number} params.before
+ */
 export function highQualityPlaylist(params) {
-  // 可选参数: cat: tag, 比如 " 华语 "、" 古风 " 、" 欧美 "、" 流行 ", 默认为 "全部", 可从精品歌单标签列表接口获取(/playlist/highquality / tags)
-  // limit: 取出歌单数量 , 默认为 20
-  // before: 分页参数,取上一页最后一个歌单的 updateTime 获取下一页数据
   return request({
     url: "/top/playlist/highquality",
     method: "get",
@@ -42,11 +70,18 @@ export function highQualityPlaylist(params) {
   });
 }
 
+/**
+ * 歌单 ( 网友精选碟 )
+ * 说明 : 调用此接口 , 可获取网友精选碟歌单
+ * - order: 可选值为 'new' 和 'hot', 分别对应最新和最热 , 默认为 'hot'
+ * - cat: tag, 比如 " 华语 "、" 古风 " 、" 欧美 "、" 流行 ", 默认为 "全部",可从歌单分类接口获取(/playlist/catlist)
+ * - limit: 取出歌单数量 , 默认为 50
+ * @param {Object} params
+ * @param {string} params.order
+ * @param {string} params.cat
+ * @param {number=} params.limit
+ */
 export function topPlaylist(params) {
-  //  可选参数 : order: 可选值为 'new' 和 'hot', 分别对应最新和最热 , 默认为 'hot'
-  // cat:cat: tag, 比如 " 华语 "、" 古风 " 、" 欧美 "、" 流行 ", 默认为 "全部",可从歌单分类接口获取(/playlist/catlist)
-  // limit: 取出歌单数量 , 默认为 50
-  // offset: 偏移数量 , 用于分页 , 如 :( 评论页数 -1)*50, 其中 50 为 limit 的值
   return request({
     url: "/top/playlist",
     method: "get",
@@ -54,23 +89,36 @@ export function topPlaylist(params) {
   });
 }
 
+/**
+ * 歌单分类
+ * 说明 : 调用此接口,可获取歌单分类,包含 category 信息
+ */
 export function playlistCatlist() {
   return request({
     url: "/playlist/catlist",
     method: "get",
   });
 }
-
+/**
+ * 所有榜单
+ * 说明 : 调用此接口,可获取所有榜单 接口地址 : /toplist
+ */
 export function toplists() {
   return request({
     url: "/toplist",
     method: "get",
   });
 }
-
+/**
+ * 收藏/取消收藏歌单
+ * 说明 : 调用此接口, 传入类型和歌单 id 可收藏歌单或者取消收藏歌单
+ * - t : 类型,1:收藏,2:取消收藏
+ * - id : 歌单 id
+ * @param {Object} params
+ * @param {number} params.t
+ * @param {number} params.id
+ */
 export function subscribePlaylist(params) {
-  // 必选参数 :
-  // t : 类型,1:收藏,2:取消收藏 id : 歌单 id
   return request({
     url: "/playlist/subscribe",
     method: "get",
diff --git a/src/api/track.js b/src/api/track.js
index f3244fd..11876c7 100644
--- a/src/api/track.js
+++ b/src/api/track.js
@@ -1,7 +1,12 @@
+import store from "@/store";
 import request from "@/utils/request";
 import { mapTrackPlayableStatus } from "@/utils/common";
-import store from "@/store";
-
+/**
+ * 获取音乐 url
+ * 说明 : 使用歌单详情接口后 , 能得到的音乐的 id, 但不能得到的音乐 url, 调用此接口, 传入的音乐 id( 可多个 , 用逗号隔开 ), 可以获取对应的音乐的 url,
+ * !!!未登录状态返回试听片段(返回字段包含被截取的正常歌曲的开始时间和结束时间)
+ * @param {string} id - 音乐的 id,例如 id=405998841,33894312
+ */
 export function getMP3(id) {
   let br =
     store.state.settings?.musicQuality !== undefined
@@ -16,37 +21,44 @@ export function getMP3(id) {
     },
   });
 }
-
-export function getTrackDetail(id) {
+/**
+ * 获取歌曲详情
+ * 说明 : 调用此接口 , 传入音乐 id(支持多个 id, 用 , 隔开), 可获得歌曲详情(注意:歌曲封面现在需要通过专辑内容接口获取)
+ * @param {string} ids - 音乐 id, 例如 ids=405998841,33894312
+ */
+export function getTrackDetail(ids) {
   return request({
     url: "/song/detail",
     method: "get",
     params: {
-      ids: id,
+      ids,
     },
   }).then((data) => {
     data.songs = mapTrackPlayableStatus(data.songs);
     return data;
   });
 }
+/**
+ * 获取歌词
+ * 说明 : 调用此接口 , 传入音乐 id 可获得对应音乐的歌词 ( 不需要登录 )
+ * @param {number} id - 音乐 id
+ */
 
 export function getLyric(id) {
   return request({
     url: "/lyric",
     method: "get",
     params: {
-      id: id,
+      id,
     },
   });
 }
-
+/**
+ * 新歌速递
+ * 说明 : 调用此接口 , 可获取新歌速递
+ * @param {number} type - 地区类型 id, 对应以下: 全部:0 华语:7 欧美:96 日本:8 韩国:16
+ */
 export function topSong(type) {
-  //   type: 地区类型 id,对应以下:
-  // 全部:0
-  // 华语:7
-  // 欧美:96
-  // 日本:8
-  // 韩国:16
   return request({
     url: "/top/song",
     method: "get",
@@ -55,10 +67,16 @@ export function topSong(type) {
     },
   });
 }
-
+/**
+ * 喜欢音乐
+ * 说明 : 调用此接口 , 传入音乐 id, 可喜欢该音乐
+ * - id - 歌曲 id
+ * - like - 默认为 true 即喜欢 , 若传 false, 则取消喜欢
+ * @param {Object} params
+ * @param {number} params.id
+ * @param {boolean=} [params.like]
+ */
 export function likeATrack(params) {
-  // 必选参数: id: 歌曲 id
-  // 可选参数 : like: 布尔值 , 默认为 true 即喜欢 , 若传 false, 则取消喜欢
   params.timestamp = new Date().getTime();
   return request({
     url: "/like",
@@ -67,9 +85,18 @@ export function likeATrack(params) {
   });
 }
 
+/**
+ * 听歌打卡
+ * 说明 : 调用此接口 , 传入音乐 id, 来源 id,歌曲时间 time,更新听歌排行数据
+ * - id - 歌曲 id
+ * - sourceid - 歌单或专辑 id
+ * - time - 歌曲播放时间,单位为秒
+ * @param {Object} params
+ * @param {number} params.id
+ * @param {number} params.sourceid
+ * @param {number=} params.time
+ */
 export function scrobble(params) {
-  // 必选参数 : id: 歌曲 id, sourceid: 歌单或专辑 id
-  // 可选参数 : time: 歌曲播放时间,单位为秒
   params.timestamp = new Date().getTime();
   return request({
     url: "/scrobble",
diff --git a/src/api/user.js b/src/api/user.js
index edabc43..dbb3a05 100644
--- a/src/api/user.js
+++ b/src/api/user.js
@@ -1,19 +1,11 @@
 import request from "@/utils/request";
 
-export function login(params) {
-  //     必选参数 :
-  // phone: 手机号码
-  // password: 密码
-  // 可选参数 :
-  // countrycode: 国家码,用于国外手机号登陆,例如美国传入:1
-  // md5_password: md5加密后的密码,传入后 password 将失效
-  return request({
-    url: "/login/cellphone",
-    method: "get",
-    params,
-  });
-}
-
+/**
+ * 获取用户详情
+ * 说明 : 登录后调用此接口 , 传入用户 id, 可以获取用户详情
+ * - uid : 用户 id
+ * @param {number} uid
+ */
 export function userDetail(uid) {
   return request({
     url: "/user/detail",
@@ -24,9 +16,18 @@ export function userDetail(uid) {
   });
 }
 
+/**
+ * 获取用户歌单
+ * 说明 : 登录后调用此接口 , 传入用户 id, 可以获取用户歌单
+ * - uid : 用户 id
+ * - limit : 返回数量 , 默认为 30
+ * - offset : 偏移数量,用于分页 , 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
+ * @param {Object} params
+ * @param {number} params.uid
+ * @param {number} params.limit
+ * @param {number=} params.offset
+ */
 export function userPlaylist(params) {
-  // limit : 返回数量 , 默认为 30
-  // offset : 偏移数量,用于分页 , 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
   return request({
     url: "/user/playlist",
     method: "get",
@@ -34,6 +35,12 @@ export function userPlaylist(params) {
   });
 }
 
+/**
+ * 喜欢音乐列表
+ * 说明 : 调用此接口 , 传入用户 id, 可获取已喜欢音乐id列表(id数组)
+ * - uid: 用户 id
+ * @param {number} uid
+ */
 export function userLikedSongsIDs(uid) {
   return request({
     url: "/likelist",
@@ -44,3 +51,14 @@ export function userLikedSongsIDs(uid) {
     },
   });
 }
+
+export function dailySignin(type = 0) {
+  //可选参数 : type: 签到类型 , 默认 0, 其中 0 为安卓端签到 ,1 为 web/PC 签到
+  return request({
+    url: "/daily_signin",
+    method: "post",
+    params: {
+      type,
+    },
+  });
+}
diff --git a/src/assets/css/slider.css b/src/assets/css/slider.css
index 87a51cd..58e197d 100644
--- a/src/assets/css/slider.css
+++ b/src/assets/css/slider.css
@@ -1,6 +1,6 @@
 /* rail style */
 .vue-slider-rail {
-  background-color: #eee;
+  background-color: rgba(128, 128, 128, 0.18);
   border-radius: 15px;
 }
 
@@ -54,7 +54,8 @@
 
 /* volume style */
 .volume-control .vue-slider-process {
-  background-color: rgba(0, 0, 0, 0.8);
+  opacity: 0.8;
+  background-color: var(--color-text);
   border-radius: 15px;
 }
 
diff --git a/src/components/ButtonIcon.vue b/src/components/ButtonIcon.vue
index 3f93ecf..12d6af6 100644
--- a/src/components/ButtonIcon.vue
+++ b/src/components/ButtonIcon.vue
@@ -19,7 +19,7 @@ button {
   border-radius: 25%;
   transition: 0.2s;
   .svg-icon {
-    color: rgba(0, 0, 0, 0.88);
+    color: var(--color-text);
     height: 16px;
     width: 16px;
   }
@@ -27,7 +27,7 @@ button {
     margin-left: 0;
   }
   &:hover {
-    background: #f5f5f7;
+    background: var(--color-secondary-bg);
   }
   &:active {
     transform: scale(0.92);
diff --git a/src/components/ButtonTwoTone.vue b/src/components/ButtonTwoTone.vue
index 576116c..48bec5b 100644
--- a/src/components/ButtonTwoTone.vue
+++ b/src/components/ButtonTwoTone.vue
@@ -54,8 +54,8 @@ button {
   justify-content: center;
   font-size: 18px;
   font-weight: 600;
-  background-color: rgba(51, 94, 234, 0.1);
-  color: #335eea;
+  background-color: var(--color-primary-bg);
+  color: var(--color-primary);
   margin-right: 12px;
   transition: 0.2s;
   .svg-icon {
@@ -70,8 +70,8 @@ button {
   }
 }
 button.grey {
-  background-color: #f5f5f7;
-  color: rgba(0, 0, 0, 0.5);
+  background-color: var(--color-secondary-bg);
+  color: var(--color-secondary);
 }
 button.transparent {
   background-color: transparent;
diff --git a/src/components/Cover.vue b/src/components/Cover.vue
index c074211..f821543 100644
--- a/src/components/Cover.vue
+++ b/src/components/Cover.vue
@@ -163,6 +163,7 @@ export default {
   filter: blur(16px) opacity(0.6);
   z-index: -1;
   height: 208px;
+  transform: scale(0.98);
 }
 .play-button {
   opacity: 0;
diff --git a/src/components/CoverRow.vue b/src/components/CoverRow.vue
index 66f7d1f..7579dc0 100644
--- a/src/components/CoverRow.vue
+++ b/src/components/CoverRow.vue
@@ -129,13 +129,13 @@ export default {
 
 .item {
   margin: 12px 12px 24px 12px;
+  color: var(--color-text);
   .text {
     width: 208px;
     margin-top: 8px;
     .name {
       font-size: 16px;
       font-weight: 600;
-      color: rgba(0, 0, 0, 0.88);
       line-height: 20px;
 
       display: -webkit-box;
@@ -145,7 +145,7 @@ export default {
     }
     .info {
       font-size: 12px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
       line-height: 18px;
       display: -webkit-box;
       -webkit-box-orient: vertical;
@@ -170,7 +170,8 @@ export default {
 }
 
 .explicit-symbol {
-  color: rgba(0, 0, 0, 0.28);
+  opacity: 0.28;
+  color: var(--color-text);
   float: right;
   .svg-icon {
     margin-bottom: -3px;
@@ -178,7 +179,8 @@ export default {
 }
 
 .lock-icon {
-  color: rgba(0, 0, 0, 0.28);
+  opacity: 0.28;
+  color: var(--color-text);
   margin-right: 4px;
   // float: right;
   .svg-icon {
@@ -189,7 +191,8 @@ export default {
 
 .play-count {
   font-weight: 600;
-  color: rgba(0, 0, 0, 0.58);
+  opacity: 0.58;
+  color: var(--color-text);
   font-size: 12px;
   .svg-icon {
     margin-right: 3px;
diff --git a/src/components/MvRow.vue b/src/components/MvRow.vue
index e80c9b5..5811f49 100644
--- a/src/components/MvRow.vue
+++ b/src/components/MvRow.vue
@@ -76,11 +76,12 @@ export default {
 .mv {
   margin: 12px 12px 24px 12px;
   width: 204px;
+  color: var(--color-text);
 
   .title {
     font-size: 16px;
     font-weight: 600;
-    color: rgba(0, 0, 0, 0.88);
+    opacity: 0.88;
     display: -webkit-box;
     -webkit-box-orient: vertical;
     -webkit-line-clamp: 2;
@@ -88,7 +89,7 @@ export default {
   }
   .artist {
     font-size: 12px;
-    color: rgba(0, 0, 0, 0.68);
+    opacity: 0.68;
     display: -webkit-box;
     -webkit-box-orient: vertical;
     -webkit-line-clamp: 2;
diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue
index 9bd5669..84d696e 100644
--- a/src/components/Navbar.vue
+++ b/src/components/Navbar.vue
@@ -24,7 +24,10 @@
       >
     </div>
     <div class="right-part">
-      <a href="https://github.com/qier222/YesPlayMusic" target="blank"
+      <a
+        href="https://github.com/qier222/YesPlayMusic"
+        target="blank"
+        v-if="settings.showGithubIcon !== false"
         ><svg-icon icon-class="github" class="github"
       /></a>
       <div class="search-box">
@@ -47,6 +50,7 @@
 
 <script>
 import ButtonIcon from "@/components/ButtonIcon.vue";
+import { mapState } from "vuex";
 
 export default {
   name: "Navbar",
@@ -60,6 +64,9 @@ export default {
       langs: ["zh-CN", "en"],
     };
   },
+  computed: {
+    ...mapState(["settings"]),
+  },
   methods: {
     go(where) {
       if (where === "back") this.$router.go(-1);
@@ -96,7 +103,10 @@ nav {
     left: 10vw;
   }
   backdrop-filter: saturate(180%) blur(30px);
-  background-color: rgba(255, 255, 255, 0.86);
+
+  // background: var(--color-body-bg);
+  // background-color: rgba(255, 255, 255, 0.86);
+  background-color: var(--color-navbar-bg);
   z-index: 100;
 }
 
@@ -120,15 +130,15 @@ nav {
     text-decoration: none;
     border-radius: 6px;
     padding: 6px 10px;
-    color: black;
+    color: var(--color-text);
     transition: 0.2s;
     margin: {
       right: 12px;
       left: 12px;
     }
     &:hover {
-      background: #eaeffd;
-      color: #335eea;
+      background: var(--color-primary-bg);
+      color: var(--color-primary);
     }
     &:active {
       transform: scale(0.92);
@@ -136,7 +146,7 @@ nav {
     }
   }
   a.active {
-    color: #335eea;
+    color: var(--color-primary);
   }
 }
 
@@ -156,7 +166,7 @@ nav {
     display: flex;
     align-items: center;
     height: 32px;
-    background: rgba(0, 0, 0, 0.06);
+    background: var(--color-secondary-bg);
     border-radius: 8px;
     width: 200px;
   }
@@ -164,7 +174,8 @@ nav {
   .svg-icon {
     height: 15px;
     width: 15px;
-    color: #aaaaaa;
+    color: var(--color-text);
+    opacity: 0.28;
     margin: {
       left: 8px;
       right: 4px;
@@ -178,13 +189,15 @@ nav {
     width: 96%;
     font-weight: 600;
     margin-top: -1px;
+    color: var(--color-text);
   }
 
   .active {
-    background: #eaeffd;
+    background: var(--color-primary-bg);
     input,
     .svg-icon {
-      color: #335eea;
+      opacity: 1;
+      color: var(--color-primary);
     }
   }
 }
@@ -198,6 +211,7 @@ nav {
     margin-right: 16px;
     height: 24px;
     width: 24px;
+    color: var(--color-text);
   }
 }
 </style>
diff --git a/src/components/Player.vue b/src/components/Player.vue
index 12f661e..5311eda 100644
--- a/src/components/Player.vue
+++ b/src/components/Player.vue
@@ -36,7 +36,8 @@
             </span>
           </div>
         </div>
-        <div class="like-button" v-show="isLoggedIn">
+        <!-- 账号登录才会显示 like 图标 -->
+        <div class="like-button" v-show="accountLogin">
           <button-icon
             @click.native="likeCurrentSong"
             :title="$t('player.like')"
@@ -118,7 +119,7 @@
 <script>
 import { updateMediaSessionMetaData } from "@/utils/mediaSession";
 import { mapState, mapMutations, mapActions } from "vuex";
-import { isLoggedIn } from "@/utils/auth";
+import { isAccountLoggedIn } from "@/utils/auth";
 import { userLikedSongsIDs } from "@/api/user";
 import { likeATrack } from "@/api/track";
 import "@/assets/css/slider.css";
@@ -144,14 +145,14 @@ export default {
     setInterval(() => {
       this.progress = ~~this.howler.seek();
     }, 1000);
-    if (this.isLoggedIn) {
+    if (isAccountLoggedIn()) {
       userLikedSongsIDs(this.settings.user.userId).then((data) => {
         this.updateLikedSongs(data.ids);
       });
     }
   },
   computed: {
-    ...mapState(["player", "howler", "settings", "liked"]),
+    ...mapState(["player", "howler", "settings", "liked", "accountLogin"]),
     currentTrack() {
       return this.player.currentTrack;
     },
@@ -174,9 +175,6 @@ export default {
       let max = ~~(this.currentTrack.dt / 1000);
       return max > 1 ? max - 1 : max;
     },
-    isLoggedIn() {
-      return isLoggedIn();
-    },
   },
   methods: {
     ...mapMutations([
@@ -296,7 +294,8 @@ export default {
   justify-content: space-around;
   height: 64px;
   backdrop-filter: saturate(180%) blur(30px);
-  background-color: rgba(255, 255, 255, 0.86);
+  // background-color: rgba(255, 255, 255, 0.86);
+  background-color: var(--color-navbar-bg);
   z-index: 100;
 }
 
@@ -336,7 +335,8 @@ export default {
     .name {
       font-weight: 600;
       font-size: 16px;
-      color: rgba(0, 0, 0, 0.88);
+      opacity: 0.88;
+      color: var(--color-text);
       margin-bottom: 4px;
       cursor: pointer;
       display: -webkit-box;
@@ -350,7 +350,8 @@ export default {
     }
     .artist {
       font-size: 12px;
-      color: rgba(0, 0, 0, 0.58);
+      opacity: 0.58;
+      color: var(--color-text);
       display: -webkit-box;
       -webkit-box-orient: vertical;
       -webkit-line-clamp: 1;
@@ -396,7 +397,7 @@ export default {
     }
   }
   .active .svg-icon {
-    color: #335eea;
+    color: var(--color-primary);
   }
   .volume-control {
     margin-left: 4px;
diff --git a/src/components/TrackListItem.vue b/src/components/TrackListItem.vue
index bb574ae..1513541 100644
--- a/src/components/TrackListItem.vue
+++ b/src/components/TrackListItem.vue
@@ -60,7 +60,7 @@
       <div></div>
     </div>
     <div class="actions" v-if="!isTracklist">
-      <button v-if="isLoggedIn" @click="likeThisSong">
+      <button v-if="accountLogin" @click="likeThisSong">
         <svg-icon
           icon-class="heart"
           :style="{
@@ -78,7 +78,7 @@
 </template>
 
 <script>
-import { isLoggedIn } from "@/utils/auth";
+import { mapState } from "vuex";
 
 import ArtistsInLine from "@/components/ArtistsInLine.vue";
 import ExplicitSymbol from "@/components/ExplicitSymbol.vue";
@@ -93,6 +93,7 @@ export default {
     return { focus: false, trackStyle: {} };
   },
   computed: {
+    ...mapState(["accountLogin"]),
     imgUrl() {
       if (this.track.al !== undefined) return this.track.al.picUrl;
       if (this.track.album !== undefined) return this.track.album.picUrl;
@@ -127,9 +128,6 @@ export default {
       if (this.isPlaying) trackClass.push("playing");
       return trackClass;
     },
-    isLoggedIn() {
-      return isLoggedIn();
-    },
   },
   methods: {
     goToAlbum() {
@@ -161,7 +159,7 @@ button {
   .svg-icon {
     height: 16px;
     width: 16px;
-    color: #335eea;
+    color: var(--color-primary);
   }
   &:active {
     transform: scale(0.92);
@@ -182,12 +180,16 @@ button {
     border-radius: 8px;
     margin: 0 20px 0 10px;
     width: 12px;
-    color: rgba(0, 0, 0, 0.58);
+    color: var(--color-text);
     cursor: default;
+    span {
+      opacity: 0.58;
+    }
   }
 
   .explicit-symbol {
-    color: rgba(0, 0, 0, 0.28);
+    opacity: 0.28;
+    color: var(--color-text);
     .svg-icon {
       margin-bottom: -3px;
     }
@@ -223,7 +225,7 @@ button {
     .title {
       font-size: 18px;
       font-weight: 600;
-      color: rgba(0, 0, 0, 0.88);
+      color: var(--color-text);
       cursor: default;
       padding-right: 16px;
       display: -webkit-box;
@@ -235,13 +237,14 @@ button {
         margin-right: 2px;
         font-weight: 500;
         font-size: 14px;
-        color: rgba(0, 0, 0, 0.72);
+        opacity: 0.72;
       }
     }
     .artist {
       margin-top: 2px;
       font-size: 13px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
+      color: var(--color-text);
       display: -webkit-box;
       -webkit-box-orient: vertical;
       -webkit-line-clamp: 1;
@@ -249,7 +252,7 @@ button {
       a {
         span {
           margin-right: 3px;
-          color: rgba(0, 0, 0, 0.8);
+          opacity: 0.8;
         }
         &:hover {
           text-decoration: underline;
@@ -262,7 +265,8 @@ button {
     flex: 1;
     display: flex;
     font-size: 16px;
-    color: rgba(0, 0, 0, 0.88);
+    opacity: 0.88;
+    color: var(--color-text);
     display: -webkit-box;
     -webkit-box-orient: vertical;
     -webkit-line-clamp: 2;
@@ -276,10 +280,12 @@ button {
     justify-content: flex-end;
     margin-right: 10px;
     font-variant-numeric: tabular-nums;
+    opacity: 0.88;
+    color: var(--color-text);
   }
   &:hover {
     transition: all 0.3s;
-    background: #f5f5f7;
+    background: var(--color-secondary-bg);
   }
 }
 .track.disable {
@@ -292,7 +298,7 @@ button {
   .time,
   .no,
   .featured {
-    color: rgba(0, 0, 0, 0.28) !important;
+    opacity: 0.28 !important;
   }
   &:hover {
     background: none;
@@ -327,24 +333,22 @@ button {
 }
 
 .track.playing {
-  background: #eaeffd;
-  color: #335eea;
+  background: var(--color-primary-bg);
+  color: var(--color-primary);
   .title,
-  .album {
-    color: #335eea;
+  .album,
+  .time {
+    color: var(--color-primary);
   }
   .title .featured,
-  .artist {
-    color: #335eea;
+  .artist,
+  .explicit-symbol {
+    color: var(--color-primary);
     opacity: 0.88;
   }
   .no span {
-    color: #335eea;
+    color: var(--color-primary);
     opacity: 0.78;
   }
-  .explicit-symbol {
-    color: #335eea;
-    opacity: 0.88;
-  }
 }
 </style>
diff --git a/src/locale/lang/en.js b/src/locale/lang/en.js
index 051e7b3..f43b6a1 100644
--- a/src/locale/lang/en.js
+++ b/src/locale/lang/en.js
@@ -108,5 +108,11 @@ export default {
       high: "High",
       lossless: "Lossless",
     },
+    appearance: {
+      text: "Appearance",
+      auto: "Auto",
+      light: "Light",
+      dark: "Dark",
+    },
   },
 };
diff --git a/src/locale/lang/zh-CN.js b/src/locale/lang/zh-CN.js
index d14746d..2dd4dad 100644
--- a/src/locale/lang/zh-CN.js
+++ b/src/locale/lang/zh-CN.js
@@ -6,9 +6,6 @@ export default {
     library: "资料库",
     search: "搜索",
   },
-  footer: {
-    settings: "设置",
-  },
   home: {
     recommendPlaylist: "推荐歌单",
     recommendArtist: "推荐歌手",
@@ -34,7 +31,7 @@ export default {
     albums: "专辑",
     withAlbums: "张专辑",
     artist: "歌手",
-    videos: "个视频",
+    videos: "个MV",
   },
   album: {
     released: "发行于",
@@ -103,7 +100,7 @@ export default {
     songs: "首歌",
   },
   settings: {
-    settings: "选项",
+    settings: "设置",
     logout: "登出",
     language: "语言",
     musicQuality: {
@@ -113,5 +110,11 @@ export default {
       high: "极高",
       lossless: "无损",
     },
+    appearance: {
+      text: "外观",
+      auto: "自动",
+      light: "浅色",
+      dark: "深色",
+    },
   },
 };
diff --git a/src/main.js b/src/main.js
index e296e80..1974c5e 100644
--- a/src/main.js
+++ b/src/main.js
@@ -8,6 +8,7 @@ import "@/assets/icons";
 import "@/utils/filters";
 import { initMediaSession } from "@/utils/mediaSession";
 import "./registerServiceWorker";
+import { dailyTask } from "@/utils/common";
 
 import * as Sentry from "@sentry/browser";
 import { Vue as VueIntegration } from "@sentry/integrations";
@@ -23,7 +24,6 @@ Vue.config.productionTip = false;
 initMediaSession();
 
 if (process.env.VUE_APP_ENABLE_SENTRY === "true") {
-  console.log("VUE_APP_ENABLE_SENTRY");
   Sentry.init({
     dsn:
       "https://30aaa25152974f48971912a394ab6bc3@o436528.ingest.sentry.io/5477409",
@@ -41,6 +41,8 @@ if (process.env.VUE_APP_ENABLE_SENTRY === "true") {
   });
 }
 
+dailyTask();
+
 new Vue({
   i18n,
   store,
diff --git a/src/router/index.js b/src/router/index.js
index 3d228b2..3b0ff37 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -3,7 +3,7 @@ import VueRouter from "vue-router";
 import store from "@/store";
 import NProgress from "nprogress";
 import "@/assets/css/nprogress.css";
-import Cookies from "js-cookie";
+import { isLooseLoggedIn } from "@/utils/auth";
 
 NProgress.configure({ showSpinner: false, trickleSpeed: 100 });
 
@@ -12,7 +12,7 @@ const routes = [
   {
     path: "/",
     name: "home",
-    component: () => import("@/views/home"),
+    component: () => import("@/views/home.vue"),
     meta: {
       keepAlive: true,
     },
@@ -20,32 +20,32 @@ const routes = [
   {
     path: "/login",
     name: "login",
-    component: () => import("@/views/login"),
+    component: () => import("@/views/login.vue"),
   },
   {
     path: "/login/username",
     name: "loginUsername",
-    component: () => import("@/views/loginUsername"),
+    component: () => import("@/views/loginUsername.vue"),
   },
   {
     path: "/login/account",
     name: "loginAccount",
-    component: () => import("@/views/loginAccount"),
+    component: () => import("@/views/loginAccount.vue"),
   },
   {
     path: "/playlist/:id",
     name: "playlist",
-    component: () => import("@/views/playlist"),
+    component: () => import("@/views/playlist.vue"),
   },
   {
     path: "/album/:id",
     name: "album",
-    component: () => import("@/views/album"),
+    component: () => import("@/views/album.vue"),
   },
   {
     path: "/artist/:id",
     name: "artist",
-    component: () => import("@/views/artist"),
+    component: () => import("@/views/artist.vue"),
     meta: {
       keepAlive: true,
     },
@@ -53,12 +53,12 @@ const routes = [
   {
     path: "/mv/:id",
     name: "mv",
-    component: () => import("@/views/mv"),
+    component: () => import("@/views/mv.vue"),
   },
   {
     path: "/next",
     name: "next",
-    component: () => import("@/views/next"),
+    component: () => import("@/views/next.vue"),
     meta: {
       keepAlive: true,
     },
@@ -66,17 +66,17 @@ const routes = [
   {
     path: "/search",
     name: "search",
-    component: () => import("@/views/search"),
+    component: () => import("@/views/search.vue"),
   },
   {
     path: "/new-album",
     name: "newAlbum",
-    component: () => import("@/views/newAlbum"),
+    component: () => import("@/views/newAlbum.vue"),
   },
   {
     path: "/explore",
     name: "explore",
-    component: () => import("@/views/explore"),
+    component: () => import("@/views/explore.vue"),
     meta: {
       keepAlive: true,
     },
@@ -84,7 +84,7 @@ const routes = [
   {
     path: "/library",
     name: "library",
-    component: () => import("@/views/library"),
+    component: () => import("@/views/library.vue"),
     meta: {
       requireLogin: true,
       keepAlive: true,
@@ -93,7 +93,7 @@ const routes = [
   {
     path: "/library/liked-songs",
     name: "likedSongs",
-    component: () => import("@/views/playlist"),
+    component: () => import("@/views/playlist.vue"),
     meta: {
       requireLogin: true,
     },
@@ -101,7 +101,7 @@ const routes = [
   {
     path: "/settings",
     name: "settings",
-    component: () => import("@/views/settings"),
+    component: () => import("@/views/settings.vue"),
   },
 ];
 const router = new VueRouter({
@@ -116,17 +116,15 @@ const router = new VueRouter({
 });
 
 router.beforeEach((to, from, next) => {
+  // 需要登录的逻辑
   if (to.meta.requireLogin) {
     if (store.state.settings.user.nickname === undefined) {
       next({ path: "/login" });
     }
-    if (
-      Cookies.get("MUSIC_U") === undefined &&
-      Cookies.get("loginMode") === "account"
-    ) {
-      next({ path: "/login" });
-    } else {
+    if (isLooseLoggedIn()) {
       next();
+    } else {
+      next({ path: "/login" });
     }
   } else {
     next();
diff --git a/src/store/actions.js b/src/store/actions.js
index 58d0b2b..6a28df9 100644
--- a/src/store/actions.js
+++ b/src/store/actions.js
@@ -1,6 +1,6 @@
 import { updateMediaSessionMetaData } from "@/utils/mediaSession";
 import { getTrackDetail, scrobble, getMP3 } from "@/api/track";
-import { isLoggedIn } from "@/utils/auth";
+import { isAccountLoggedIn } from "@/utils/auth";
 import { updateHttps } from "@/utils/common";
 
 export default {
@@ -41,8 +41,7 @@ export default {
           dispatch("nextTrack");
         });
       }
-
-      if (isLoggedIn) {
+      if (isAccountLoggedIn()) {
         getMP3(track.id).then((data) => {
           // 未知情况下会没有返回数据导致报错,增加防范逻辑
           if (data.data[0]) {
diff --git a/src/store/index.js b/src/store/index.js
index f69ef0a..5b10180 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -5,6 +5,7 @@ import mutations from "./mutations";
 import actions from "./actions";
 import initState from "./initState";
 import { Howl, Howler } from "howler";
+import { changeAppearance } from "@/utils/common";
 
 if (localStorage.getItem("appVersion") === null) {
   localStorage.setItem("player", JSON.stringify(initState.player));
@@ -45,4 +46,13 @@ if ([undefined, null].includes(store.state.settings.lang)) {
   localStorage.setItem("settings", JSON.stringify(store.state.settings));
 }
 
+changeAppearance(store.state.settings.appearance);
+window
+  .matchMedia("(prefers-color-scheme: dark)")
+  .addEventListener("change", () => {
+    if (store.state.settings.appearance === "auto") {
+      changeAppearance(store.state.settings.appearance);
+    }
+  });
+
 export default store;
diff --git a/src/store/initState.js b/src/store/initState.js
index 70c45a0..9f0c01f 100644
--- a/src/store/initState.js
+++ b/src/store/initState.js
@@ -1,5 +1,7 @@
 const initState = {
   howler: null,
+  accountLogin: false,
+  usernameLogin: false,
   liked: {
     songs: [],
   },
@@ -85,7 +87,11 @@ const initState = {
       id: 0,
     },
     lang: null,
+    appearance: "auto",
     musicQuality: 320000,
+    showGithubIcon: true,
+    showPlaylistsByAppleMusic: true,
+    lastRefreshCookieDate: 0,
   },
 };
 
diff --git a/src/store/mutations.js b/src/store/mutations.js
index 0dde032..2e4b811 100644
--- a/src/store/mutations.js
+++ b/src/store/mutations.js
@@ -81,6 +81,16 @@ export default {
       return track;
     });
   },
+  updateAccountLogin(state, status) {
+    state.accountLogin = status;
+  },
+  updateUsernameLogin(state, status) {
+    state.usernameLogin = status;
+  },
+  updateLogout() {
+    this.commit("updateAccountLogin", false);
+    this.commit("updateUsernameLogin", false);
+  },
   updateUser(state, user) {
     state.settings.user = user;
   },
@@ -106,4 +116,7 @@ export default {
   changeMusicQuality(state, value) {
     state.settings.musicQuality = value;
   },
+  updateSettings(state, { key, value }) {
+    state.settings[key] = value;
+  },
 };
diff --git a/src/store/state.js b/src/store/state.js
index d2d5dfd..8a5d99e 100644
--- a/src/store/state.js
+++ b/src/store/state.js
@@ -1,5 +1,7 @@
 export default {
   howler: null,
+  accountLogin: false,
+  usernameLogin: false,
   liked: {
     songs: [],
   },
diff --git a/src/utils/auth.js b/src/utils/auth.js
index 239ddcb..eb3c9bb 100644
--- a/src/utils/auth.js
+++ b/src/utils/auth.js
@@ -4,10 +4,38 @@ import store from "@/store";
 
 export function doLogout() {
   logout();
+  // 移除前端本地用来认证登录的字段
   Cookies.remove("loginMode");
+  // 网易云的接口会自动移除该 cookies
+  // Cookies.remove("MUSIC_U");
+  // 更新状态仓库中的用户信息
   store.commit("updateUser", { id: 0 });
+  // 更新状态仓库中的登录状态
+  store.commit("updateLogout");
 }
 
+// MUSIC_U 只有在账户登录的情况下才有
 export function isLoggedIn() {
   return Cookies.get("MUSIC_U") !== undefined ? true : false;
 }
+
+// 账号登录
+export function isAccountLoggedIn() {
+  return (
+    Cookies.get("MUSIC_U") !== undefined &&
+    Cookies.get("loginMode") === "account"
+  );
+}
+
+// 用户名搜索(用户数据为只读)
+export function isUsernameLoggedIn() {
+  return (
+    Cookies.get("MUSIC_U") === undefined &&
+    Cookies.get("loginMode") === "username"
+  );
+}
+
+// 账户登录或者用户名搜索都判断为登录,宽松检查
+export function isLooseLoggedIn() {
+  return isAccountLoggedIn() || isUsernameLoggedIn();
+}
diff --git a/src/utils/common.js b/src/utils/common.js
index 575e634..3663147 100644
--- a/src/utils/common.js
+++ b/src/utils/common.js
@@ -1,4 +1,7 @@
-import { isLoggedIn } from "./auth";
+import { isAccountLoggedIn } from "./auth";
+import { refreshCookie } from "@/api/auth";
+import { dailySignin } from "@/api/user";
+import dayjs from "dayjs";
 import store from "@/store";
 
 export function isTrackPlayable(track) {
@@ -7,7 +10,7 @@ export function isTrackPlayable(track) {
     reason: "",
   };
   if (track.fee === 1 || track.privilege?.fee === 1) {
-    if (isLoggedIn && store.state.settings.user.vipType === 11) {
+    if (isAccountLoggedIn() && store.state.settings.user.vipType === 11) {
       result.playable = true;
     } else {
       result.playable = false;
@@ -75,3 +78,32 @@ export function updateHttps(url) {
   if (!url) return "";
   return url.replace(/^http:/, "https:");
 }
+
+export function dailyTask() {
+  let lastDate = store.state.settings.lastRefreshCookieDate;
+  if (
+    isAccountLoggedIn() &&
+    (lastDate === undefined || lastDate !== dayjs().date())
+  ) {
+    console.log("execute dailyTask");
+    store.commit("updateSettings", {
+      key: "lastRefreshCookieDate",
+      value: dayjs().date(),
+    });
+    refreshCookie();
+    dailySignin(0);
+    dailySignin(1);
+  }
+}
+
+export function changeAppearance(appearance) {
+  if (appearance === "auto" || appearance === undefined) {
+    appearance = window.matchMedia("(prefers-color-scheme: dark)").matches
+      ? "dark"
+      : "light";
+  }
+  document.body.setAttribute("data-theme", appearance);
+  document
+    .querySelector('meta[name="theme-color"]')
+    .setAttribute("content", appearance === "dark" ? "#222" : "#fff");
+}
diff --git a/src/views/album.vue b/src/views/album.vue
index 4c9f472..5c3f409 100644
--- a/src/views/album.vue
+++ b/src/views/album.vue
@@ -60,8 +60,8 @@
         More by
         <router-link :to="`/artist/${album.artist.id}`"
           >{{ album.artist.name }}
-        </router-link></div
-      >
+        </router-link>
+      </div>
       <div>
         <CoverRow
           type="album"
@@ -200,6 +200,7 @@ export default {
     justify-content: center;
     flex: 1;
     margin-left: 56px;
+    color: var(--color-text);
     .title {
       font-size: 56px;
       font-weight: 700;
@@ -208,7 +209,7 @@ export default {
     }
     .artist {
       font-size: 18px;
-      color: rgba(0, 0, 0, 0.88);
+      opacity: 0.88;
       margin-top: 24px;
       a {
         font-weight: 600;
@@ -216,13 +217,13 @@ export default {
     }
     .date-and-count {
       font-size: 14px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
       margin-top: 2px;
     }
     .description {
       user-select: none;
       font-size: 14px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
       margin-top: 24px;
       display: -webkit-box;
       -webkit-box-orient: vertical;
@@ -230,8 +231,8 @@ export default {
       overflow: hidden;
       cursor: pointer;
       &:hover {
-        transition: color 0.3s;
-        color: rgba(0, 0, 0, 0.88);
+        transition: opacity 0.3s;
+        opacity: 0.88;
       }
     }
   }
@@ -281,7 +282,8 @@ export default {
 }
 
 .explicit-symbol {
-  color: rgba(0, 0, 0, 0.28);
+  opacity: 0.28;
+  color: var(--color-text);
   margin-right: 4px;
   .svg-icon {
     margin-bottom: -3px;
@@ -292,22 +294,25 @@ export default {
   margin-top: 36px;
   margin-bottom: 36px;
   font-size: 12px;
-  color: rgba(0, 0, 0, 0.48);
+  opacity: 0.48;
+  color: var(--color-text);
   div {
-    margin-bottom: 8px;
+    margin-bottom: 4px;
   }
   .album-time {
-    color: rgba(0, 0, 0, 0.68);
+    opacity: 0.68;
   }
 }
 
 .more-by {
-  border-top: 1px solid rgba(0, 0, 0, 0.08);
+  border-top: 1px solid rgba(128, 128, 128, 0.18);
+
   padding-top: 22px;
   .section-title {
     font-size: 22px;
     font-weight: 600;
-    color: rgba(0, 0, 0, 0.88);
+    opacity: 0.88;
+    color: var(--color-text);
     margin-bottom: 8px;
   }
 }
diff --git a/src/views/artist.vue b/src/views/artist.vue
index 71e0cdc..481799b 100644
--- a/src/views/artist.vue
+++ b/src/views/artist.vue
@@ -195,6 +195,7 @@ export default {
   display: flex;
   align-items: center;
   margin-bottom: 72px;
+  color: var(--color-text);
   img {
     height: 192px;
     width: 192px;
@@ -209,13 +210,13 @@ export default {
 
   .artist {
     font-size: 18px;
-    color: rgba(0, 0, 0, 0.88);
+    opacity: 0.88;
     margin-top: 24px;
   }
 
   .statistics {
     font-size: 14px;
-    color: rgba(0, 0, 0, 0.68);
+    opacity: 0.68;
     margin-top: 2px;
   }
 
@@ -234,12 +235,14 @@ export default {
 .section-title {
   font-weight: 600;
   font-size: 22px;
-  color: rgba(0, 0, 0, 0.88);
+  opacity: 0.88;
+  color: var(--color-text);
   margin-bottom: 16px;
   margin-top: 46px;
 }
 
 .latest-release {
+  color: var(--color-text);
   .release {
     display: flex;
   }
@@ -258,17 +261,16 @@ export default {
   .name {
     font-size: 18px;
     font-weight: 600;
-    color: rgba(0, 0, 0, 0.88);
     margin-bottom: 8px;
   }
   .date {
     font-size: 14px;
-    color: rgba(0, 0, 0, 0.78);
+    opacity: 0.78;
   }
   .type {
     margin-top: 2px;
     font-size: 12px;
-    color: rgba(0, 0, 0, 0.68);
+    opacity: 0.68;
   }
 }
 
@@ -281,11 +283,12 @@ export default {
       margin-top: 8px;
       border-radius: 6px;
       font-size: 12px;
-      color: rgba(0, 0, 0, 0.78);
+      opacity: 0.78;
+      color: var(--color-secondary);
       font-weight: 600;
       &:hover {
-        background: #f5f5f7;
-        color: rgba(0, 0, 0, 0.96);
+        opacity: 1;
+        // background: var(--color-primary-bg);
       }
     }
   }
diff --git a/src/views/explore.vue b/src/views/explore.vue
index b73b5e5..8ed611c 100644
--- a/src/views/explore.vue
+++ b/src/views/explore.vue
@@ -161,6 +161,7 @@ export default {
 
 <style lang="scss" scoped>
 h1 {
+  color: var(--color-text);
   font-size: 56px;
 }
 .buttons {
@@ -178,19 +179,18 @@ h1 {
   font-weight: 600;
   font-size: 18px;
   border-radius: 10px;
-  color: rgb(0, 0, 0);
-  background-color: #f5f5f7;
-  color: rgba(0, 0, 0, 0.68);
+  background-color: var(--color-secondary-bg);
+  color: var(--color-secondary);
   transition: 0.2s;
 
   &:hover {
-    background-color: rgba(51, 94, 234, 0.1);
-    color: #335eea;
+    background-color: var(--color-primary-bg);
+    color: var(--color-primary);
   }
 }
 .button.active {
-  background-color: rgba(51, 94, 234, 0.1);
-  color: #335eea;
+  background-color: var(--color-primary-bg);
+  color: var(--color-primary);
 }
 
 .playlists {
diff --git a/src/views/home.vue b/src/views/home.vue
index f38f3ab..d354d1e 100644
--- a/src/views/home.vue
+++ b/src/views/home.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="home" v-show="show">
-    <div class="index-row">
+    <div class="index-row" v-if="settings.showPlaylistsByAppleMusic !== false">
       <div class="title"> by Apple Music </div>
       <CoverRow
         :type="'playlist'"
@@ -54,7 +54,7 @@
         :color="'grey'"
         @click.native="goTo('/settings')"
       >
-        {{ $t("footer.settings") }}
+        {{ $t("settings.settings") }}
       </ButtonTwoTone>
     </footer>
   </div>
@@ -66,6 +66,8 @@ import { toplistOfArtists } from "@/api/artist";
 import { byAppleMusic } from "@/utils/staticPlaylist";
 import { newAlbums } from "@/api/album";
 import NProgress from "nprogress";
+import { mapState } from "vuex";
+
 import CoverRow from "@/components/CoverRow.vue";
 import ButtonTwoTone from "@/components/ButtonTwoTone.vue";
 
@@ -88,6 +90,7 @@ export default {
     };
   },
   computed: {
+    ...mapState(["settings"]),
     byAppleMusic() {
       return byAppleMusic;
     },
@@ -158,40 +161,11 @@ export default {
   margin-bottom: 20px;
   font-size: 28px;
   font-weight: 700;
-
+  color: var(--color-text);
   a {
     font-size: 13px;
     font-weight: 600;
-    color: rgba(0, 0, 0, 0.68);
-  }
-}
-
-.item {
-  margin: 12px 12px 24px 12px;
-  .text {
-    width: 208px;
-    margin-top: 8px;
-    .name {
-      font-size: 16px;
-      font-weight: 600;
-      color: rgba(0, 0, 0, 0.88);
-      line-height: 20px;
-
-      display: -webkit-box;
-      -webkit-box-orient: vertical;
-      -webkit-line-clamp: 2;
-      overflow: hidden;
-    }
-    .info {
-      font-size: 12px;
-      color: rgba(0, 0, 0, 0.68);
-      line-height: 18px;
-      display: -webkit-box;
-      -webkit-box-orient: vertical;
-      -webkit-line-clamp: 2;
-      overflow: hidden;
-      // margin-top: 4px;
-    }
+    opacity: 0.68;
   }
 }
 
diff --git a/src/views/library.vue b/src/views/library.vue
index efa986e..e961559 100644
--- a/src/views/library.vue
+++ b/src/views/library.vue
@@ -58,7 +58,7 @@
 import { mapState } from "vuex";
 import { getTrackDetail, getLyric } from "@/api/track";
 import { userDetail, userPlaylist } from "@/api/user";
-import { randomNum } from "@/utils/common";
+import { randomNum, dailyTask } from "@/utils/common";
 import { getPlaylistDetail } from "@/api/playlist";
 import { playPlaylistByID } from "@/utils/play";
 import NProgress from "nprogress";
@@ -98,6 +98,7 @@ export default {
   },
   activated() {
     this.loadData();
+    dailyTask();
   },
   computed: {
     ...mapState(["settings"]),
@@ -185,6 +186,7 @@ export default {
 <style lang="scss" scoped>
 h1 {
   font-size: 42px;
+  color: var(--color-text);
   .head {
     height: 44px;
     margin-right: 12px;
@@ -219,25 +221,21 @@ h1 {
   transition: all 0.4s;
   box-sizing: border-box;
 
-  background: #eaeffd;
-  // background: linear-gradient(-30deg, #60a6f7, #4364f7, #0052d4);
-  // color: white;
-  // background: linear-gradient(149.46deg, #450af5, #8e8ee5 99.16%);
+  background: var(--color-primary-bg);
 
   .bottom {
     display: flex;
     justify-content: space-between;
     align-items: center;
+    color: var(--color-primary);
 
     .title {
       font-size: 24px;
       font-weight: 700;
-      color: #335eea;
     }
     .sub-title {
       font-size: 15px;
       margin-top: 2px;
-      color: #335eea;
     }
 
     button {
@@ -247,16 +245,14 @@ h1 {
       align-items: center;
       height: 44px;
       width: 44px;
-      // background: rgba(255, 255, 255, 1);
-      background: #335eea;
+      background: var(--color-primary);
       border-radius: 50%;
       transition: 0.2s;
       box-shadow: 0 6px 12px -4px rgba(0, 0, 0, 0.2);
       cursor: default;
 
       .svg-icon {
-        // color: #3f63f5;
-        color: #eaeffd;
+        color: var(--color-primary-bg);
         margin-left: 4px;
         height: 16px;
         width: 16px;
@@ -276,7 +272,8 @@ h1 {
     display: flex;
     flex-wrap: wrap;
     font-size: 14px;
-    color: rgba(51, 94, 234, 0.88);
+    opacity: 0.88;
+    color: var(--color-primary);
     p {
       margin-top: 2px;
     }
@@ -286,7 +283,8 @@ h1 {
 .playlists {
   margin-top: 54px;
   .title {
-    color: rgba(0, 0, 0, 0.88);
+    color: var(--color-text);
+    opacity: 0.88;
     margin-bottom: 8px;
     font-size: 24px;
     font-weight: 600;
diff --git a/src/views/loginAccount.vue b/src/views/loginAccount.vue
index 2958bc0..c4ede97 100644
--- a/src/views/loginAccount.vue
+++ b/src/views/loginAccount.vue
@@ -112,9 +112,9 @@ export default {
     NProgress.done();
   },
   methods: {
-    ...mapMutations(["updateUser", "updateUserInfo"]),
+    ...mapMutations(["updateUser", "updateUserInfo", "updateAccountLogin"]),
     afterLogin() {
-      // Cookies.set("MUSIC_U", true, { expires: 3650 });
+      this.updateAccountLogin(true);
       Cookies.set("loginMode", "account", { expires: 3650 });
       userPlaylist({
         uid: this.$store.state.settings.user.userId,
@@ -127,18 +127,34 @@ export default {
         this.$router.push({ path: "/library" });
       });
     },
+    validatePhone() {
+      if (
+        this.countryCode === "" ||
+        this.phone === "" ||
+        this.password === ""
+      ) {
+        alert("国家区号、手机或密码不正确");
+        this.processing = false;
+        return false;
+      }
+      return true;
+    },
+    validateEmail() {
+      const emailReg = /^[A-Za-z0-9]+([_][A-Za-z0-9]+)*@([A-Za-z0-9]+\.)+[A-Za-z]{2,6}$/;
+      if (
+        this.email === "" ||
+        this.password === "" ||
+        !emailReg.test(this.email)
+      ) {
+        alert("邮箱或密码不正确");
+        return false;
+      }
+      return true;
+    },
     login() {
       this.processing = true;
       if (this.mode === "phone") {
-        if (
-          this.countryCode === "" ||
-          this.phone === "" ||
-          this.password === ""
-        ) {
-          alert("国家区号、手机或密码不正确");
-          this.processing = false;
-          return;
-        }
+        this.processing = this.validatePhone();
         loginWithPhone({
           countrycode: this.countryCode.replace("+", "").replace(/\s/g, ""),
           phone: this.phoneNumber.replace(/\s/g, ""),
@@ -156,16 +172,7 @@ export default {
             alert(error);
           });
       } else {
-        let emailReg = /^[A-Za-z0-9]+([_][A-Za-z0-9]+)*@([A-Za-z0-9]+\.)+[A-Za-z]{2,6}$/;
-        if (
-          this.email === "" ||
-          this.password === "" ||
-          !emailReg.test(this.email)
-        ) {
-          alert("邮箱或密码不正确");
-          this.processing = false;
-          return;
-        }
+        this.processing = this.validateEmail();
         loginWithEmail({
           email: this.email.replace(/\s/g, ""),
           password: "fakePassword",
@@ -199,6 +206,7 @@ export default {
   font-size: 24px;
   font-weight: 700;
   margin-bottom: 48px;
+  color: var(--color-text);
 }
 
 .section-1 {
@@ -209,11 +217,6 @@ export default {
     height: 64px;
     margin: 20px;
   }
-  .svg-icon {
-    height: 24px;
-    width: 24px;
-    color: rgba(82, 82, 82, 0.28);
-  }
 }
 
 .section-2 {
@@ -226,12 +229,13 @@ export default {
   display: flex;
   justify-content: flex-end;
   margin-bottom: 16px;
+  color: var(--color-text);
 
   .container {
     display: flex;
     align-items: center;
     height: 46px;
-    background: rgba(0, 0, 0, 0.06);
+    background: var(--color-secondary-bg);
     border-radius: 8px;
     width: 300px;
   }
@@ -258,7 +262,12 @@ export default {
     width: 100%;
     font-weight: 600;
     margin-top: -1px;
-    color: rgba(0, 0, 0, 0.88);
+    color: var(--color-text);
+  }
+
+  input::placeholder {
+    color: var(--color-text);
+    opacity: 0.38;
   }
 
   input#countryCode {
@@ -269,10 +278,10 @@ export default {
   }
 
   .active {
-    background: #eaeffd;
+    background: var(--color-primary-bg);
     input,
     .svg-icon {
-      color: #335eea;
+      color: var(--color-primary);
     }
   }
 }
@@ -283,8 +292,8 @@ export default {
   justify-content: center;
   font-size: 20px;
   font-weight: 600;
-  background-color: rgba(51, 94, 234, 0.1);
-  color: #335eea;
+  background-color: var(--color-primary-bg);
+  color: var(--color-primary);
   border-radius: 8px;
   margin-top: 24px;
   transition: 0.2s;
@@ -304,17 +313,19 @@ export default {
   a {
     cursor: pointer;
     font-size: 13px;
-    color: rgba(0, 0, 0, 0.68);
+    color: var(--color-text);
+    opacity: 0.68;
   }
 }
 
 .notice {
   width: 300px;
-  border-top: 1px solid rgba(0, 0, 0, 0.18);
+  border-top: 1px solid rgba(128, 128, 128);
   margin-top: 48px;
   padding-top: 12px;
   font-size: 12px;
-  color: rgba(0, 0, 0, 0.48);
+  color: var(--color-text);
+  opacity: 0.48;
 }
 
 @keyframes loading {
@@ -339,7 +350,7 @@ button.loading {
 .loading span {
   width: 6px;
   height: 6px;
-  background-color: #335eea;
+  background-color: var(--color-primary);
   border-radius: 50%;
   margin: 0 2px;
   animation: loading 1.4s infinite both;
diff --git a/src/views/loginUsername.vue b/src/views/loginUsername.vue
index 0ffd895..8a50950 100644
--- a/src/views/loginUsername.vue
+++ b/src/views/loginUsername.vue
@@ -41,8 +41,9 @@
       <ButtonTwoTone
         @click.native="confirm"
         v-show="activeUser.nickname !== undefined"
-        >{{ $t("login.confirm") }}</ButtonTwoTone
       >
+        {{ $t("login.confirm") }}
+      </ButtonTwoTone>
     </div>
   </div>
 </template>
@@ -73,7 +74,7 @@ export default {
     NProgress.done();
   },
   methods: {
-    ...mapMutations(["updateUser", "updateUserInfo"]),
+    ...mapMutations(["updateUser", "updateUserInfo", "updateUsernameLogin"]),
     search() {
       if (!this.keyword) return;
       search({ keywords: this.keyword, limit: 9, type: 1002 }).then((data) => {
@@ -83,6 +84,7 @@ export default {
     },
     confirm() {
       this.updateUser(this.activeUser);
+      this.updateUsernameLogin(true);
       Cookies.set("loginMode", "username", { expires: 3650 });
       userPlaylist({
         uid: this.activeUser.userId,
@@ -105,6 +107,7 @@ export default {
 <style lang="scss" scoped>
 .login {
   display: flex;
+  color: var(--color-text);
 }
 
 .title {
@@ -119,7 +122,7 @@ export default {
     font-size: 14px;
     font-weight: 500;
     margin-bottom: 8px;
-    color: rgba(0, 0, 0, 0.78);
+    opacity: 0.78;
   }
 }
 
@@ -130,13 +133,13 @@ export default {
     height: 48px;
     border-radius: 11px;
     width: 326px;
-    background: #eaeffd;
+    background: var(--color-primary-bg);
   }
 
   .svg-icon {
     height: 22px;
     width: 22px;
-    color: #335eea;
+    color: var(--color-primary);
     margin: {
       left: 12px;
       right: 8px;
@@ -151,9 +154,10 @@ export default {
     width: 115%;
     font-weight: 600;
     margin-top: -1px;
-    color: #335eea;
+    color: var(--color-primary);
     &::placeholder {
-      color: #335eeac4;
+      color: var(--color-primary);
+      opacity: 0.78;
     }
   }
 }
@@ -185,15 +189,15 @@ export default {
     margin-left: 12px;
   }
   &:hover {
-    background: #f5f5f7;
+    background: var(--color-secondary-bg);
   }
 }
 
 .user.active {
   transition: 0.2s;
-  background: #eaeffd;
-  .name {
-    color: #335eea;
+  background: var(--color-primary-bg);
+  .nickname {
+    color: var(--color-primary);
   }
 }
 </style>
diff --git a/src/views/mv.vue b/src/views/mv.vue
index 48bdd16..ffd3e55 100644
--- a/src/views/mv.vue
+++ b/src/views/mv.vue
@@ -127,19 +127,20 @@ export default {
 
 .video-info {
   margin-top: 12px;
+  color: var(--color-text);
   .title {
     font-size: 24px;
     font-weight: 600;
   }
   .artist {
     font-size: 14px;
-    color: rgba(0, 0, 0, 0.88);
+    opacity: 0.88;
     margin-top: 2px;
     font-weight: 600;
   }
   .info {
     font-size: 12px;
-    color: rgba(0, 0, 0, 0.68);
+    opacity: 0.68;
     margin-top: 12px;
   }
 }
@@ -149,7 +150,8 @@ export default {
   .section-title {
     font-size: 18px;
     font-weight: 600;
-    color: rgba(0, 0, 0, 0.88);
+    color: var(--color-text);
+    opacity: 0.88;
   }
 }
 </style>
diff --git a/src/views/next.vue b/src/views/next.vue
index df5cb2b..045c22d 100644
--- a/src/views/next.vue
+++ b/src/views/next.vue
@@ -106,5 +106,6 @@ h1 {
   margin-top: 36px;
   margin-bottom: 18px;
   cursor: default;
+  color: var(--color-text);
 }
 </style>
diff --git a/src/views/playlist.vue b/src/views/playlist.vue
index cf3d9c2..e58b685 100644
--- a/src/views/playlist.vue
+++ b/src/views/playlist.vue
@@ -46,7 +46,7 @@
           </ButtonTwoTone>
           <ButtonTwoTone
             v-if="
-              isLoggedIn && playlist.creator.userId !== settings.user.userId
+              accountLogin && playlist.creator.userId !== settings.user.userId
             "
             shape="round"
             :iconClass="playlist.subscribed ? 'heart-solid' : 'heart'"
@@ -92,7 +92,6 @@ import NProgress from "nprogress";
 import { getPlaylistDetail, subscribePlaylist } from "@/api/playlist";
 import { playAList } from "@/utils/play";
 import { getTrackDetail } from "@/api/track";
-import { isLoggedIn } from "@/utils/auth";
 
 import ButtonTwoTone from "@/components/ButtonTwoTone.vue";
 import TrackList from "@/components/TrackList.vue";
@@ -132,10 +131,7 @@ export default {
     window.removeEventListener("scroll", this.handleScroll, true);
   },
   computed: {
-    ...mapState(["player", "settings"]),
-    isLoggedIn() {
-      return isLoggedIn();
-    },
+    ...mapState(["player", "settings", "accountLogin"]),
     isLikeSongsPage() {
       return this.$route.name === "likedSongs";
     },
@@ -230,20 +226,24 @@ export default {
     .title {
       font-size: 36px;
       font-weight: 700;
+      color: var(--color-text);
     }
     .artist {
       font-size: 18px;
-      color: rgba(0, 0, 0, 0.88);
+      opacity: 0.88;
+      color: var(--color-text);
       margin-top: 24px;
     }
     .date-and-count {
       font-size: 14px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
+      color: var(--color-text);
       margin-top: 2px;
     }
     .description {
       font-size: 14px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
+      color: var(--color-text);
       margin-top: 24px;
       display: -webkit-box;
       -webkit-box-orient: vertical;
@@ -251,8 +251,8 @@ export default {
       overflow: hidden;
       cursor: pointer;
       &:hover {
-        transition: color 0.3s;
-        color: rgba(0, 0, 0, 0.88);
+        transition: opacity 0.3s;
+        opacity: 0.88;
       }
     }
     .buttons {
@@ -303,6 +303,7 @@ export default {
 .user-info {
   h1 {
     font-size: 42px;
+    color: var(--color-text);
     .avatar {
       height: 44px;
       margin-right: 12px;
diff --git a/src/views/search.vue b/src/views/search.vue
index b08f5cc..ece7683 100644
--- a/src/views/search.vue
+++ b/src/views/search.vue
@@ -192,15 +192,17 @@ export default {
 h1 {
   margin-top: -10px;
   margin-bottom: 0;
+  color: var(--color-text);
   span {
-    color: rgba(0, 0, 0, 0.58);
+    opacity: 0.58;
   }
 }
 
 .section-title {
   font-weight: 600;
   font-size: 22px;
-  color: rgba(0, 0, 0, 0.88);
+  opacity: 0.88;
+  color: var(--color-text);
   margin-bottom: 16px;
   margin-top: 46px;
 }
@@ -219,6 +221,7 @@ h1 {
   padding-right: 48px;
   font-size: 16px;
   font-weight: 600;
+  color: var(--color-text);
   .artist {
     display: flex;
     align-items: center;
@@ -236,6 +239,7 @@ h1 {
 
 .albums-list {
   display: flex;
+  color: var(--color-text);
   .album {
     img {
       height: 128px;
@@ -249,7 +253,6 @@ h1 {
     .name {
       margin-top: 6px;
       font-weight: 600;
-      color: rgba(0, 0, 0, 0.88);
       font-size: 14px;
       width: 128px;
       display: -webkit-box;
@@ -259,7 +262,7 @@ h1 {
     }
     .artist {
       font-size: 12px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
     }
   }
 }
diff --git a/src/views/settings.vue b/src/views/settings.vue
index 7277fd5..54517e8 100644
--- a/src/views/settings.vue
+++ b/src/views/settings.vue
@@ -11,9 +11,10 @@
                 ><img
                   class="cvip"
                   src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHIAAAA8CAYAAAC6j+5hAAAQK0lEQVR4AXzNh5WDMAwA0Dv3Su+wIfuxC3MwgCMUOz3xe1/N7e/X0lovhJCVUroR8r9DfVBKAuQAM8QYQ4815wlHQqQsIh6kFEA+USpRCP4H92yMfmCCtScL7rVzd967Fz5kmcf6zHmeJdDf66LIowJzWd5zUlUlqmsU6wo1TVI/adsmutZd1z7p+6Q7HePY7WCbpmGd53kBF87L4yiTMAaiM+u9N2NTIpB1CZEHuZAGHLFS8T9UXdJqzeHRw5VX3Z8YAIAPwf5Ii8k6Hsfx0nBxgEQwcWQIDKGPEZolAhIRGLg8hCaJUEuEVwhFIN8QMkOgfXsCApNESBLj+yNCEYjEg0iRicB7mdP05T7n+eulcbzv+2IMAHyAF/HI5J2pwBGBpIA4iCZqGwF5yKSJ4AJpIm1EoCfytJWAwKqN8MZRmYEIpI0IJCuJtUD/VoGIQ6aL01Yi8OuBu+95nlzo2bIsR8bggPxikn6ZwGuXiEhS2+iJQBKJEEJpIm1Epksr2ggiEanIRGDRRhCJuY1Znjaxm9R3CCRTIxHZtTHJI0MkbUQqMq+2bfllDMAHTbwax0HlZYGBymRWaaOIDIFQy/SkjaBtlFlFpgjs2whlE0nEQddGEonN24hAaWaSSQOjic5EwhXNpJH+JrrJw5yWbQQRiEQE0kJLREobEcmcIhGB8i7KpCIUkQhEome0MLJ5G7PAto2Q55TvaGHTxlqivItdG0PksszOGW/m4D/8sGFOQ55KzE0ko4UqE4nayHypIq6eVARGC5V+UmuBKjLkBe2kCv2kaiMRWM+qg0RQgZ7LMgm2pseHRR0247ITmY8cBPazqu+iytRGqlBE5neRpIX9rML/zCqJRJWZGwkqEJAY6QL7WSWRKDJppH9f+r8mLvJ7SASuVEQmiWRqIdBEMq7U30+qkie1eRdFHDKZVY6bflIVJEL9LqYWAgJJmthMqkITSZfnIpHoua53Mm1dv7vIk9RGoZeISEAc06qNdLSFJKhAeEGmS5VUoSGwnlZklm+jkJv4vrtUmVJ5H2li9zaCCtRGIhKZiNy2+WQweachEZDYzik0bcxXKvRtVImAxPrASXPqQvsDp34j2ybWIj8mEAdVG0kOHG0jTEATaSNprKcu8vxPVyoJWSIp72N55HCx1lcqqZNKBkh0uFJJlRm8kXntr9TyfYQkkfRG6vuYr1Tex6KJJDKrIwehNNJYPM+HelZDHO8jLSSdW1rOAci5bYnCeSprmLHtubbte8fXtm3btm3btm3bxq/9TqfeqtpZ0+fszrs5VbUqU+Pkq9W9GzsCjAUnAmJ1Nus2mZpwKy29FOfGHLhrzz7duU8+SNQN553NuREdHF++E0O/k0GGvp9zIz5v1q9vv+befewhd+9Vl7s9t9vaDfX3CjA+qSpOzMblRoEIkC7DAFmAyG7kniogwo1rrriCe+T6a9zsj9/PPZGvX3rO1VZX+zBF8jn5WvCF2GhyDDD1vEgK/D7qq4ZBUngNwwto1kfvuUtPOdEN9PVwucGhFW5kmJCUIADJYTW5gxNX/IuWX2Jx99wdt6r//LVnn6EW/2uvuUbwiX//6kuupamRa0bOkciLZpAIp4Hv51IjDMuoX956za0/PqrmRg6nDJBBAiLlREgrN/7DbszlsWP328fNSf7HI2ir84RDJJCDT/rOyy4OuhGh1Q7S5kguN+ywwpKotc8O29MJFQLE/NwIIbxmeMIh0ro3eOR2nLgxGyXwJ2+5MfgPI8TW1VTjgAPJ50whdusN1wNMbd5odiSfUI0gi+tIgrnBxCi14UheyQEnQhkPIh1wfKDxJ9Wy0lKEUrOuOycXYnlobAqxP73xiutqb6cuDp1SCwNpciSfVIsNEmF2aKBPYHITAADJkR5Ia2Oc2nAicYbZiax11lpDAHJP1RRiH7z2KgHHDQAopRwpANMDCV16yknkyGrfjb4TPZi1cCTgadP/eDcef8B+2j9jDrH1tbU8ppLPmULsLltuFjemsoJEWDWD9GGmARGn2bkGByi0JrmRQHLxDyeKGKBoyYUXQmkR1IwP3sk5bYPodNbf3eXK5UUpFZWoM0dxa+h3/vbOG26wr0eFmUKO9N1oduRnzz3ltlh/Hdff2xWpO/p4Xflc8Of22n4bv4vDAEV6jgTAUE/VB/rqfXeZnsyN553jujva1U4OQqrXS0Vz3BRin7j5BoADSCn0LSC5DWd1JDo4Jogd7S1S7Od1cro624Iw77v6coDk3KhCrK+PHOkfbPDoO1Fz5GrLLWs6he213dYo/rkVR06cDrOhzhZi991xe3VEZQeZjiPFiRhVcStuyw3WTfpZ6QAlFv8C04coUnOk1orzYErHJvhE9tx2a2W9EY88+dd3cdZZa83g3/nzvbfcvMODfk81FZCAaD3s9PV0+U7Ma44P9HUH2nmvx9SNeQccypGASNJqRlF9bY0hnJ4NgDzhiHMjT/5RK5pC7PN33hbBKMGIKo3QSpONIEjJizzhgKQtFyxDuGZEbqSQKhDhyPCoCk4UbTg+FjzYSE7k5jitccTuqQIgmuON9fWmEHvYnrv5k400cqQ33TCHVlHBofW9xx/i5jhcySA5R8aXGzxnvOTk4xP/CXEQb8RBbSWl7soFFnKfrriySD6Wz8W6EUX/uiNrmk7Giy4wnxlkaWlBIOFEE0gcdjo7WqdB7OpsNxx2rvDdGIIYqU5AMsT4/Ch66tbkBsAG4yPiRjqlCsQS983Kq7lZa4z4ks8BproBgML/+nPPCr54r91/j7zIZkdi6p9GaAVMcZ+UHpIX5WNL+bH3DtvEnlIRXhFSIYAUEcD8HIlB8fuPP5Kc5Lu6ABESmOI+hgjJ12K34qCmhgb3zcvPB1+E4w/cvwCQJWaQvBWXZkNg7qFBdcIB4aBDIP+plBsifdlYTlSJIaukhPOj5EUJpbEgP1tpZUAEUHUrbr3REdMLsfSiCxvni/bQynuqaYG87NSTqOSoCUJsaJDQ6hf/BJDyo0hOVMmHgtJSbQ8nAHKVWIAkU4h959EHzYNi68Sfd1TTaprPNdTvQ4T4pKqDFGlb4yK+FvfWw/cXFFrhyCsXWDAQWnnFUQVqDrEp5EiBia24VMZYG06O8SEHEBmmp7qcMur9Rs+FDFImD6HDjlcv4lEONLGHnfbSMnZjTgO93dqYyhRirY40zhd5M67YEKVDpdaMHFbhSDgRyuQ3xmn1X1lvlD0Tw6xRxOuNavnRXoryI38rTnT7JRcKNED0B8fBEGsHaXIkrzYWNZyKE7nUYKAAqIVVP0f6YoD+jSpTQ6Cns523xRPvNwo0rh2H+/vdzA/fjcLocxJOARBFv+zvBEJsUXMk398o0vLVSW54sE8g+opx5LRwio/hSMDzICq5EarKVsgLHJx4xF8Zt12Ju+eKS/H7xH0CkmHKWOxvgERYNYGkPdWwI2UH5+4rLnEfPvloNHJ7XU770gyXyYaMqaISY4CHxtxP5ZOqyIdJoZUmHH7JAfGi8QPXXBkuarffBj1VBaAOE2H1/OOPnvb71h8bQVM8D+YN56khttjrjbRoHAbJq43+1F/ZACCIITcqOZLcCKluBMixVVc2jrG2ITcq9xsppB6z397q75Mw2tzYQNvi5hAb2MGxO9IOEvcb4y7jVAMiL1R5j8iN+e04htjYWA+Q8SEVjuT7G4/fdL15sNzb1eE7Ug2r3R0dcriJ/T0Isdp1uA2Qt+3iG1UFOjKYIwFOcyQ7kdwYLP4prNYDJOVIAklhFTBl1cP0guEAdN05Z+a2xQd6elylHBrKyyLAndHnxuXaQCjv+iHWv0kFybWC/8eRVpCAiEcrSEA0bsWFW3GcH6FM+A5H/P3GUw49iJ5A+jrugH3V+42tzU3GEAuQhS0c+/cbs9kgSADE0jEgJk3+qTEuwIIhlUIrhVUA1K7G+beMJVTeeyVOl+nrxvPPATwGiRCbYo4UiObQGroax4NjiJ0IoBxa40H6SoLIN6qy0ZN648H7UgWIRSt5IQNv4IAQW+yFY1yHM4NkiOEDTtz0H1Lc6OfIuHfh8E+peIT4fqPAvPfKy1KDKDtCjQ31gJh4v7GtpRkhtphbcXRJ1Q5SoOExfr1Rg8nlRh2UBxPKBJzofUxupOm/nECLnTP/ev9tt99O26sA8cgwRRtOjJmXqSALSH8rLgzSfr8RUpxIboTqFZBKbiSgAAiY6h4OCn85zUoYDIMKT/sXnm8e1IxBN7ICIVbgFRhaAbEgR1JeFMXu4XAHh5vjihuhBpcBRN2RajhVt+L47v/E6qvKpMRUVkBzIj15yw1Ro3yt8Ds3Bt7cqL21JSnEem4sNQ2KPTdaHQl4BDAUVuHC+HCKvAg1Nf0PpPYOHPwuVfFujN8aF9VGT2KTqUl3+WknuYevv9q9/cgDuY6/7KN+9eIz7qV77owWuk50O22+qetsa7W+Jw5JvfMvNaoxR9pDK94Lxx5aATHgxsAhh2GyMv9t7WxS2wiiINw7ZxOyT/w3YPBtdBGDL+R76C66hrWVIO8FCgq+rtYgsvimtS/qve7XM6US7MwBgDkSvbF5gAPtN6Y39wYbsaT+WubFuZiMUkFeHN6Ms2sqpFOlYKMC47j1FEdizu8bGwrI3apKartx257PowQ7lYjY4HAI8MMdaXAwznEcRWQU59SJWnF+FBQQUWOzdCrgAjJuTALKkauYsQZDAIgouEvFgNwEpCNLyOLl1I48tmgG+uL8+43GX/8PjuTtRkioEkipWuWo7gz+25/eSAGXOaruROFOhBvDq422copDAZ8bE/PlOEqkjxDDieNG4WKG0L+b943ThCriQu51Y44aW6cau4hCgsKNtSY3akWAA/qjCQg3srQmN6q0vnz8yy2vHnmZhf7xRePbeXFaeU1FN2YxZ72RrKPG5EQK6Hh/VFmVA11IwbJKMfmlMXeo7JGroTg2N94jL29vb4+jHqPE+rIjR3Rj/UY55feNdKNhIv5s1jGc+3vjMvjPmAXFe2qjpVPBjchTDVlxcGMex/2ZnRlttd6Y3fhVjNGPDt4t8b6xW4WAKYIzlVQ48u4IznBmDBmqaYOTs1QpplwJAdMmR3he3NSRTiqpBgSsVSJ+v7+//y7G6EdRYj4cypVXllXvi3Ckwe8b2Rc99T86EvyHshr6I3qkC5i+b8TNfyir6Ita3YUU83ElpAt63bbtUIymH6L75WcJeGUMpztxUVJ53AiZOLsF1JpSjbWClGrMGE4mNzIwPvRFzFKdUFLhRo7hcE3FvngtPosh+uF0mT2UcN/p0zjsVPlmnASEI3luBBDwnt7o0Ik5ML6RcN4fPfxvvVOlgKvXOPJV1VMcjnc53banQzGcfoDumSXiV3FBOb3tRogoPA+HAQ47yylEnE9yBFONU7qxYDkNbpSgdCNEnLpRKyY4YaZ66Y2NeqKjHhnpo0kJ9VGCHuv3qTi7oL7Jsb6oFX0x/5kKd6vBifYbTrzVHwV6Yq3crXKXylEcd6la8VYcR3GY3mgV59fXp1Nx7HNiHzGKkfgLQfHe2MpsYnIAAAAASUVORK5CYII="
-                />黑胶VIP
+                />
+                <span class="text">黑胶VIP</span>
               </span>
-              <span v-else>{{ settings.user.signature }}</span>
+              <span class="text" v-else>{{ settings.user.signature }}</span>
             </div>
           </div>
         </div>
@@ -36,6 +37,22 @@
           </select>
         </div>
       </div>
+      <div class="item">
+        <div class="left">
+          <div class="title"> {{ $t("settings.appearance.text") }} </div>
+        </div>
+        <div class="right">
+          <select v-model="appearance">
+            <option value="auto">{{ $t("settings.appearance.auto") }}</option>
+            <option value="light"
+              >🌞 {{ $t("settings.appearance.light") }}</option
+            >
+            <option value="dark"
+              >🌚 {{ $t("settings.appearance.dark") }}</option
+            >
+          </select>
+        </div>
+      </div>
       <div class="item">
         <div class="left">
           <div class="title"> {{ $t("settings.musicQuality.text") }} </div>
@@ -57,6 +74,38 @@
           </select>
         </div>
       </div>
+      <div class="item">
+        <div class="left">
+          <div class="title"> Show Github Icon </div>
+        </div>
+        <div class="right">
+          <div class="toggle">
+            <input
+              type="checkbox"
+              name="show-github-icon"
+              id="show-github-icon"
+              v-model="showGithubIcon"
+            />
+            <label for="show-github-icon"></label>
+          </div>
+        </div>
+      </div>
+      <div class="item">
+        <div class="left">
+          <div class="title"> Show Playlists by Apple Music</div>
+        </div>
+        <div class="right">
+          <div class="toggle">
+            <input
+              type="checkbox"
+              name="show-playlists-by-apple-music"
+              id="show-playlists-by-apple-music"
+              v-model="showPlaylistsByAppleMusic"
+            />
+            <label for="show-playlists-by-apple-music"></label>
+          </div>
+        </div>
+      </div>
     </div>
   </div>
 </template>
@@ -64,6 +113,8 @@
 <script>
 import { mapState } from "vuex";
 import { doLogout } from "@/utils/auth";
+import { changeAppearance } from "@/utils/common";
+
 export default {
   name: "settings",
   computed: {
@@ -77,14 +128,52 @@ export default {
         this.$store.commit("changeLang", lang);
       },
     },
+    appearance: {
+      get() {
+        if (this.settings.appearance === undefined) return "auto";
+        return this.settings.appearance;
+      },
+      set(value) {
+        this.$store.commit("updateSettings", {
+          key: "appearance",
+          value,
+        });
+        changeAppearance(value);
+      },
+    },
     musicQuality: {
       get() {
+        if (this.settings.appearance === undefined) return 320000;
         return this.settings.musicQuality;
       },
       set(value) {
         this.$store.commit("changeMusicQuality", value);
       },
     },
+    showGithubIcon: {
+      get() {
+        if (this.settings.showGithubIcon === undefined) return true;
+        return this.settings.showGithubIcon;
+      },
+      set(value) {
+        this.$store.commit("updateSettings", {
+          key: "showGithubIcon",
+          value,
+        });
+      },
+    },
+    showPlaylistsByAppleMusic: {
+      get() {
+        if (this.settings.showPlaylistsByAppleMusic === undefined) return true;
+        return this.settings.showPlaylistsByAppleMusic;
+      },
+      set(value) {
+        this.$store.commit("updateSettings", {
+          key: "showPlaylistsByAppleMusic",
+          value,
+        });
+      },
+    },
   },
   methods: {
     logout() {
@@ -107,13 +196,15 @@ export default {
 h2 {
   margin-top: 48px;
   font-size: 36px;
+  color: var(--color-text);
 }
 
 .user {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  background: #f5f5f7;
+  background: var(--color-secondary-bg);
+  color: var(--color-text);
   padding: 16px 20px;
   border-radius: 16px;
   img.avatar {
@@ -134,12 +225,13 @@ h2 {
     .nickname {
       font-size: 20px;
       font-weight: 600;
-      color: rgba(0, 0, 0, 0.88);
       margin-bottom: 2px;
     }
     .extra-info {
       font-size: 13px;
-      color: rgba(0, 0, 0, 0.68);
+      .text {
+        opacity: 0.68;
+      }
       .vip {
         display: flex;
         align-items: center;
@@ -160,17 +252,20 @@ h2 {
       text-decoration: none;
       border-radius: 10px;
       padding: 8px 12px;
-      color: rgba(0, 0, 0, 0.68);
+      opacity: 0.68;
+      color: var(--color-text);
       transition: 0.2s;
       margin: {
         right: 12px;
         left: 12px;
       }
       &:hover {
+        opacity: 1;
         background: #eaeffd;
         color: #335eea;
       }
       &:active {
+        opacity: 1;
         transform: scale(0.92);
         transition: 0.2s;
       }
@@ -181,12 +276,14 @@ h2 {
 .item {
   margin: 24px 0;
   display: flex;
+  align-items: center;
   justify-content: space-between;
+  color: var(--color-text);
 
   .title {
     font-size: 18px;
     font-weight: 600;
-    color: rgba(0, 0, 0, 0.88);
+    opacity: 0.88;
   }
 
   select {
@@ -195,14 +292,81 @@ h2 {
     border: none;
     padding: 8px 12px 8px 12px;
     border-radius: 8px;
-    color: rgba(0, 0, 0, 0.88);
-    background: #f5f5f7;
+    color: var(--color-text);
+    background: var(--color-secondary-bg);
     appearance: none;
     &:focus {
       outline: none;
-      color: #335eea;
-      background: #eaeffd;
+      color: var(--color-primary);
+      background: var(--color-primary-bg);
     }
   }
 }
+
+.beforeAnimation {
+  -webkit-transition: 0.2s cubic-bezier(0.24, 0, 0.5, 1);
+  transition: 0.2s cubic-bezier(0.24, 0, 0.5, 1);
+}
+.afterAnimation {
+  box-shadow: 0 0 0 1px hsla(0, 0%, 0%, 0.1), 0 4px 0px 0 hsla(0, 0%, 0%, 0.04),
+    0 4px 9px hsla(0, 0%, 0%, 0.13), 0 3px 3px hsla(0, 0%, 0%, 0.05);
+  -webkit-transition: 0.35s cubic-bezier(0.54, 1.6, 0.5, 1);
+  transition: 0.35s cubic-bezier(0.54, 1.6, 0.5, 1);
+}
+.toggle {
+  margin: auto;
+}
+.toggle input {
+  opacity: 0;
+  position: absolute;
+}
+.toggle input + label {
+  position: relative;
+  display: inline-block;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  -webkit-transition: 0.4s ease;
+  transition: 0.4s ease;
+  height: 32px;
+  width: 68px;
+  background: var(--color-secondary-bg);
+  border-radius: 8px;
+}
+.toggle input + label:before {
+  content: "";
+  position: absolute;
+  display: block;
+  -webkit-transition: 0.2s cubic-bezier(0.24, 0, 0.5, 1);
+  transition: 0.2s cubic-bezier(0.24, 0, 0.5, 1);
+  height: 32px;
+  width: 68px;
+  top: 0;
+  left: 0;
+  border-radius: 8px;
+}
+.toggle input + label:after {
+  content: "";
+  position: absolute;
+  display: block;
+  box-shadow: 0 0 0 1px hsla(0, 0%, 0%, 0.02), 0 4px 0px 0 hsla(0, 0%, 0%, 0.01),
+    0 4px 9px hsla(0, 0%, 0%, 0.08), 0 3px 3px hsla(0, 0%, 0%, 0.03);
+  -webkit-transition: 0.35s cubic-bezier(0.54, 1.6, 0.5, 1);
+  transition: 0.35s cubic-bezier(0.54, 1.6, 0.5, 1);
+  background: #fff;
+  height: 20px;
+  width: 28px;
+  top: 6px;
+  left: 6px;
+  border-radius: 6px;
+}
+.toggle input:checked + label:before {
+  background: var(--color-primary);
+  -webkit-transition: width 0.2s cubic-bezier(0, 0, 0, 0.1);
+  transition: width 0.2s cubic-bezier(0, 0, 0, 0.1);
+}
+.toggle input:checked + label:after {
+  left: 34px;
+}
 </style>